Mercurial > hg
comparison mercurial/merge.py @ 6269:ffdf70e74623
merge: privatize some functions, unnest some others
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Sat, 15 Mar 2008 10:02:31 -0500 |
parents | 7e4f66fe964b |
children | 14f0fe2e2db7 |
comparison
equal
deleted
inserted
replaced
6268:7e4f66fe964b | 6269:ffdf70e74623 |
---|---|
7 | 7 |
8 from node import nullid, nullrev | 8 from node import nullid, nullrev |
9 from i18n import _ | 9 from i18n import _ |
10 import errno, util, os, heapq, filemerge | 10 import errno, util, os, heapq, filemerge |
11 | 11 |
12 def checkunknown(wctx, mctx): | 12 def _checkunknown(wctx, mctx): |
13 "check for collisions between unknown files and files in mctx" | 13 "check for collisions between unknown files and files in mctx" |
14 man = mctx.manifest() | 14 man = mctx.manifest() |
15 for f in wctx.unknown(): | 15 for f in wctx.unknown(): |
16 if f in man: | 16 if f in man: |
17 if mctx.filectx(f).cmp(wctx.filectx(f).data()): | 17 if mctx.filectx(f).cmp(wctx.filectx(f).data()): |
18 raise util.Abort(_("untracked file in working directory differs" | 18 raise util.Abort(_("untracked file in working directory differs" |
19 " from file in requested revision: '%s'") | 19 " from file in requested revision: '%s'") |
20 % f) | 20 % f) |
21 | 21 |
22 def checkcollision(mctx): | 22 def _checkcollision(mctx): |
23 "check for case folding collisions in the destination context" | 23 "check for case folding collisions in the destination context" |
24 folded = {} | 24 folded = {} |
25 for fn in mctx.manifest(): | 25 for fn in mctx.manifest(): |
26 fold = fn.lower() | 26 fold = fn.lower() |
27 if fold in folded: | 27 if fold in folded: |
28 raise util.Abort(_("case-folding collision between %s and %s") | 28 raise util.Abort(_("case-folding collision between %s and %s") |
29 % (fn, folded[fold])) | 29 % (fn, folded[fold])) |
30 folded[fold] = fn | 30 folded[fold] = fn |
31 | 31 |
32 def forgetremoved(wctx, mctx, branchmerge): | 32 def _forgetremoved(wctx, mctx, branchmerge): |
33 """ | 33 """ |
34 Forget removed files | 34 Forget removed files |
35 | 35 |
36 If we're jumping between revisions (as opposed to merging), and if | 36 If we're jumping between revisions (as opposed to merging), and if |
37 neither the working directory nor the target rev has the file, | 37 neither the working directory nor the target rev has the file, |
56 if f not in man: | 56 if f not in man: |
57 action.append((f, "f")) | 57 action.append((f, "f")) |
58 | 58 |
59 return action | 59 return action |
60 | 60 |
61 def _nonoverlap(d1, d2, d3): | |
62 "Return list of elements in d1 not in d2 or d3" | |
63 l = [d for d in d1 if d not in d3 and d not in d2] | |
64 l.sort() | |
65 return l | |
66 | |
67 def _dirname(f): | |
68 s = f.rfind("/") | |
69 if s == -1: | |
70 return "" | |
71 return f[:s] | |
72 | |
73 def _dirs(files): | |
74 d = {} | |
75 for f in files: | |
76 f = _dirname(f) | |
77 while f not in d: | |
78 d[f] = True | |
79 f = _dirname(f) | |
80 return d | |
81 | |
82 def _findoldnames(fctx, limit): | |
83 "find files that path was copied from, back to linkrev limit" | |
84 old = {} | |
85 seen = {} | |
86 orig = fctx.path() | |
87 visit = [fctx] | |
88 while visit: | |
89 fc = visit.pop() | |
90 s = str(fc) | |
91 if s in seen: | |
92 continue | |
93 seen[s] = 1 | |
94 if fc.path() != orig and fc.path() not in old: | |
95 old[fc.path()] = 1 | |
96 if fc.rev() < limit: | |
97 continue | |
98 visit += fc.parents() | |
99 | |
100 old = old.keys() | |
101 old.sort() | |
102 return old | |
103 | |
61 def findcopies(repo, m1, m2, ma, limit): | 104 def findcopies(repo, m1, m2, ma, limit): |
62 """ | 105 """ |
63 Find moves and copies between m1 and m2 back to limit linkrev | 106 Find moves and copies between m1 and m2 back to limit linkrev |
64 """ | 107 """ |
65 | |
66 def nonoverlap(d1, d2, d3): | |
67 "Return list of elements in d1 not in d2 or d3" | |
68 l = [d for d in d1 if d not in d3 and d not in d2] | |
69 l.sort() | |
70 return l | |
71 | |
72 def dirname(f): | |
73 s = f.rfind("/") | |
74 if s == -1: | |
75 return "" | |
76 return f[:s] | |
77 | |
78 def dirs(files): | |
79 d = {} | |
80 for f in files: | |
81 f = dirname(f) | |
82 while f not in d: | |
83 d[f] = True | |
84 f = dirname(f) | |
85 return d | |
86 | 108 |
87 wctx = repo.workingctx() | 109 wctx = repo.workingctx() |
88 | 110 |
89 def makectx(f, n): | 111 def makectx(f, n): |
90 if len(n) == 20: | 112 if len(n) == 20: |
91 return repo.filectx(f, fileid=n) | 113 return repo.filectx(f, fileid=n) |
92 return wctx.filectx(f) | 114 return wctx.filectx(f) |
93 ctx = util.cachefunc(makectx) | 115 ctx = util.cachefunc(makectx) |
94 | 116 |
95 def findold(fctx): | |
96 "find files that path was copied from, back to linkrev limit" | |
97 old = {} | |
98 seen = {} | |
99 orig = fctx.path() | |
100 visit = [fctx] | |
101 while visit: | |
102 fc = visit.pop() | |
103 s = str(fc) | |
104 if s in seen: | |
105 continue | |
106 seen[s] = 1 | |
107 if fc.path() != orig and fc.path() not in old: | |
108 old[fc.path()] = 1 | |
109 if fc.rev() < limit and fc.rev() is not None: | |
110 continue | |
111 visit += fc.parents() | |
112 | |
113 old = old.keys() | |
114 old.sort() | |
115 return old | |
116 | |
117 copy = {} | 117 copy = {} |
118 fullcopy = {} | 118 fullcopy = {} |
119 diverge = {} | 119 diverge = {} |
120 | 120 |
121 def checkcopies(c, man, aman): | 121 def checkcopies(c, man, aman): |
122 '''check possible copies for filectx c''' | 122 '''check possible copies for filectx c''' |
123 for of in findold(c): | 123 for of in _findoldnames(c, limit): |
124 fullcopy[c.path()] = of # remember for dir rename detection | 124 fullcopy[c.path()] = of # remember for dir rename detection |
125 if of not in man: # original file not in other manifest? | 125 if of not in man: # original file not in other manifest? |
126 if of in ma: | 126 if of in ma: |
127 diverge.setdefault(of, []).append(c.path()) | 127 diverge.setdefault(of, []).append(c.path()) |
128 continue | 128 continue |
147 if not m1 or not m2 or not ma: | 147 if not m1 or not m2 or not ma: |
148 return {}, {} | 148 return {}, {} |
149 | 149 |
150 repo.ui.debug(_(" searching for copies back to rev %d\n") % limit) | 150 repo.ui.debug(_(" searching for copies back to rev %d\n") % limit) |
151 | 151 |
152 u1 = nonoverlap(m1, m2, ma) | 152 u1 = _nonoverlap(m1, m2, ma) |
153 u2 = nonoverlap(m2, m1, ma) | 153 u2 = _nonoverlap(m2, m1, ma) |
154 | 154 |
155 if u1: | 155 if u1: |
156 repo.ui.debug(_(" unmatched files in local:\n %s\n") | 156 repo.ui.debug(_(" unmatched files in local:\n %s\n") |
157 % "\n ".join(u1)) | 157 % "\n ".join(u1)) |
158 if u2: | 158 if u2: |
186 return copy, diverge | 186 return copy, diverge |
187 | 187 |
188 repo.ui.debug(_(" checking for directory renames\n")) | 188 repo.ui.debug(_(" checking for directory renames\n")) |
189 | 189 |
190 # generate a directory move map | 190 # generate a directory move map |
191 d1, d2 = dirs(m1), dirs(m2) | 191 d1, d2 = _dirs(m1), _dirs(m2) |
192 invalid = {} | 192 invalid = {} |
193 dirmove = {} | 193 dirmove = {} |
194 | 194 |
195 # examine each file copy for a potential directory move, which is | 195 # examine each file copy for a potential directory move, which is |
196 # when all the files in a directory are moved to a new directory | 196 # when all the files in a directory are moved to a new directory |
197 for dst, src in fullcopy.items(): | 197 for dst, src in fullcopy.items(): |
198 dsrc, ddst = dirname(src), dirname(dst) | 198 dsrc, ddst = _dirname(src), _dirname(dst) |
199 if dsrc in invalid: | 199 if dsrc in invalid: |
200 # already seen to be uninteresting | 200 # already seen to be uninteresting |
201 continue | 201 continue |
202 elif dsrc in d1 and ddst in d1: | 202 elif dsrc in d1 and ddst in d1: |
203 # directory wasn't entirely moved locally | 203 # directory wasn't entirely moved locally |
234 repo.ui.debug(_(" file %s -> %s\n") % (f, copy[f])) | 234 repo.ui.debug(_(" file %s -> %s\n") % (f, copy[f])) |
235 break | 235 break |
236 | 236 |
237 return copy, diverge | 237 return copy, diverge |
238 | 238 |
239 def symmetricdifference(repo, rev1, rev2): | 239 def _symmetricdifference(repo, rev1, rev2): |
240 """symmetric difference of the sets of ancestors of rev1 and rev2 | 240 """symmetric difference of the sets of ancestors of rev1 and rev2 |
241 | 241 |
242 I.e. revisions that are ancestors of rev1 or rev2, but not both. | 242 I.e. revisions that are ancestors of rev1 or rev2, but not both. |
243 """ | 243 """ |
244 # basic idea: | 244 # basic idea: |
338 if not (backwards or overwrite): | 338 if not (backwards or overwrite): |
339 rev1 = p1.rev() | 339 rev1 = p1.rev() |
340 if rev1 is None: | 340 if rev1 is None: |
341 # p1 is a workingctx | 341 # p1 is a workingctx |
342 rev1 = p1.parents()[0].rev() | 342 rev1 = p1.parents()[0].rev() |
343 limit = min(symmetricdifference(repo, rev1, p2.rev())) | 343 limit = min(_symmetricdifference(repo, rev1, p2.rev())) |
344 copy, diverge = findcopies(repo, m1, m2, ma, limit) | 344 copy, diverge = findcopies(repo, m1, m2, ma, limit) |
345 | 345 |
346 for of, fl in diverge.items(): | 346 for of, fl in diverge.items(): |
347 act("divergent renames", "dr", of, fl) | 347 act("divergent renames", "dr", of, fl) |
348 | 348 |
613 raise util.Abort(_("outstanding uncommitted changes")) | 613 raise util.Abort(_("outstanding uncommitted changes")) |
614 | 614 |
615 ### calculate phase | 615 ### calculate phase |
616 action = [] | 616 action = [] |
617 if not force: | 617 if not force: |
618 checkunknown(wc, p2) | 618 _checkunknown(wc, p2) |
619 if not util.checkfolding(repo.path): | 619 if not util.checkfolding(repo.path): |
620 checkcollision(p2) | 620 _checkcollision(p2) |
621 action += forgetremoved(wc, p2, branchmerge) | 621 action += _forgetremoved(wc, p2, branchmerge) |
622 action += manifestmerge(repo, wc, p2, pa, overwrite, partial) | 622 action += manifestmerge(repo, wc, p2, pa, overwrite, partial) |
623 | 623 |
624 ### apply phase | 624 ### apply phase |
625 if not branchmerge: # just jump to the new rev | 625 if not branchmerge: # just jump to the new rev |
626 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, '' | 626 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, '' |