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