mercurial/localrepo.py
changeset 1469 0847c45ffee6
parent 1448 182879d71922
parent 1466 b6d9ea0bc107
child 1473 7d66ce9895fa
equal deleted inserted replaced
1456:214f42f23a3b 1469:0847c45ffee6
   725 
   725 
   726             r.append(l)
   726             r.append(l)
   727 
   727 
   728         return r
   728         return r
   729 
   729 
   730     def newer(self, nodes):
       
   731         m = {}
       
   732         nl = []
       
   733         pm = {}
       
   734         cl = self.changelog
       
   735         t = l = cl.count()
       
   736 
       
   737         # find the lowest numbered node
       
   738         for n in nodes:
       
   739             l = min(l, cl.rev(n))
       
   740             m[n] = 1
       
   741 
       
   742         for i in xrange(l, t):
       
   743             n = cl.node(i)
       
   744             if n in m: # explicitly listed
       
   745                 pm[n] = 1
       
   746                 nl.append(n)
       
   747                 continue
       
   748             for p in cl.parents(n):
       
   749                 if p in pm: # parent listed
       
   750                     pm[n] = 1
       
   751                     nl.append(n)
       
   752                     break
       
   753 
       
   754         return nl
       
   755 
       
   756     def findincoming(self, remote, base=None, heads=None):
   730     def findincoming(self, remote, base=None, heads=None):
   757         m = self.changelog.nodemap
   731         m = self.changelog.nodemap
   758         search = []
   732         search = []
   759         fetch = {}
   733         fetch = {}
   760         seen = {}
   734         seen = {}
   901                 subset.append(n)
   875                 subset.append(n)
   902 
   876 
   903         # this is the set of all roots we have to push
   877         # this is the set of all roots we have to push
   904         return subset
   878         return subset
   905 
   879 
   906     def pull(self, remote):
   880     def pull(self, remote, heads = None):
   907         lock = self.lock()
   881         lock = self.lock()
   908 
   882 
   909         # if we have an empty repo, fetch everything
   883         # if we have an empty repo, fetch everything
   910         if self.changelog.tip() == nullid:
   884         if self.changelog.tip() == nullid:
   911             self.ui.status(_("requesting all changes\n"))
   885             self.ui.status(_("requesting all changes\n"))
   915 
   889 
   916         if not fetch:
   890         if not fetch:
   917             self.ui.status(_("no changes found\n"))
   891             self.ui.status(_("no changes found\n"))
   918             return 1
   892             return 1
   919 
   893 
   920         cg = remote.changegroup(fetch)
   894         if heads is None:
       
   895             cg = remote.changegroup(fetch)
       
   896         else:
       
   897             cg = remote.changegroupsubset(fetch, heads)
   921         return self.addchangegroup(cg)
   898         return self.addchangegroup(cg)
   922 
   899 
   923     def push(self, remote, force=False):
   900     def push(self, remote, force=False):
   924         lock = remote.lock()
   901         lock = remote.lock()
   925 
   902 
   943                 return 1
   920                 return 1
   944 
   921 
   945         cg = self.changegroup(update)
   922         cg = self.changegroup(update)
   946         return remote.addchangegroup(cg)
   923         return remote.addchangegroup(cg)
   947 
   924 
       
   925     def changegroupsubset(self, bases, heads):
       
   926         """This function generates a changegroup consisting of all the nodes
       
   927         that are descendents of any of the bases, and ancestors of any of
       
   928         the heads.
       
   929 
       
   930         It is fairly complex as determining which filenodes and which
       
   931         manifest nodes need to be included for the changeset to be complete
       
   932         is non-trivial.
       
   933 
       
   934         Another wrinkle is doing the reverse, figuring out which changeset in
       
   935         the changegroup a particular filenode or manifestnode belongs to."""
       
   936 
       
   937         # Set up some initial variables
       
   938         # Make it easy to refer to self.changelog
       
   939         cl = self.changelog
       
   940         # msng is short for missing - compute the list of changesets in this
       
   941         # changegroup.
       
   942         msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
       
   943         # Some bases may turn out to be superfluous, and some heads may be
       
   944         # too.  nodesbetween will return the minimal set of bases and heads
       
   945         # necessary to re-create the changegroup.
       
   946 
       
   947         # Known heads are the list of heads that it is assumed the recipient
       
   948         # of this changegroup will know about.
       
   949         knownheads = {}
       
   950         # We assume that all parents of bases are known heads.
       
   951         for n in bases:
       
   952             for p in cl.parents(n):
       
   953                 if p != nullid:
       
   954                     knownheads[p] = 1
       
   955         knownheads = knownheads.keys()
       
   956         if knownheads:
       
   957             # Now that we know what heads are known, we can compute which
       
   958             # changesets are known.  The recipient must know about all
       
   959             # changesets required to reach the known heads from the null
       
   960             # changeset.
       
   961             has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
       
   962             junk = None
       
   963             # Transform the list into an ersatz set.
       
   964             has_cl_set = dict.fromkeys(has_cl_set)
       
   965         else:
       
   966             # If there were no known heads, the recipient cannot be assumed to
       
   967             # know about any changesets.
       
   968             has_cl_set = {}
       
   969 
       
   970         # Make it easy to refer to self.manifest
       
   971         mnfst = self.manifest
       
   972         # We don't know which manifests are missing yet
       
   973         msng_mnfst_set = {}
       
   974         # Nor do we know which filenodes are missing.
       
   975         msng_filenode_set = {}
       
   976 
       
   977         junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
       
   978         junk = None
       
   979 
       
   980         # A changeset always belongs to itself, so the changenode lookup
       
   981         # function for a changenode is identity.
       
   982         def identity(x):
       
   983             return x
       
   984 
       
   985         # A function generating function.  Sets up an environment for the
       
   986         # inner function.
       
   987         def cmp_by_rev_func(revlog):
       
   988             # Compare two nodes by their revision number in the environment's
       
   989             # revision history.  Since the revision number both represents the
       
   990             # most efficient order to read the nodes in, and represents a
       
   991             # topological sorting of the nodes, this function is often useful.
       
   992             def cmp_by_rev(a, b):
       
   993                 return cmp(revlog.rev(a), revlog.rev(b))
       
   994             return cmp_by_rev
       
   995 
       
   996         # If we determine that a particular file or manifest node must be a
       
   997         # node that the recipient of the changegroup will already have, we can
       
   998         # also assume the recipient will have all the parents.  This function
       
   999         # prunes them from the set of missing nodes.
       
  1000         def prune_parents(revlog, hasset, msngset):
       
  1001             haslst = hasset.keys()
       
  1002             haslst.sort(cmp_by_rev_func(revlog))
       
  1003             for node in haslst:
       
  1004                 parentlst = [p for p in revlog.parents(node) if p != nullid]
       
  1005                 while parentlst:
       
  1006                     n = parentlst.pop()
       
  1007                     if n not in hasset:
       
  1008                         hasset[n] = 1
       
  1009                         p = [p for p in revlog.parents(n) if p != nullid]
       
  1010                         parentlst.extend(p)
       
  1011             for n in hasset:
       
  1012                 msngset.pop(n, None)
       
  1013 
       
  1014         # This is a function generating function used to set up an environment
       
  1015         # for the inner function to execute in.
       
  1016         def manifest_and_file_collector(changedfileset):
       
  1017             # This is an information gathering function that gathers
       
  1018             # information from each changeset node that goes out as part of
       
  1019             # the changegroup.  The information gathered is a list of which
       
  1020             # manifest nodes are potentially required (the recipient may
       
  1021             # already have them) and total list of all files which were
       
  1022             # changed in any changeset in the changegroup.
       
  1023             #
       
  1024             # We also remember the first changenode we saw any manifest
       
  1025             # referenced by so we can later determine which changenode 'owns'
       
  1026             # the manifest.
       
  1027             def collect_manifests_and_files(clnode):
       
  1028                 c = cl.read(clnode)
       
  1029                 for f in c[3]:
       
  1030                     # This is to make sure we only have one instance of each
       
  1031                     # filename string for each filename.
       
  1032                     changedfileset.setdefault(f, f)
       
  1033                 msng_mnfst_set.setdefault(c[0], clnode)
       
  1034             return collect_manifests_and_files
       
  1035 
       
  1036         # Figure out which manifest nodes (of the ones we think might be part
       
  1037         # of the changegroup) the recipient must know about and remove them
       
  1038         # from the changegroup.
       
  1039         def prune_manifests():
       
  1040             has_mnfst_set = {}
       
  1041             for n in msng_mnfst_set:
       
  1042                 # If a 'missing' manifest thinks it belongs to a changenode
       
  1043                 # the recipient is assumed to have, obviously the recipient
       
  1044                 # must have that manifest.
       
  1045                 linknode = cl.node(mnfst.linkrev(n))
       
  1046                 if linknode in has_cl_set:
       
  1047                     has_mnfst_set[n] = 1
       
  1048             prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
       
  1049 
       
  1050         # Use the information collected in collect_manifests_and_files to say
       
  1051         # which changenode any manifestnode belongs to.
       
  1052         def lookup_manifest_link(mnfstnode):
       
  1053             return msng_mnfst_set[mnfstnode]
       
  1054 
       
  1055         # A function generating function that sets up the initial environment
       
  1056         # the inner function.
       
  1057         def filenode_collector(changedfiles):
       
  1058             next_rev = [0]
       
  1059             # This gathers information from each manifestnode included in the
       
  1060             # changegroup about which filenodes the manifest node references
       
  1061             # so we can include those in the changegroup too.
       
  1062             #
       
  1063             # It also remembers which changenode each filenode belongs to.  It
       
  1064             # does this by assuming the a filenode belongs to the changenode
       
  1065             # the first manifest that references it belongs to.
       
  1066             def collect_msng_filenodes(mnfstnode):
       
  1067                 r = mnfst.rev(mnfstnode)
       
  1068                 if r == next_rev[0]:
       
  1069                     # If the last rev we looked at was the one just previous,
       
  1070                     # we only need to see a diff.
       
  1071                     delta = mdiff.patchtext(mnfst.delta(mnfstnode))
       
  1072                     # For each line in the delta
       
  1073                     for dline in delta.splitlines():
       
  1074                         # get the filename and filenode for that line
       
  1075                         f, fnode = dline.split('\0')
       
  1076                         fnode = bin(fnode[:40])
       
  1077                         f = changedfiles.get(f, None)
       
  1078                         # And if the file is in the list of files we care
       
  1079                         # about.
       
  1080                         if f is not None:
       
  1081                             # Get the changenode this manifest belongs to
       
  1082                             clnode = msng_mnfst_set[mnfstnode]
       
  1083                             # Create the set of filenodes for the file if
       
  1084                             # there isn't one already.
       
  1085                             ndset = msng_filenode_set.setdefault(f, {})
       
  1086                             # And set the filenode's changelog node to the
       
  1087                             # manifest's if it hasn't been set already.
       
  1088                             ndset.setdefault(fnode, clnode)
       
  1089                 else:
       
  1090                     # Otherwise we need a full manifest.
       
  1091                     m = mnfst.read(mnfstnode)
       
  1092                     # For every file in we care about.
       
  1093                     for f in changedfiles:
       
  1094                         fnode = m.get(f, None)
       
  1095                         # If it's in the manifest
       
  1096                         if fnode is not None:
       
  1097                             # See comments above.
       
  1098                             clnode = msng_mnfst_set[mnfstnode]
       
  1099                             ndset = msng_filenode_set.setdefault(f, {})
       
  1100                             ndset.setdefault(fnode, clnode)
       
  1101                 # Remember the revision we hope to see next.
       
  1102                 next_rev[0] = r + 1
       
  1103             return collect_msng_filenodes
       
  1104 
       
  1105         # We have a list of filenodes we think we need for a file, lets remove
       
  1106         # all those we now the recipient must have.
       
  1107         def prune_filenodes(f, filerevlog):
       
  1108             msngset = msng_filenode_set[f]
       
  1109             hasset = {}
       
  1110             # If a 'missing' filenode thinks it belongs to a changenode we
       
  1111             # assume the recipient must have, then the recipient must have
       
  1112             # that filenode.
       
  1113             for n in msngset:
       
  1114                 clnode = cl.node(filerevlog.linkrev(n))
       
  1115                 if clnode in has_cl_set:
       
  1116                     hasset[n] = 1
       
  1117             prune_parents(filerevlog, hasset, msngset)
       
  1118 
       
  1119         # A function generator function that sets up the a context for the
       
  1120         # inner function.
       
  1121         def lookup_filenode_link_func(fname):
       
  1122             msngset = msng_filenode_set[fname]
       
  1123             # Lookup the changenode the filenode belongs to.
       
  1124             def lookup_filenode_link(fnode):
       
  1125                 return msngset[fnode]
       
  1126             return lookup_filenode_link
       
  1127 
       
  1128         # Now that we have all theses utility functions to help out and
       
  1129         # logically divide up the task, generate the group.
       
  1130         def gengroup():
       
  1131             # The set of changed files starts empty.
       
  1132             changedfiles = {}
       
  1133             # Create a changenode group generator that will call our functions
       
  1134             # back to lookup the owning changenode and collect information.
       
  1135             group = cl.group(msng_cl_lst, identity,
       
  1136                              manifest_and_file_collector(changedfiles))
       
  1137             for chnk in group:
       
  1138                 yield chnk
       
  1139 
       
  1140             # The list of manifests has been collected by the generator
       
  1141             # calling our functions back.
       
  1142             prune_manifests()
       
  1143             msng_mnfst_lst = msng_mnfst_set.keys()
       
  1144             # Sort the manifestnodes by revision number.
       
  1145             msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
       
  1146             # Create a generator for the manifestnodes that calls our lookup
       
  1147             # and data collection functions back.
       
  1148             group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
       
  1149                                 filenode_collector(changedfiles))
       
  1150             for chnk in group:
       
  1151                 yield chnk
       
  1152 
       
  1153             # These are no longer needed, dereference and toss the memory for
       
  1154             # them.
       
  1155             msng_mnfst_lst = None
       
  1156             msng_mnfst_set.clear()
       
  1157 
       
  1158             changedfiles = changedfiles.keys()
       
  1159             changedfiles.sort()
       
  1160             # Go through all our files in order sorted by name.
       
  1161             for fname in changedfiles:
       
  1162                 filerevlog = self.file(fname)
       
  1163                 # Toss out the filenodes that the recipient isn't really
       
  1164                 # missing.
       
  1165                 prune_filenodes(fname, filerevlog)
       
  1166                 msng_filenode_lst = msng_filenode_set[fname].keys()
       
  1167                 # If any filenodes are left, generate the group for them,
       
  1168                 # otherwise don't bother.
       
  1169                 if len(msng_filenode_lst) > 0:
       
  1170                     yield struct.pack(">l", len(fname) + 4) + fname
       
  1171                     # Sort the filenodes by their revision #
       
  1172                     msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
       
  1173                     # Create a group generator and only pass in a changenode
       
  1174                     # lookup function as we need to collect no information
       
  1175                     # from filenodes.
       
  1176                     group = filerevlog.group(msng_filenode_lst,
       
  1177                                              lookup_filenode_link_func(fname))
       
  1178                     for chnk in group:
       
  1179                         yield chnk
       
  1180                 # Don't need this anymore, toss it to free memory.
       
  1181                 del msng_filenode_set[fname]
       
  1182             # Signal that no more groups are left.
       
  1183             yield struct.pack(">l", 0)
       
  1184 
       
  1185         return util.chunkbuffer(gengroup())
       
  1186 
   948     def changegroup(self, basenodes):
  1187     def changegroup(self, basenodes):
   949         genread = util.chunkbuffer
  1188         """Generate a changegroup of all nodes that we have that a recipient
       
  1189         doesn't.
       
  1190 
       
  1191         This is much easier than the previous function as we can assume that
       
  1192         the recipient has any changenode we aren't sending them."""
       
  1193         cl = self.changelog
       
  1194         nodes = cl.nodesbetween(basenodes, None)[0]
       
  1195         revset = dict.fromkeys([cl.rev(n) for n in nodes])
       
  1196 
       
  1197         def identity(x):
       
  1198             return x
       
  1199 
       
  1200         def gennodelst(revlog):
       
  1201             for r in xrange(0, revlog.count()):
       
  1202                 n = revlog.node(r)
       
  1203                 if revlog.linkrev(n) in revset:
       
  1204                     yield n
       
  1205 
       
  1206         def changed_file_collector(changedfileset):
       
  1207             def collect_changed_files(clnode):
       
  1208                 c = cl.read(clnode)
       
  1209                 for fname in c[3]:
       
  1210                     changedfileset[fname] = 1
       
  1211             return collect_changed_files
       
  1212 
       
  1213         def lookuprevlink_func(revlog):
       
  1214             def lookuprevlink(n):
       
  1215                 return cl.node(revlog.linkrev(n))
       
  1216             return lookuprevlink
   950 
  1217 
   951         def gengroup():
  1218         def gengroup():
   952             nodes = self.newer(basenodes)
       
   953 
       
   954             # construct the link map
       
   955             linkmap = {}
       
   956             for n in nodes:
       
   957                 linkmap[self.changelog.rev(n)] = n
       
   958 
       
   959             # construct a list of all changed files
  1219             # construct a list of all changed files
   960             changed = {}
  1220             changedfiles = {}
   961             for n in nodes:
  1221 
   962                 c = self.changelog.read(n)
  1222             for chnk in cl.group(nodes, identity,
   963                 for f in c[3]:
  1223                                  changed_file_collector(changedfiles)):
   964                     changed[f] = 1
  1224                 yield chnk
   965             changed = changed.keys()
  1225             changedfiles = changedfiles.keys()
   966             changed.sort()
  1226             changedfiles.sort()
   967 
  1227 
   968             # the changegroup is changesets + manifests + all file revs
  1228             mnfst = self.manifest
   969             revs = [ self.changelog.rev(n) for n in nodes ]
  1229             nodeiter = gennodelst(mnfst)
   970 
  1230             for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
   971             for y in self.changelog.group(linkmap): yield y
  1231                 yield chnk
   972             for y in self.manifest.group(linkmap): yield y
  1232 
   973             for f in changed:
  1233             for fname in changedfiles:
   974                 yield struct.pack(">l", len(f) + 4) + f
  1234                 filerevlog = self.file(fname)
   975                 g = self.file(f).group(linkmap)
  1235                 nodeiter = gennodelst(filerevlog)
   976                 for y in g:
  1236                 nodeiter = list(nodeiter)
   977                     yield y
  1237                 if nodeiter:
       
  1238                     yield struct.pack(">l", len(fname) + 4) + fname
       
  1239                     lookup = lookuprevlink_func(filerevlog)
       
  1240                     for chnk in filerevlog.group(nodeiter, lookup):
       
  1241                         yield chnk
   978 
  1242 
   979             yield struct.pack(">l", 0)
  1243             yield struct.pack(">l", 0)
   980 
  1244 
   981         return genread(gengroup())
  1245         return util.chunkbuffer(gengroup())
   982 
  1246 
   983     def addchangegroup(self, source):
  1247     def addchangegroup(self, source):
   984 
  1248 
   985         def getchunk():
  1249         def getchunk():
   986             d = source.read(4)
  1250             d = source.read(4)