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) |