##// END OF EJS Templates
Merge bundle -r work from Eric Hopper
Matt Mackall -
r1469:0847c45f merge default
parent child Browse files
Show More
@@ -0,0 +1,59 b''
1 #!/bin/bash
2
3 hg init test
4 cd test
5 cat >>afile <<EOF
6 0
7 EOF
8 hg add afile
9 hg commit -m "0.0"
10 cat >>afile <<EOF
11 1
12 EOF
13 hg commit -m "0.1"
14 cat >>afile <<EOF
15 2
16 EOF
17 hg commit -m "0.2"
18 cat >>afile <<EOF
19 3
20 EOF
21 hg commit -m "0.3"
22 hg update -C 0
23 cat >>afile <<EOF
24 1
25 EOF
26 hg commit -m "1.1"
27 cat >>afile <<EOF
28 2
29 EOF
30 hg commit -m "1.2"
31 cat >fred <<EOF
32 a line
33 EOF
34 cat >>afile <<EOF
35 3
36 EOF
37 hg add fred
38 hg commit -m "1.3"
39 hg mv afile adifferentfile
40 hg commit -m "1.3m"
41 hg update -C 3
42 hg mv afile anotherfile
43 hg commit -m "0.3m"
44 hg debugindex .hg/data/afile.i
45 hg debugindex .hg/data/adifferentfile.i
46 hg debugindex .hg/data/anotherfile.i
47 hg debugindex .hg/data/fred.i
48 hg debugindex .hg/00manifest.i
49 hg verify
50 cd ..
51 for i in 0 1 2 3 4 5 6 7 8; do
52 hg clone -r "$i" test test-"$i"
53 cd test-"$i"
54 hg verify
55 cd ..
56 done
57 cd test-8
58 hg pull ../test-7
59 hg verify
@@ -0,0 +1,126 b''
1 rev offset length base linkrev nodeid p1 p2
2 0 0 3 0 0 362fef284ce2 000000000000 000000000000
3 1 3 5 1 1 125144f7e028 362fef284ce2 000000000000
4 2 8 7 2 2 4c982badb186 125144f7e028 000000000000
5 3 15 9 3 3 19b1fc555737 4c982badb186 000000000000
6 rev offset length base linkrev nodeid p1 p2
7 0 0 75 0 7 905359268f77 000000000000 000000000000
8 rev offset length base linkrev nodeid p1 p2
9 0 0 75 0 8 905359268f77 000000000000 000000000000
10 rev offset length base linkrev nodeid p1 p2
11 0 0 8 0 6 12ab3bcc5ea4 000000000000 000000000000
12 rev offset length base linkrev nodeid p1 p2
13 0 0 48 0 0 43eadb1d2d06 000000000000 000000000000
14 1 48 48 1 1 8b89697eba2c 43eadb1d2d06 000000000000
15 2 96 48 2 2 626a32663c2f 8b89697eba2c 000000000000
16 3 144 48 3 3 f54c32f13478 626a32663c2f 000000000000
17 4 192 58 3 6 de68e904d169 626a32663c2f 000000000000
18 5 250 68 3 7 3b45cc2ab868 de68e904d169 000000000000
19 6 318 54 6 8 24d86153a002 f54c32f13478 000000000000
20 checking changesets
21 checking manifests
22 crosschecking files in changesets and manifests
23 checking files
24 4 files, 9 changesets, 7 total revisions
25 requesting all changes
26 adding changesets
27 adding manifests
28 adding file changes
29 added 1 changesets with 1 changes to 1 files
30 checking changesets
31 checking manifests
32 crosschecking files in changesets and manifests
33 checking files
34 1 files, 1 changesets, 1 total revisions
35 requesting all changes
36 adding changesets
37 adding manifests
38 adding file changes
39 added 2 changesets with 2 changes to 1 files
40 checking changesets
41 checking manifests
42 crosschecking files in changesets and manifests
43 checking files
44 1 files, 2 changesets, 2 total revisions
45 requesting all changes
46 adding changesets
47 adding manifests
48 adding file changes
49 added 3 changesets with 3 changes to 1 files
50 checking changesets
51 checking manifests
52 crosschecking files in changesets and manifests
53 checking files
54 1 files, 3 changesets, 3 total revisions
55 requesting all changes
56 adding changesets
57 adding manifests
58 adding file changes
59 added 4 changesets with 4 changes to 1 files
60 checking changesets
61 checking manifests
62 crosschecking files in changesets and manifests
63 checking files
64 1 files, 4 changesets, 4 total revisions
65 requesting all changes
66 adding changesets
67 adding manifests
68 adding file changes
69 added 2 changesets with 2 changes to 1 files
70 checking changesets
71 checking manifests
72 crosschecking files in changesets and manifests
73 checking files
74 1 files, 2 changesets, 2 total revisions
75 requesting all changes
76 adding changesets
77 adding manifests
78 adding file changes
79 added 3 changesets with 3 changes to 1 files
80 checking changesets
81 checking manifests
82 crosschecking files in changesets and manifests
83 checking files
84 1 files, 3 changesets, 3 total revisions
85 requesting all changes
86 adding changesets
87 adding manifests
88 adding file changes
89 added 4 changesets with 5 changes to 2 files
90 checking changesets
91 checking manifests
92 crosschecking files in changesets and manifests
93 checking files
94 2 files, 4 changesets, 5 total revisions
95 requesting all changes
96 adding changesets
97 adding manifests
98 adding file changes
99 added 5 changesets with 6 changes to 3 files
100 checking changesets
101 checking manifests
102 crosschecking files in changesets and manifests
103 checking files
104 3 files, 5 changesets, 6 total revisions
105 requesting all changes
106 adding changesets
107 adding manifests
108 adding file changes
109 added 5 changesets with 5 changes to 2 files
110 checking changesets
111 checking manifests
112 crosschecking files in changesets and manifests
113 checking files
114 2 files, 5 changesets, 5 total revisions
115 pulling from ../test-7
116 searching for changes
117 adding changesets
118 adding manifests
119 adding file changes
120 added 4 changesets with 2 changes to 3 files (+1 heads)
121 (run 'hg update' to get a working copy)
122 checking changesets
123 checking manifests
124 crosschecking files in changesets and manifests
125 checking files
126 4 files, 9 changesets, 7 total revisions
@@ -693,7 +693,7 b' def clone(ui, source, dest=None, **opts)'
693 copy = False
693 copy = False
694 if other.dev() != -1:
694 if other.dev() != -1:
695 abspath = os.path.abspath(source)
695 abspath = os.path.abspath(source)
696 if not opts['pull']:
696 if not opts['pull'] and not opts['rev']:
697 copy = True
697 copy = True
698
698
699 if copy:
699 if copy:
@@ -723,8 +723,14 b' def clone(ui, source, dest=None, **opts)'
723 repo = hg.repository(ui, dest)
723 repo = hg.repository(ui, dest)
724
724
725 else:
725 else:
726 revs = None
727 if opts['rev']:
728 if not other.local():
729 raise util.Abort("clone -r not supported yet for remote repositories.")
730 else:
731 revs = [other.lookup(rev) for rev in opts['rev']]
726 repo = hg.repository(ui, dest, create=1)
732 repo = hg.repository(ui, dest, create=1)
727 repo.pull(other)
733 repo.pull(other, heads = revs)
728
734
729 f = repo.opener("hgrc", "w", text=True)
735 f = repo.opener("hgrc", "w", text=True)
730 f.write("[paths]\n")
736 f.write("[paths]\n")
@@ -1396,7 +1402,7 b' def incoming(ui, repo, source="default",'
1396 o = repo.findincoming(other)
1402 o = repo.findincoming(other)
1397 if not o:
1403 if not o:
1398 return
1404 return
1399 o = other.newer(o)
1405 o = other.changelog.nodesbetween(o)[0]
1400 if opts['newest_first']:
1406 if opts['newest_first']:
1401 o.reverse()
1407 o.reverse()
1402 for n in o:
1408 for n in o:
@@ -1561,7 +1567,7 b' def outgoing(ui, repo, dest="default-pus'
1561 dest = ui.expandpath(dest, repo.root)
1567 dest = ui.expandpath(dest, repo.root)
1562 other = hg.repository(ui, dest)
1568 other = hg.repository(ui, dest)
1563 o = repo.findoutgoing(other)
1569 o = repo.findoutgoing(other)
1564 o = repo.newer(o)
1570 o = repo.changelog.nodesbetween(o)[0]
1565 if opts['newest_first']:
1571 if opts['newest_first']:
1566 o.reverse()
1572 o.reverse()
1567 for n in o:
1573 for n in o:
@@ -1643,7 +1649,12 b' def pull(ui, repo, source="default", **o'
1643 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1649 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1644
1650
1645 other = hg.repository(ui, source)
1651 other = hg.repository(ui, source)
1646 r = repo.pull(other)
1652 revs = None
1653 if opts['rev'] and not other.local():
1654 raise util.Abort("pull -r doesn't work for remote repositories yet")
1655 elif opts['rev']:
1656 revs = [other.lookup(rev) for rev in opts['rev']]
1657 r = repo.pull(other, heads=revs)
1647 if not r:
1658 if not r:
1648 if opts['update']:
1659 if opts['update']:
1649 return update(ui, repo)
1660 return update(ui, repo)
@@ -2193,6 +2204,7 b' table = {'
2193 [('U', 'noupdate', None, _('do not update the new working directory')),
2204 [('U', 'noupdate', None, _('do not update the new working directory')),
2194 ('e', 'ssh', "", _('specify ssh command to use')),
2205 ('e', 'ssh', "", _('specify ssh command to use')),
2195 ('', 'pull', None, _('use pull protocol to copy metadata')),
2206 ('', 'pull', None, _('use pull protocol to copy metadata')),
2207 ('r', 'rev', [], _('a changeset you would like to have after cloning')),
2196 ('', 'remotecmd', "", _('specify hg command to run on the remote side'))],
2208 ('', 'remotecmd', "", _('specify hg command to run on the remote side'))],
2197 _('hg clone [OPTION]... SOURCE [DEST]')),
2209 _('hg clone [OPTION]... SOURCE [DEST]')),
2198 "^commit|ci":
2210 "^commit|ci":
@@ -2304,8 +2316,9 b' table = {'
2304 (pull,
2316 (pull,
2305 [('u', 'update', None, _('update the working directory to tip after pull')),
2317 [('u', 'update', None, _('update the working directory to tip after pull')),
2306 ('e', 'ssh', "", _('specify ssh command to use')),
2318 ('e', 'ssh', "", _('specify ssh command to use')),
2319 ('r', 'rev', [], _('a specific revision you would like to pull')),
2307 ('', 'remotecmd', "", _('specify hg command to run on the remote side'))],
2320 ('', 'remotecmd', "", _('specify hg command to run on the remote side'))],
2308 _('hg pull [-u] [-e FILE] [--remotecmd FILE] [SOURCE]')),
2321 _('hg pull [-u] [-e FILE] [-r rev] [--remotecmd FILE] [SOURCE]')),
2309 "^push":
2322 "^push":
2310 (push,
2323 (push,
2311 [('f', 'force', None, _('force push')),
2324 [('f', 'force', None, _('force push')),
@@ -727,32 +727,6 b' class localrepository:'
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 = []
@@ -903,7 +877,7 b' class localrepository:'
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
@@ -917,7 +891,10 b' class localrepository:'
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):
@@ -945,40 +922,327 b' class localrepository:'
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)
1219 # construct a list of all changed files
1220 changedfiles = {}
953
1221
954 # construct the link map
1222 for chnk in cl.group(nodes, identity,
955 linkmap = {}
1223 changed_file_collector(changedfiles)):
956 for n in nodes:
1224 yield chnk
957 linkmap[self.changelog.rev(n)] = n
1225 changedfiles = changedfiles.keys()
1226 changedfiles.sort()
958
1227
959 # construct a list of all changed files
1228 mnfst = self.manifest
960 changed = {}
1229 nodeiter = gennodelst(mnfst)
961 for n in nodes:
1230 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
962 c = self.changelog.read(n)
1231 yield chnk
963 for f in c[3]:
964 changed[f] = 1
965 changed = changed.keys()
966 changed.sort()
967
1232
968 # the changegroup is changesets + manifests + all file revs
1233 for fname in changedfiles:
969 revs = [ self.changelog.rev(n) for n in nodes ]
1234 filerevlog = self.file(fname)
970
1235 nodeiter = gennodelst(filerevlog)
971 for y in self.changelog.group(linkmap): yield y
1236 nodeiter = list(nodeiter)
972 for y in self.manifest.group(linkmap): yield y
1237 if nodeiter:
973 for f in changed:
1238 yield struct.pack(">l", len(fname) + 4) + fname
974 yield struct.pack(">l", len(f) + 4) + f
1239 lookup = lookuprevlink_func(filerevlog)
975 g = self.file(f).group(linkmap)
1240 for chnk in filerevlog.group(nodeiter, lookup):
976 for y in g:
1241 yield chnk
977 yield y
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
@@ -249,6 +249,157 b' class revlog:'
249 visit.append(p)
249 visit.append(p)
250 return reachable
250 return reachable
251
251
252 def nodesbetween(self, roots=None, heads=None):
253 """Return a tuple containing three elements. Elements 1 and 2 contain
254 a final list bases and heads after all the unreachable ones have been
255 pruned. Element 0 contains a topologically sorted list of all
256
257 nodes that satisfy these constraints:
258 1. All nodes must be descended from a node in roots (the nodes on
259 roots are considered descended from themselves).
260 2. All nodes must also be ancestors of a node in heads (the nodes in
261 heads are considered to be their own ancestors).
262
263 If roots is unspecified, nullid is assumed as the only root.
264 If heads is unspecified, it is taken to be the output of the
265 heads method (i.e. a list of all nodes in the repository that
266 have no children)."""
267 nonodes = ([], [], [])
268 if roots is not None:
269 roots = list(roots)
270 if not roots:
271 return nonodes
272 lowestrev = min([self.rev(n) for n in roots])
273 else:
274 roots = [nullid] # Everybody's a descendent of nullid
275 lowestrev = -1
276 if (lowestrev == -1) and (heads is None):
277 # We want _all_ the nodes!
278 return ([self.node(r) for r in xrange(0, self.count())],
279 [nullid], list(self.heads()))
280 if heads is None:
281 # All nodes are ancestors, so the latest ancestor is the last
282 # node.
283 highestrev = self.count() - 1
284 # Set ancestors to None to signal that every node is an ancestor.
285 ancestors = None
286 # Set heads to an empty dictionary for later discovery of heads
287 heads = {}
288 else:
289 heads = list(heads)
290 if not heads:
291 return nonodes
292 ancestors = {}
293 # Start at the top and keep marking parents until we're done.
294 nodestotag = heads[:]
295 # Turn heads into a dictionary so we can remove 'fake' heads.
296 # Also, later we will be using it to filter out the heads we can't
297 # find from roots.
298 heads = dict.fromkeys(heads, 0)
299 # Remember where the top was so we can use it as a limit later.
300 highestrev = max([self.rev(n) for n in nodestotag])
301 while nodestotag:
302 # grab a node to tag
303 n = nodestotag.pop()
304 # Never tag nullid
305 if n == nullid:
306 continue
307 # A node's revision number represents its place in a
308 # topologically sorted list of nodes.
309 r = self.rev(n)
310 if r >= lowestrev:
311 if n not in ancestors:
312 # If we are possibly a descendent of one of the roots
313 # and we haven't already been marked as an ancestor
314 ancestors[n] = 1 # Mark as ancestor
315 # Add non-nullid parents to list of nodes to tag.
316 nodestotag.extend([p for p in self.parents(n) if
317 p != nullid])
318 elif n in heads: # We've seen it before, is it a fake head?
319 # So it is, real heads should not be the ancestors of
320 # any other heads.
321 heads.pop(n)
322 if not ancestors:
323 return nonodes
324 # Now that we have our set of ancestors, we want to remove any
325 # roots that are not ancestors.
326
327 # If one of the roots was nullid, everything is included anyway.
328 if lowestrev > -1:
329 # But, since we weren't, let's recompute the lowest rev to not
330 # include roots that aren't ancestors.
331
332 # Filter out roots that aren't ancestors of heads
333 roots = [n for n in roots if n in ancestors]
334 # Recompute the lowest revision
335 if roots:
336 lowestrev = min([self.rev(n) for n in roots])
337 else:
338 # No more roots? Return empty list
339 return nonodes
340 else:
341 # We are descending from nullid, and don't need to care about
342 # any other roots.
343 lowestrev = -1
344 roots = [nullid]
345 # Transform our roots list into a 'set' (i.e. a dictionary where the
346 # values don't matter.
347 descendents = dict.fromkeys(roots, 1)
348 # Also, keep the original roots so we can filter out roots that aren't
349 # 'real' roots (i.e. are descended from other roots).
350 roots = descendents.copy()
351 # Our topologically sorted list of output nodes.
352 orderedout = []
353 # Don't start at nullid since we don't want nullid in our output list,
354 # and if nullid shows up in descedents, empty parents will look like
355 # they're descendents.
356 for r in xrange(max(lowestrev, 0), highestrev + 1):
357 n = self.node(r)
358 isdescendent = False
359 if lowestrev == -1: # Everybody is a descendent of nullid
360 isdescendent = True
361 elif n in descendents:
362 # n is already a descendent
363 isdescendent = True
364 # This check only needs to be done here because all the roots
365 # will start being marked is descendents before the loop.
366 if n in roots:
367 # If n was a root, check if it's a 'real' root.
368 p = tuple(self.parents(n))
369 # If any of its parents are descendents, it's not a root.
370 if (p[0] in descendents) or (p[1] in descendents):
371 roots.pop(n)
372 else:
373 p = tuple(self.parents(n))
374 # A node is a descendent if either of its parents are
375 # descendents. (We seeded the dependents list with the roots
376 # up there, remember?)
377 if (p[0] in descendents) or (p[1] in descendents):
378 descendents[n] = 1
379 isdescendent = True
380 if isdescendent and ((ancestors is None) or (n in ancestors)):
381 # Only include nodes that are both descendents and ancestors.
382 orderedout.append(n)
383 if (ancestors is not None) and (n in heads):
384 # We're trying to figure out which heads are reachable
385 # from roots.
386 # Mark this head as having been reached
387 heads[n] = 1
388 elif ancestors is None:
389 # Otherwise, we're trying to discover the heads.
390 # Assume this is a head because if it isn't, the next step
391 # will eventually remove it.
392 heads[n] = 1
393 # But, obviously its parents aren't.
394 for p in self.parents(n):
395 heads.pop(p, None)
396 heads = [n for n in heads.iterkeys() if heads[n] != 0]
397 roots = roots.keys()
398 assert orderedout
399 assert roots
400 assert heads
401 return (orderedout, roots, heads)
402
252 def heads(self, stop=None):
403 def heads(self, stop=None):
253 """return the list of all nodes that have no children"""
404 """return the list of all nodes that have no children"""
254 p = {}
405 p = {}
@@ -482,7 +633,7 b' class revlog:'
482 #print "next x"
633 #print "next x"
483 gx = x.next()
634 gx = x.next()
484
635
485 def group(self, linkmap):
636 def group(self, nodelist, lookup, infocollect = None):
486 """calculate a delta group
637 """calculate a delta group
487
638
488 Given a list of changeset revs, return a set of deltas and
639 Given a list of changeset revs, return a set of deltas and
@@ -491,14 +642,8 b' class revlog:'
491 have this parent as it has all history before these
642 have this parent as it has all history before these
492 changesets. parent is parent[0]
643 changesets. parent is parent[0]
493 """
644 """
494 revs = []
645 revs = [self.rev(n) for n in nodelist]
495 needed = {}
646 needed = dict.fromkeys(revs, 1)
496
497 # find file nodes/revs that match changeset revs
498 for i in xrange(0, self.count()):
499 if self.index[i][3] in linkmap:
500 revs.append(i)
501 needed[i] = 1
502
647
503 # if we don't have any revisions touched by these changesets, bail
648 # if we don't have any revisions touched by these changesets, bail
504 if not revs:
649 if not revs:
@@ -566,6 +711,9 b' class revlog:'
566 a, b = revs[d], revs[d + 1]
711 a, b = revs[d], revs[d + 1]
567 n = self.node(b)
712 n = self.node(b)
568
713
714 if infocollect is not None:
715 infocollect(n)
716
569 # do we need to construct a new delta?
717 # do we need to construct a new delta?
570 if a + 1 != b or self.base(b) == b:
718 if a + 1 != b or self.base(b) == b:
571 if a >= 0:
719 if a >= 0:
@@ -587,7 +735,7 b' class revlog:'
587 d = chunks[b]
735 d = chunks[b]
588
736
589 p = self.parents(n)
737 p = self.parents(n)
590 meta = n + p[0] + p[1] + linkmap[self.linkrev(n)]
738 meta = n + p[0] + p[1] + lookup(n)
591 l = struct.pack(">l", len(meta) + len(d) + 4)
739 l = struct.pack(">l", len(meta) + len(d) + 4)
592 yield l
740 yield l
593 yield meta
741 yield meta
General Comments 0
You need to be logged in to leave comments. Login now