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