131 return l |
131 return l |
132 |
132 |
133 def checkcopies(c, man): |
133 def checkcopies(c, man): |
134 '''check possible copies for filectx c''' |
134 '''check possible copies for filectx c''' |
135 for of in findold(c): |
135 for of in findold(c): |
136 if of not in man: |
136 if of not in man: # original file not in other manifest? |
137 continue |
137 continue |
138 c2 = ctx(of, man[of]) |
138 c2 = ctx(of, man[of]) |
139 ca = c.ancestor(c2) |
139 ca = c.ancestor(c2) |
140 if not ca: # unrelated |
140 if not ca: # unrelated? |
141 continue |
141 continue |
|
142 # named changed on only one side? |
142 if ca.path() == c.path() or ca.path() == c2.path(): |
143 if ca.path() == c.path() or ca.path() == c2.path(): |
143 fullcopy[c.path()] = of |
144 fullcopy[c.path()] = of # remember for dir rename detection |
144 if c == ca and c2 == ca: # no merge needed, ignore copy |
145 if c == ca and c2 == ca: # no merge needed, ignore copy |
145 continue |
146 continue |
146 copy[c.path()] = of |
147 copy[c.path()] = of |
147 |
148 |
148 def dirs(files): |
149 def dirs(files): |
177 # generate a directory move map |
178 # generate a directory move map |
178 d1, d2 = dirs(m1), dirs(m2) |
179 d1, d2 = dirs(m1), dirs(m2) |
179 invalid = {} |
180 invalid = {} |
180 dirmove = {} |
181 dirmove = {} |
181 |
182 |
|
183 # examine each file copy for a potential directory move, which is |
|
184 # when all the files in a directory are moved to a new directory |
182 for dst, src in fullcopy.items(): |
185 for dst, src in fullcopy.items(): |
183 dsrc, ddst = os.path.dirname(src), os.path.dirname(dst) |
186 dsrc, ddst = os.path.dirname(src), os.path.dirname(dst) |
184 if dsrc in invalid: |
187 if dsrc in invalid: |
185 continue |
188 # already seen to be uninteresting |
186 elif (dsrc in d1 and ddst in d1) or (dsrc in d2 and ddst in d2): |
189 continue |
|
190 elif dsrc in d1 and ddst in d1: |
|
191 # directory wasn't entirely moved locally |
|
192 invalid[dsrc] = True |
|
193 elif dsrc in d2 and ddst in d2: |
|
194 # directory wasn't entirely moved remotely |
187 invalid[dsrc] = True |
195 invalid[dsrc] = True |
188 elif dsrc in dirmove and dirmove[dsrc] != ddst: |
196 elif dsrc in dirmove and dirmove[dsrc] != ddst: |
|
197 # files from the same directory moved to two different places |
189 invalid[dsrc] = True |
198 invalid[dsrc] = True |
190 del dirmove[dsrc] |
199 del dirmove[dsrc] |
191 else: |
200 else: |
|
201 # looks good so far |
192 dirmove[dsrc + "/"] = ddst + "/" |
202 dirmove[dsrc + "/"] = ddst + "/" |
193 |
203 |
194 del d1, d2, invalid |
204 del d1, d2, invalid |
195 |
205 |
196 if not dirmove: |
206 if not dirmove: |
197 return copy |
207 return copy |
198 |
208 |
199 # check unaccounted nonoverlapping files |
209 # check unaccounted nonoverlapping files against directory moves |
200 for f in u1 + u2: |
210 for f in u1 + u2: |
201 if f not in fullcopy: |
211 if f not in fullcopy: |
202 for d in dirmove: |
212 for d in dirmove: |
203 if f.startswith(d): |
213 if f.startswith(d): |
|
214 # new file added in a directory that was moved, move it |
204 copy[f] = dirmove[d] + f[len(d):] |
215 copy[f] = dirmove[d] + f[len(d):] |
205 break |
216 break |
206 |
217 |
207 return copy |
218 return copy |
208 |
219 |