Mercurial > hg > mercurial-crew-with-dirclash
comparison mercurial/merge.py @ 4404:47371e1c1db4
Merge with stable
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Thu, 03 May 2007 17:27:21 -0500 |
parents | 10edaed7f909 84cd52b48f94 |
children | bbc97d419b16 |
comparison
equal
deleted
inserted
replaced
4396:15289406f89c | 4404:47371e1c1db4 |
---|---|
99 | 99 |
100 def findcopies(repo, m1, m2, ma, limit): | 100 def findcopies(repo, m1, m2, ma, limit): |
101 """ | 101 """ |
102 Find moves and copies between m1 and m2 back to limit linkrev | 102 Find moves and copies between m1 and m2 back to limit linkrev |
103 """ | 103 """ |
104 | |
105 def nonoverlap(d1, d2, d3): | |
106 "Return list of elements in d1 not in d2 or d3" | |
107 l = [d for d in d1 if d not in d3 and d not in d2] | |
108 l.sort() | |
109 return l | |
110 | |
111 def dirname(f): | |
112 s = f.rfind("/") | |
113 if s == -1: | |
114 return "" | |
115 return f[:s] | |
116 | |
117 def dirs(files): | |
118 d = {} | |
119 for f in files: | |
120 f = dirname(f) | |
121 while f not in d: | |
122 d[f] = True | |
123 f = dirname(f) | |
124 return d | |
104 | 125 |
105 def findold(fctx): | 126 def findold(fctx): |
106 "find files that path was copied from, back to linkrev limit" | 127 "find files that path was copied from, back to linkrev limit" |
107 old = {} | 128 old = {} |
108 seen = {} | 129 seen = {} |
122 | 143 |
123 old = old.keys() | 144 old = old.keys() |
124 old.sort() | 145 old.sort() |
125 return old | 146 return old |
126 | 147 |
127 def nonoverlap(d1, d2, d3): | 148 copy = {} |
128 "Return list of elements in d1 not in d2 or d3" | 149 fullcopy = {} |
129 l = [d for d in d1 if d not in d3 and d not in d2] | |
130 l.sort() | |
131 return l | |
132 | 150 |
133 def checkcopies(c, man): | 151 def checkcopies(c, man): |
134 '''check possible copies for filectx c''' | 152 '''check possible copies for filectx c''' |
135 for of in findold(c): | 153 for of in findold(c): |
136 if of not in man: | 154 if of not in man: # original file not in other manifest? |
137 continue | 155 continue |
138 c2 = ctx(of, man[of]) | 156 c2 = ctx(of, man[of]) |
139 ca = c.ancestor(c2) | 157 ca = c.ancestor(c2) |
140 if not ca: # unrelated | 158 if not ca: # unrelated? |
141 continue | 159 continue |
160 # named changed on only one side? | |
142 if ca.path() == c.path() or ca.path() == c2.path(): | 161 if ca.path() == c.path() or ca.path() == c2.path(): |
143 fullcopy[c.path()] = of | 162 fullcopy[c.path()] = of # remember for dir rename detection |
144 if c == ca and c2 == ca: # no merge needed, ignore copy | 163 if c == c2: # no merge needed, ignore copy |
145 continue | 164 continue |
146 copy[c.path()] = of | 165 copy[c.path()] = of |
147 | |
148 def dirs(files): | |
149 d = {} | |
150 for f in files: | |
151 d[os.path.dirname(f)] = True | |
152 return d | |
153 | 166 |
154 if not repo.ui.configbool("merge", "followcopies", True): | 167 if not repo.ui.configbool("merge", "followcopies", True): |
155 return {} | 168 return {} |
156 | 169 |
157 # avoid silly behavior for update from empty dir | 170 # avoid silly behavior for update from empty dir |
158 if not m1 or not m2 or not ma: | 171 if not m1 or not m2 or not ma: |
159 return {} | 172 return {} |
160 | 173 |
161 dcopies = repo.dirstate.copies() | 174 dcopies = repo.dirstate.copies() |
162 copy = {} | |
163 fullcopy = {} | |
164 u1 = nonoverlap(m1, m2, ma) | 175 u1 = nonoverlap(m1, m2, ma) |
165 u2 = nonoverlap(m2, m1, ma) | 176 u2 = nonoverlap(m2, m1, ma) |
166 ctx = util.cachefunc(lambda f, n: repo.filectx(f, fileid=n[:20])) | 177 ctx = util.cachefunc(lambda f, n: repo.filectx(f, fileid=n[:20])) |
167 | 178 |
168 for f in u1: | 179 for f in u1: |
177 # generate a directory move map | 188 # generate a directory move map |
178 d1, d2 = dirs(m1), dirs(m2) | 189 d1, d2 = dirs(m1), dirs(m2) |
179 invalid = {} | 190 invalid = {} |
180 dirmove = {} | 191 dirmove = {} |
181 | 192 |
193 # examine each file copy for a potential directory move, which is | |
194 # when all the files in a directory are moved to a new directory | |
182 for dst, src in fullcopy.items(): | 195 for dst, src in fullcopy.items(): |
183 dsrc, ddst = os.path.dirname(src), os.path.dirname(dst) | 196 dsrc, ddst = dirname(src), dirname(dst) |
184 if dsrc in invalid: | 197 if dsrc in invalid: |
185 continue | 198 # already seen to be uninteresting |
186 elif (dsrc in d1 and ddst in d1) or (dsrc in d2 and ddst in d2): | 199 continue |
200 elif dsrc in d1 and ddst in d1: | |
201 # directory wasn't entirely moved locally | |
202 invalid[dsrc] = True | |
203 elif dsrc in d2 and ddst in d2: | |
204 # directory wasn't entirely moved remotely | |
187 invalid[dsrc] = True | 205 invalid[dsrc] = True |
188 elif dsrc in dirmove and dirmove[dsrc] != ddst: | 206 elif dsrc in dirmove and dirmove[dsrc] != ddst: |
207 # files from the same directory moved to two different places | |
189 invalid[dsrc] = True | 208 invalid[dsrc] = True |
190 del dirmove[dsrc] | |
191 else: | 209 else: |
210 # looks good so far | |
192 dirmove[dsrc + "/"] = ddst + "/" | 211 dirmove[dsrc + "/"] = ddst + "/" |
212 | |
213 for i in invalid: | |
214 if i in dirmove: | |
215 del dirmove[i] | |
193 | 216 |
194 del d1, d2, invalid | 217 del d1, d2, invalid |
195 | 218 |
196 if not dirmove: | 219 if not dirmove: |
197 return copy | 220 return copy |
198 | 221 |
199 # check unaccounted nonoverlapping files | 222 # check unaccounted nonoverlapping files against directory moves |
200 for f in u1 + u2: | 223 for f in u1 + u2: |
201 if f not in fullcopy: | 224 if f not in fullcopy: |
202 for d in dirmove: | 225 for d in dirmove: |
203 if f.startswith(d): | 226 if f.startswith(d): |
227 # new file added in a directory that was moved, move it | |
204 copy[f] = dirmove[d] + f[len(d):] | 228 copy[f] = dirmove[d] + f[len(d):] |
205 break | 229 break |
206 | 230 |
207 return copy | 231 return copy |
208 | 232 |