Mercurial > public > mercurial-scm > hg
comparison mercurial/changegroup.py @ 27433:12f727a5b434
changegroup: add flags field to cg3 delta header
This lets revlog flags be transmitted over the wire. Right now this is
useful for censored nodes and for narrowhg's ellipsis nodes.
author | Mike Edgar <adgar@google.com> |
---|---|
date | Mon, 14 Dec 2015 15:55:12 -0500 |
parents | 77d25b913f80 |
children | 8b01558d5d23 |
comparison
equal
deleted
inserted
replaced
27432:77d25b913f80 | 27433:12f727a5b434 |
---|---|
30 util, | 30 util, |
31 ) | 31 ) |
32 | 32 |
33 _CHANGEGROUPV1_DELTA_HEADER = "20s20s20s20s" | 33 _CHANGEGROUPV1_DELTA_HEADER = "20s20s20s20s" |
34 _CHANGEGROUPV2_DELTA_HEADER = "20s20s20s20s20s" | 34 _CHANGEGROUPV2_DELTA_HEADER = "20s20s20s20s20s" |
35 _CHANGEGROUPV3_DELTA_HEADER = ">20s20s20s20s20sH" | |
35 | 36 |
36 def readexactly(stream, n): | 37 def readexactly(stream, n): |
37 '''read n bytes from stream.read and abort if less was available''' | 38 '''read n bytes from stream.read and abort if less was available''' |
38 s = stream.read(n) | 39 s = stream.read(n) |
39 if len(s) < n: | 40 if len(s) < n: |
244 node, p1, p2, cs = headertuple | 245 node, p1, p2, cs = headertuple |
245 if prevnode is None: | 246 if prevnode is None: |
246 deltabase = p1 | 247 deltabase = p1 |
247 else: | 248 else: |
248 deltabase = prevnode | 249 deltabase = prevnode |
249 return node, p1, p2, deltabase, cs | 250 flags = 0 |
251 return node, p1, p2, deltabase, cs, flags | |
250 | 252 |
251 def deltachunk(self, prevnode): | 253 def deltachunk(self, prevnode): |
252 l = self._chunklength() | 254 l = self._chunklength() |
253 if not l: | 255 if not l: |
254 return {} | 256 return {} |
255 headerdata = readexactly(self._stream, self.deltaheadersize) | 257 headerdata = readexactly(self._stream, self.deltaheadersize) |
256 header = struct.unpack(self.deltaheader, headerdata) | 258 header = struct.unpack(self.deltaheader, headerdata) |
257 delta = readexactly(self._stream, l - self.deltaheadersize) | 259 delta = readexactly(self._stream, l - self.deltaheadersize) |
258 node, p1, p2, deltabase, cs = self._deltaheader(header, prevnode) | 260 node, p1, p2, deltabase, cs, flags = self._deltaheader(header, prevnode) |
259 return {'node': node, 'p1': p1, 'p2': p2, 'cs': cs, | 261 return {'node': node, 'p1': p1, 'p2': p2, 'cs': cs, |
260 'deltabase': deltabase, 'delta': delta} | 262 'deltabase': deltabase, 'delta': delta, 'flags': flags} |
261 | 263 |
262 def getchunks(self): | 264 def getchunks(self): |
263 """returns all the chunks contains in the bundle | 265 """returns all the chunks contains in the bundle |
264 | 266 |
265 Used when you need to forward the binary stream to a file or another | 267 Used when you need to forward the binary stream to a file or another |
294 # no need to check for empty manifest group here: | 296 # no need to check for empty manifest group here: |
295 # if the result of the merge of 1 and 2 is the same in 3 and 4, | 297 # if the result of the merge of 1 and 2 is the same in 3 and 4, |
296 # no new manifest will be created and the manifest group will | 298 # no new manifest will be created and the manifest group will |
297 # be empty during the pull | 299 # be empty during the pull |
298 self.manifestheader() | 300 self.manifestheader() |
301 repo.manifest.narrowdebug = repo.ui.warn | |
299 repo.manifest.addgroup(self, revmap, trp) | 302 repo.manifest.addgroup(self, revmap, trp) |
300 repo.ui.progress(_('manifests'), None) | 303 repo.ui.progress(_('manifests'), None) |
301 | 304 |
302 def apply(self, repo, srctype, url, emptyok=False, | 305 def apply(self, repo, srctype, url, emptyok=False, |
303 targetphase=phases.draft, expectedtotal=None): | 306 targetphase=phases.draft, expectedtotal=None): |
493 deltaheadersize = struct.calcsize(deltaheader) | 496 deltaheadersize = struct.calcsize(deltaheader) |
494 version = '02' | 497 version = '02' |
495 | 498 |
496 def _deltaheader(self, headertuple, prevnode): | 499 def _deltaheader(self, headertuple, prevnode): |
497 node, p1, p2, deltabase, cs = headertuple | 500 node, p1, p2, deltabase, cs = headertuple |
498 return node, p1, p2, deltabase, cs | 501 flags = 0 |
502 return node, p1, p2, deltabase, cs, flags | |
499 | 503 |
500 class cg3unpacker(cg2unpacker): | 504 class cg3unpacker(cg2unpacker): |
501 """Unpacker for cg3 streams. | 505 """Unpacker for cg3 streams. |
502 | 506 |
503 cg3 streams add support for exchanging treemanifests, so the only | 507 cg3 streams add support for exchanging treemanifests and revlog |
504 thing that changes is the version number. | 508 flags, so the only changes from cg2 are the delta header and |
509 version number. | |
505 """ | 510 """ |
511 deltaheader = _CHANGEGROUPV3_DELTA_HEADER | |
512 deltaheadersize = struct.calcsize(deltaheader) | |
506 version = '03' | 513 version = '03' |
514 | |
515 def _deltaheader(self, headertuple, prevnode): | |
516 node, p1, p2, deltabase, cs, flags = headertuple | |
517 return node, p1, p2, deltabase, cs, flags | |
507 | 518 |
508 class headerlessfixup(object): | 519 class headerlessfixup(object): |
509 def __init__(self, fh, h): | 520 def __init__(self, fh, h): |
510 self._h = h | 521 self._h = h |
511 self._fh = fh | 522 self._fh = fh |
839 prefix = mdiff.trivialdiffheader(len(delta)) | 850 prefix = mdiff.trivialdiffheader(len(delta)) |
840 else: | 851 else: |
841 delta = revlog.revdiff(base, rev) | 852 delta = revlog.revdiff(base, rev) |
842 p1n, p2n = revlog.parents(node) | 853 p1n, p2n = revlog.parents(node) |
843 basenode = revlog.node(base) | 854 basenode = revlog.node(base) |
844 meta = self.builddeltaheader(node, p1n, p2n, basenode, linknode) | 855 flags = revlog.flags(rev) |
856 meta = self.builddeltaheader(node, p1n, p2n, basenode, linknode, flags) | |
845 meta += prefix | 857 meta += prefix |
846 l = len(meta) + len(delta) | 858 l = len(meta) + len(delta) |
847 yield chunkheader(l) | 859 yield chunkheader(l) |
848 yield meta | 860 yield meta |
849 yield delta | 861 yield delta |
850 def builddeltaheader(self, node, p1n, p2n, basenode, linknode): | 862 def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags): |
851 # do nothing with basenode, it is implicitly the previous one in HG10 | 863 # do nothing with basenode, it is implicitly the previous one in HG10 |
864 # do nothing with flags, it is implicitly 0 for cg1 and cg2 | |
852 return struct.pack(self.deltaheader, node, p1n, p2n, linknode) | 865 return struct.pack(self.deltaheader, node, p1n, p2n, linknode) |
853 | 866 |
854 class cg2packer(cg1packer): | 867 class cg2packer(cg1packer): |
855 version = '02' | 868 version = '02' |
856 deltaheader = _CHANGEGROUPV2_DELTA_HEADER | 869 deltaheader = _CHANGEGROUPV2_DELTA_HEADER |
869 # also pick prev when we can't be sure remote has dp | 882 # also pick prev when we can't be sure remote has dp |
870 if dp == nullrev or (dp != p1 and dp != p2 and dp != prev): | 883 if dp == nullrev or (dp != p1 and dp != p2 and dp != prev): |
871 return prev | 884 return prev |
872 return dp | 885 return dp |
873 | 886 |
874 def builddeltaheader(self, node, p1n, p2n, basenode, linknode): | 887 def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags): |
888 # Do nothing with flags, it is implicitly 0 in cg1 and cg2 | |
875 return struct.pack(self.deltaheader, node, p1n, p2n, basenode, linknode) | 889 return struct.pack(self.deltaheader, node, p1n, p2n, basenode, linknode) |
876 | 890 |
877 class cg3packer(cg2packer): | 891 class cg3packer(cg2packer): |
878 version = '03' | 892 version = '03' |
893 deltaheader = _CHANGEGROUPV3_DELTA_HEADER | |
879 | 894 |
880 def _packmanifests(self, mfnodes, tmfnodes, lookuplinknode): | 895 def _packmanifests(self, mfnodes, tmfnodes, lookuplinknode): |
881 # Note that debug prints are super confusing in this code, as | 896 # Note that debug prints are super confusing in this code, as |
882 # tmfnodes gets populated by the calls to lookuplinknode in | 897 # tmfnodes gets populated by the calls to lookuplinknode in |
883 # the superclass's manifest packer. In the future we should | 898 # the superclass's manifest packer. In the future we should |
892 # a trailing '/' on the path. | 907 # a trailing '/' on the path. |
893 yield self.fileheader(name + '/') | 908 yield self.fileheader(name + '/') |
894 for chunk in self.group(nodes, dirlog(name), nodes.get): | 909 for chunk in self.group(nodes, dirlog(name), nodes.get): |
895 yield chunk | 910 yield chunk |
896 | 911 |
912 def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags): | |
913 return struct.pack( | |
914 self.deltaheader, node, p1n, p2n, basenode, linknode, flags) | |
897 | 915 |
898 packermap = {'01': (cg1packer, cg1unpacker), | 916 packermap = {'01': (cg1packer, cg1unpacker), |
899 # cg2 adds support for exchanging generaldelta | 917 # cg2 adds support for exchanging generaldelta |
900 '02': (cg2packer, cg2unpacker), | 918 '02': (cg2packer, cg2unpacker), |
901 # cg3 adds support for exchanging treemanifests | 919 # cg3 adds support for exchanging treemanifests |