Mercurial > public > mercurial-scm > hg
comparison mercurial/localrepo.py @ 1469:0847c45ffee6
Merge bundle -r work from Eric Hopper
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Thu, 27 Oct 2005 12:26:16 -0700 |
parents | 182879d71922 b6d9ea0bc107 |
children | 7d66ce9895fa |
comparison
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) |