##// END OF EJS Templates
icasefs: rewrite case-folding collision detection (issue3452)...
FUJIWARA Katsunori -
r19105:c60a7f5a stable
parent child Browse files
Show More
@@ -1,752 +1,760 b''
1 # merge.py - directory-level update/merge handling for Mercurial
1 # merge.py - directory-level update/merge handling for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import nullid, nullrev, hex, bin
8 from node import nullid, nullrev, hex, bin
9 from i18n import _
9 from i18n import _
10 from mercurial import obsolete
10 from mercurial import obsolete
11 import error, util, filemerge, copies, subrepo, worker, dicthelpers
11 import error, util, filemerge, copies, subrepo, worker, dicthelpers
12 import errno, os, shutil
12 import errno, os, shutil
13
13
14 class mergestate(object):
14 class mergestate(object):
15 '''track 3-way merge state of individual files'''
15 '''track 3-way merge state of individual files'''
16 def __init__(self, repo):
16 def __init__(self, repo):
17 self._repo = repo
17 self._repo = repo
18 self._dirty = False
18 self._dirty = False
19 self._read()
19 self._read()
20 def reset(self, node=None):
20 def reset(self, node=None):
21 self._state = {}
21 self._state = {}
22 if node:
22 if node:
23 self._local = node
23 self._local = node
24 shutil.rmtree(self._repo.join("merge"), True)
24 shutil.rmtree(self._repo.join("merge"), True)
25 self._dirty = False
25 self._dirty = False
26 def _read(self):
26 def _read(self):
27 self._state = {}
27 self._state = {}
28 try:
28 try:
29 f = self._repo.opener("merge/state")
29 f = self._repo.opener("merge/state")
30 for i, l in enumerate(f):
30 for i, l in enumerate(f):
31 if i == 0:
31 if i == 0:
32 self._local = bin(l[:-1])
32 self._local = bin(l[:-1])
33 else:
33 else:
34 bits = l[:-1].split("\0")
34 bits = l[:-1].split("\0")
35 self._state[bits[0]] = bits[1:]
35 self._state[bits[0]] = bits[1:]
36 f.close()
36 f.close()
37 except IOError, err:
37 except IOError, err:
38 if err.errno != errno.ENOENT:
38 if err.errno != errno.ENOENT:
39 raise
39 raise
40 self._dirty = False
40 self._dirty = False
41 def commit(self):
41 def commit(self):
42 if self._dirty:
42 if self._dirty:
43 f = self._repo.opener("merge/state", "w")
43 f = self._repo.opener("merge/state", "w")
44 f.write(hex(self._local) + "\n")
44 f.write(hex(self._local) + "\n")
45 for d, v in self._state.iteritems():
45 for d, v in self._state.iteritems():
46 f.write("\0".join([d] + v) + "\n")
46 f.write("\0".join([d] + v) + "\n")
47 f.close()
47 f.close()
48 self._dirty = False
48 self._dirty = False
49 def add(self, fcl, fco, fca, fd):
49 def add(self, fcl, fco, fca, fd):
50 hash = util.sha1(fcl.path()).hexdigest()
50 hash = util.sha1(fcl.path()).hexdigest()
51 self._repo.opener.write("merge/" + hash, fcl.data())
51 self._repo.opener.write("merge/" + hash, fcl.data())
52 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
52 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
53 hex(fca.filenode()), fco.path(), fcl.flags()]
53 hex(fca.filenode()), fco.path(), fcl.flags()]
54 self._dirty = True
54 self._dirty = True
55 def __contains__(self, dfile):
55 def __contains__(self, dfile):
56 return dfile in self._state
56 return dfile in self._state
57 def __getitem__(self, dfile):
57 def __getitem__(self, dfile):
58 return self._state[dfile][0]
58 return self._state[dfile][0]
59 def __iter__(self):
59 def __iter__(self):
60 l = self._state.keys()
60 l = self._state.keys()
61 l.sort()
61 l.sort()
62 for f in l:
62 for f in l:
63 yield f
63 yield f
64 def mark(self, dfile, state):
64 def mark(self, dfile, state):
65 self._state[dfile][0] = state
65 self._state[dfile][0] = state
66 self._dirty = True
66 self._dirty = True
67 def resolve(self, dfile, wctx, octx):
67 def resolve(self, dfile, wctx, octx):
68 if self[dfile] == 'r':
68 if self[dfile] == 'r':
69 return 0
69 return 0
70 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
70 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
71 fcd = wctx[dfile]
71 fcd = wctx[dfile]
72 fco = octx[ofile]
72 fco = octx[ofile]
73 fca = self._repo.filectx(afile, fileid=anode)
73 fca = self._repo.filectx(afile, fileid=anode)
74 # "premerge" x flags
74 # "premerge" x flags
75 flo = fco.flags()
75 flo = fco.flags()
76 fla = fca.flags()
76 fla = fca.flags()
77 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
77 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
78 if fca.node() == nullid:
78 if fca.node() == nullid:
79 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
79 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
80 afile)
80 afile)
81 elif flags == fla:
81 elif flags == fla:
82 flags = flo
82 flags = flo
83 # restore local
83 # restore local
84 f = self._repo.opener("merge/" + hash)
84 f = self._repo.opener("merge/" + hash)
85 self._repo.wwrite(dfile, f.read(), flags)
85 self._repo.wwrite(dfile, f.read(), flags)
86 f.close()
86 f.close()
87 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
87 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
88 if r is None:
88 if r is None:
89 # no real conflict
89 # no real conflict
90 del self._state[dfile]
90 del self._state[dfile]
91 elif not r:
91 elif not r:
92 self.mark(dfile, 'r')
92 self.mark(dfile, 'r')
93 return r
93 return r
94
94
95 def _checkunknownfile(repo, wctx, mctx, f):
95 def _checkunknownfile(repo, wctx, mctx, f):
96 return (not repo.dirstate._ignore(f)
96 return (not repo.dirstate._ignore(f)
97 and os.path.isfile(repo.wjoin(f))
97 and os.path.isfile(repo.wjoin(f))
98 and repo.dirstate.normalize(f) not in repo.dirstate
98 and repo.dirstate.normalize(f) not in repo.dirstate
99 and mctx[f].cmp(wctx[f]))
99 and mctx[f].cmp(wctx[f]))
100
100
101 def _checkunknown(repo, wctx, mctx):
101 def _checkunknown(repo, wctx, mctx):
102 "check for collisions between unknown files and files in mctx"
102 "check for collisions between unknown files and files in mctx"
103
103
104 error = False
104 error = False
105 for f in mctx:
105 for f in mctx:
106 if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
106 if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
107 error = True
107 error = True
108 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
108 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
109 if error:
109 if error:
110 raise util.Abort(_("untracked files in working directory differ "
110 raise util.Abort(_("untracked files in working directory differ "
111 "from files in requested revision"))
111 "from files in requested revision"))
112
112
113 def _remains(f, m, ma, workingctx=False):
114 """check whether specified file remains after merge.
115
116 It is assumed that specified file is not contained in the manifest
117 of the other context.
118 """
119 if f in ma:
120 n = m[f]
121 if n != ma[f]:
122 return True # because it is changed locally
123 # even though it doesn't remain, if "remote deleted" is
124 # chosen in manifestmerge()
125 elif workingctx and n[20:] == "a":
126 return True # because it is added locally (linear merge specific)
127 else:
128 return False # because it is removed remotely
129 else:
130 return True # because it is added locally
131
132 def _checkcollision(mctx, extractxs):
133 "check for case folding collisions in the destination context"
134 folded = {}
135 for fn in mctx:
136 fold = util.normcase(fn)
137 if fold in folded:
138 raise util.Abort(_("case-folding collision between %s and %s")
139 % (fn, folded[fold]))
140 folded[fold] = fn
141
142 if extractxs:
143 wctx, actx = extractxs
144 # class to delay looking up copy mapping
145 class pathcopies(object):
146 @util.propertycache
147 def map(self):
148 # {dst@mctx: src@wctx} copy mapping
149 return copies.pathcopies(wctx, mctx)
150 pc = pathcopies()
151
152 for fn in wctx:
153 fold = util.normcase(fn)
154 mfn = folded.get(fold, None)
155 if (mfn and mfn != fn and pc.map.get(mfn) != fn and
156 _remains(fn, wctx.manifest(), actx.manifest(), True) and
157 _remains(mfn, mctx.manifest(), actx.manifest())):
158 raise util.Abort(_("case-folding collision between %s and %s")
159 % (mfn, fn))
160
161 def _forgetremoved(wctx, mctx, branchmerge):
113 def _forgetremoved(wctx, mctx, branchmerge):
162 """
114 """
163 Forget removed files
115 Forget removed files
164
116
165 If we're jumping between revisions (as opposed to merging), and if
117 If we're jumping between revisions (as opposed to merging), and if
166 neither the working directory nor the target rev has the file,
118 neither the working directory nor the target rev has the file,
167 then we need to remove it from the dirstate, to prevent the
119 then we need to remove it from the dirstate, to prevent the
168 dirstate from listing the file when it is no longer in the
120 dirstate from listing the file when it is no longer in the
169 manifest.
121 manifest.
170
122
171 If we're merging, and the other revision has removed a file
123 If we're merging, and the other revision has removed a file
172 that is not present in the working directory, we need to mark it
124 that is not present in the working directory, we need to mark it
173 as removed.
125 as removed.
174 """
126 """
175
127
176 actions = []
128 actions = []
177 state = branchmerge and 'r' or 'f'
129 state = branchmerge and 'r' or 'f'
178 for f in wctx.deleted():
130 for f in wctx.deleted():
179 if f not in mctx:
131 if f not in mctx:
180 actions.append((f, state, None, "forget deleted"))
132 actions.append((f, state, None, "forget deleted"))
181
133
182 if not branchmerge:
134 if not branchmerge:
183 for f in wctx.removed():
135 for f in wctx.removed():
184 if f not in mctx:
136 if f not in mctx:
185 actions.append((f, "f", None, "forget removed"))
137 actions.append((f, "f", None, "forget removed"))
186
138
187 return actions
139 return actions
188
140
141 def _checkcollision(repo, wmf, actions, prompts):
142 # build provisional merged manifest up
143 pmmf = set(wmf)
144
145 def addop(f, args):
146 pmmf.add(f)
147 def removeop(f, args):
148 pmmf.discard(f)
149 def nop(f, args):
150 pass
151
152 def renameop(f, args):
153 f2, fd, flags = args
154 if f:
155 pmmf.discard(f)
156 pmmf.add(fd)
157 def mergeop(f, args):
158 f2, fd, move = args
159 if move:
160 pmmf.discard(f)
161 pmmf.add(fd)
162
163 opmap = {
164 "a": addop,
165 "d": renameop,
166 "dr": nop,
167 "e": nop,
168 "f": addop, # untracked file should be kept in working directory
169 "g": addop,
170 "m": mergeop,
171 "r": removeop,
172 "rd": nop,
173 }
174 for f, m, args, msg in actions:
175 op = opmap.get(m)
176 assert op, m
177 op(f, args)
178
179 opmap = {
180 "cd": addop,
181 "dc": addop,
182 }
183 for f, m in prompts:
184 op = opmap.get(m)
185 assert op, m
186 op(f, None)
187
188 # check case-folding collision in provisional merged manifest
189 foldmap = {}
190 for f in sorted(pmmf):
191 fold = util.normcase(f)
192 if fold in foldmap:
193 raise util.Abort(_("case-folding collision between %s and %s")
194 % (f, foldmap[fold]))
195 foldmap[fold] = f
196
189 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
197 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
190 acceptremote=False):
198 acceptremote=False):
191 """
199 """
192 Merge p1 and p2 with ancestor pa and generate merge action list
200 Merge p1 and p2 with ancestor pa and generate merge action list
193
201
194 branchmerge and force are as passed in to update
202 branchmerge and force are as passed in to update
195 partial = function to filter file lists
203 partial = function to filter file lists
196 acceptremote = accept the incoming changes without prompting
204 acceptremote = accept the incoming changes without prompting
197 """
205 """
198
206
199 overwrite = force and not branchmerge
207 overwrite = force and not branchmerge
200 actions, copy, movewithdir = [], {}, {}
208 actions, copy, movewithdir = [], {}, {}
201
209
202 followcopies = False
210 followcopies = False
203 if overwrite:
211 if overwrite:
204 pa = wctx
212 pa = wctx
205 elif pa == p2: # backwards
213 elif pa == p2: # backwards
206 pa = wctx.p1()
214 pa = wctx.p1()
207 elif not branchmerge and not wctx.dirty(missing=True):
215 elif not branchmerge and not wctx.dirty(missing=True):
208 pass
216 pass
209 elif pa and repo.ui.configbool("merge", "followcopies", True):
217 elif pa and repo.ui.configbool("merge", "followcopies", True):
210 followcopies = True
218 followcopies = True
211
219
212 # manifests fetched in order are going to be faster, so prime the caches
220 # manifests fetched in order are going to be faster, so prime the caches
213 [x.manifest() for x in
221 [x.manifest() for x in
214 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
222 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
215
223
216 if followcopies:
224 if followcopies:
217 ret = copies.mergecopies(repo, wctx, p2, pa)
225 ret = copies.mergecopies(repo, wctx, p2, pa)
218 copy, movewithdir, diverge, renamedelete = ret
226 copy, movewithdir, diverge, renamedelete = ret
219 for of, fl in diverge.iteritems():
227 for of, fl in diverge.iteritems():
220 actions.append((of, "dr", (fl,), "divergent renames"))
228 actions.append((of, "dr", (fl,), "divergent renames"))
221 for of, fl in renamedelete.iteritems():
229 for of, fl in renamedelete.iteritems():
222 actions.append((of, "rd", (fl,), "rename and delete"))
230 actions.append((of, "rd", (fl,), "rename and delete"))
223
231
224 repo.ui.note(_("resolving manifests\n"))
232 repo.ui.note(_("resolving manifests\n"))
225 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
233 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
226 % (bool(branchmerge), bool(force), bool(partial)))
234 % (bool(branchmerge), bool(force), bool(partial)))
227 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
235 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
228
236
229 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
237 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
230 copied = set(copy.values())
238 copied = set(copy.values())
231 copied.update(movewithdir.values())
239 copied.update(movewithdir.values())
232
240
233 if '.hgsubstate' in m1:
241 if '.hgsubstate' in m1:
234 # check whether sub state is modified
242 # check whether sub state is modified
235 for s in sorted(wctx.substate):
243 for s in sorted(wctx.substate):
236 if wctx.sub(s).dirty():
244 if wctx.sub(s).dirty():
237 m1['.hgsubstate'] += "+"
245 m1['.hgsubstate'] += "+"
238 break
246 break
239
247
240 aborts, prompts = [], []
248 aborts, prompts = [], []
241 # Compare manifests
249 # Compare manifests
242 fdiff = dicthelpers.diff(m1, m2)
250 fdiff = dicthelpers.diff(m1, m2)
243 flagsdiff = m1.flagsdiff(m2)
251 flagsdiff = m1.flagsdiff(m2)
244 diff12 = dicthelpers.join(fdiff, flagsdiff)
252 diff12 = dicthelpers.join(fdiff, flagsdiff)
245
253
246 for f, (n12, fl12) in diff12.iteritems():
254 for f, (n12, fl12) in diff12.iteritems():
247 if n12:
255 if n12:
248 n1, n2 = n12
256 n1, n2 = n12
249 else: # file contents didn't change, but flags did
257 else: # file contents didn't change, but flags did
250 n1 = n2 = m1.get(f, None)
258 n1 = n2 = m1.get(f, None)
251 if n1 is None:
259 if n1 is None:
252 # Since n1 == n2, the file isn't present in m2 either. This
260 # Since n1 == n2, the file isn't present in m2 either. This
253 # means that the file was removed or deleted locally and
261 # means that the file was removed or deleted locally and
254 # removed remotely, but that residual entries remain in flags.
262 # removed remotely, but that residual entries remain in flags.
255 # This can happen in manifests generated by workingctx.
263 # This can happen in manifests generated by workingctx.
256 continue
264 continue
257 if fl12:
265 if fl12:
258 fl1, fl2 = fl12
266 fl1, fl2 = fl12
259 else: # flags didn't change, file contents did
267 else: # flags didn't change, file contents did
260 fl1 = fl2 = m1.flags(f)
268 fl1 = fl2 = m1.flags(f)
261
269
262 if partial and not partial(f):
270 if partial and not partial(f):
263 continue
271 continue
264 if n1 and n2:
272 if n1 and n2:
265 fla = ma.flags(f)
273 fla = ma.flags(f)
266 nol = 'l' not in fl1 + fl2 + fla
274 nol = 'l' not in fl1 + fl2 + fla
267 a = ma.get(f, nullid)
275 a = ma.get(f, nullid)
268 if n2 == a and fl2 == fla:
276 if n2 == a and fl2 == fla:
269 pass # remote unchanged - keep local
277 pass # remote unchanged - keep local
270 elif n1 == a and fl1 == fla: # local unchanged - use remote
278 elif n1 == a and fl1 == fla: # local unchanged - use remote
271 if n1 == n2: # optimization: keep local content
279 if n1 == n2: # optimization: keep local content
272 actions.append((f, "e", (fl2,), "update permissions"))
280 actions.append((f, "e", (fl2,), "update permissions"))
273 else:
281 else:
274 actions.append((f, "g", (fl2,), "remote is newer"))
282 actions.append((f, "g", (fl2,), "remote is newer"))
275 elif nol and n2 == a: # remote only changed 'x'
283 elif nol and n2 == a: # remote only changed 'x'
276 actions.append((f, "e", (fl2,), "update permissions"))
284 actions.append((f, "e", (fl2,), "update permissions"))
277 elif nol and n1 == a: # local only changed 'x'
285 elif nol and n1 == a: # local only changed 'x'
278 actions.append((f, "g", (fl1,), "remote is newer"))
286 actions.append((f, "g", (fl1,), "remote is newer"))
279 else: # both changed something
287 else: # both changed something
280 actions.append((f, "m", (f, f, False), "versions differ"))
288 actions.append((f, "m", (f, f, False), "versions differ"))
281 elif f in copied: # files we'll deal with on m2 side
289 elif f in copied: # files we'll deal with on m2 side
282 pass
290 pass
283 elif n1 and f in movewithdir: # directory rename
291 elif n1 and f in movewithdir: # directory rename
284 f2 = movewithdir[f]
292 f2 = movewithdir[f]
285 actions.append((f, "d", (None, f2, fl1),
293 actions.append((f, "d", (None, f2, fl1),
286 "remote renamed directory to " + f2))
294 "remote renamed directory to " + f2))
287 elif n1 and f in copy:
295 elif n1 and f in copy:
288 f2 = copy[f]
296 f2 = copy[f]
289 actions.append((f, "m", (f2, f, False),
297 actions.append((f, "m", (f2, f, False),
290 "local copied/moved to " + f2))
298 "local copied/moved to " + f2))
291 elif n1 and f in ma: # clean, a different, no remote
299 elif n1 and f in ma: # clean, a different, no remote
292 if n1 != ma[f]:
300 if n1 != ma[f]:
293 prompts.append((f, "cd")) # prompt changed/deleted
301 prompts.append((f, "cd")) # prompt changed/deleted
294 elif n1[20:] == "a": # added, no remote
302 elif n1[20:] == "a": # added, no remote
295 actions.append((f, "f", None, "remote deleted"))
303 actions.append((f, "f", None, "remote deleted"))
296 else:
304 else:
297 actions.append((f, "r", None, "other deleted"))
305 actions.append((f, "r", None, "other deleted"))
298 elif n2 and f in movewithdir:
306 elif n2 and f in movewithdir:
299 f2 = movewithdir[f]
307 f2 = movewithdir[f]
300 actions.append((None, "d", (f, f2, fl2),
308 actions.append((None, "d", (f, f2, fl2),
301 "local renamed directory to " + f2))
309 "local renamed directory to " + f2))
302 elif n2 and f in copy:
310 elif n2 and f in copy:
303 f2 = copy[f]
311 f2 = copy[f]
304 if f2 in m2:
312 if f2 in m2:
305 actions.append((f2, "m", (f, f, False),
313 actions.append((f2, "m", (f, f, False),
306 "remote copied to " + f))
314 "remote copied to " + f))
307 else:
315 else:
308 actions.append((f2, "m", (f, f, True),
316 actions.append((f2, "m", (f, f, True),
309 "remote moved to " + f))
317 "remote moved to " + f))
310 elif n2 and f not in ma:
318 elif n2 and f not in ma:
311 # local unknown, remote created: the logic is described by the
319 # local unknown, remote created: the logic is described by the
312 # following table:
320 # following table:
313 #
321 #
314 # force branchmerge different | action
322 # force branchmerge different | action
315 # n * n | get
323 # n * n | get
316 # n * y | abort
324 # n * y | abort
317 # y n * | get
325 # y n * | get
318 # y y n | get
326 # y y n | get
319 # y y y | merge
327 # y y y | merge
320 #
328 #
321 # Checking whether the files are different is expensive, so we
329 # Checking whether the files are different is expensive, so we
322 # don't do that when we can avoid it.
330 # don't do that when we can avoid it.
323 if force and not branchmerge:
331 if force and not branchmerge:
324 actions.append((f, "g", (fl2,), "remote created"))
332 actions.append((f, "g", (fl2,), "remote created"))
325 else:
333 else:
326 different = _checkunknownfile(repo, wctx, p2, f)
334 different = _checkunknownfile(repo, wctx, p2, f)
327 if force and branchmerge and different:
335 if force and branchmerge and different:
328 actions.append((f, "m", (f, f, False),
336 actions.append((f, "m", (f, f, False),
329 "remote differs from untracked local"))
337 "remote differs from untracked local"))
330 elif not force and different:
338 elif not force and different:
331 aborts.append((f, "ud"))
339 aborts.append((f, "ud"))
332 else:
340 else:
333 actions.append((f, "g", (fl2,), "remote created"))
341 actions.append((f, "g", (fl2,), "remote created"))
334 elif n2 and n2 != ma[f]:
342 elif n2 and n2 != ma[f]:
335 prompts.append((f, "dc")) # prompt deleted/changed
343 prompts.append((f, "dc")) # prompt deleted/changed
336
344
337 for f, m in sorted(aborts):
345 for f, m in sorted(aborts):
338 if m == "ud":
346 if m == "ud":
339 repo.ui.warn(_("%s: untracked file differs\n") % f)
347 repo.ui.warn(_("%s: untracked file differs\n") % f)
340 else: assert False, m
348 else: assert False, m
341 if aborts:
349 if aborts:
342 raise util.Abort(_("untracked files in working directory differ "
350 raise util.Abort(_("untracked files in working directory differ "
343 "from files in requested revision"))
351 "from files in requested revision"))
344
352
353 if not util.checkcase(repo.path):
354 # check collision between files only in p2 for clean update
355 if (not branchmerge and
356 (force or not wctx.dirty(missing=True, branch=False))):
357 _checkcollision(repo, m2, [], [])
358 else:
359 _checkcollision(repo, m1, actions, prompts)
360
345 for f, m in sorted(prompts):
361 for f, m in sorted(prompts):
346 if m == "cd":
362 if m == "cd":
347 if acceptremote:
363 if acceptremote:
348 actions.append((f, "r", None, "remote delete"))
364 actions.append((f, "r", None, "remote delete"))
349 elif repo.ui.promptchoice(
365 elif repo.ui.promptchoice(
350 _("local changed %s which remote deleted\n"
366 _("local changed %s which remote deleted\n"
351 "use (c)hanged version or (d)elete?") % f,
367 "use (c)hanged version or (d)elete?") % f,
352 (_("&Changed"), _("&Delete")), 0):
368 (_("&Changed"), _("&Delete")), 0):
353 actions.append((f, "r", None, "prompt delete"))
369 actions.append((f, "r", None, "prompt delete"))
354 else:
370 else:
355 actions.append((f, "a", None, "prompt keep"))
371 actions.append((f, "a", None, "prompt keep"))
356 elif m == "dc":
372 elif m == "dc":
357 if acceptremote:
373 if acceptremote:
358 actions.append((f, "g", (m2.flags(f),), "remote recreating"))
374 actions.append((f, "g", (m2.flags(f),), "remote recreating"))
359 elif repo.ui.promptchoice(
375 elif repo.ui.promptchoice(
360 _("remote changed %s which local deleted\n"
376 _("remote changed %s which local deleted\n"
361 "use (c)hanged version or leave (d)eleted?") % f,
377 "use (c)hanged version or leave (d)eleted?") % f,
362 (_("&Changed"), _("&Deleted")), 0) == 0:
378 (_("&Changed"), _("&Deleted")), 0) == 0:
363 actions.append((f, "g", (m2.flags(f),), "prompt recreating"))
379 actions.append((f, "g", (m2.flags(f),), "prompt recreating"))
364 else: assert False, m
380 else: assert False, m
365 return actions
381 return actions
366
382
367 def actionkey(a):
383 def actionkey(a):
368 return a[1] == "r" and -1 or 0, a
384 return a[1] == "r" and -1 or 0, a
369
385
370 def getremove(repo, mctx, overwrite, args):
386 def getremove(repo, mctx, overwrite, args):
371 """apply usually-non-interactive updates to the working directory
387 """apply usually-non-interactive updates to the working directory
372
388
373 mctx is the context to be merged into the working copy
389 mctx is the context to be merged into the working copy
374
390
375 yields tuples for progress updates
391 yields tuples for progress updates
376 """
392 """
377 verbose = repo.ui.verbose
393 verbose = repo.ui.verbose
378 unlink = util.unlinkpath
394 unlink = util.unlinkpath
379 wjoin = repo.wjoin
395 wjoin = repo.wjoin
380 fctx = mctx.filectx
396 fctx = mctx.filectx
381 wwrite = repo.wwrite
397 wwrite = repo.wwrite
382 audit = repo.wopener.audit
398 audit = repo.wopener.audit
383 i = 0
399 i = 0
384 for arg in args:
400 for arg in args:
385 f = arg[0]
401 f = arg[0]
386 if arg[1] == 'r':
402 if arg[1] == 'r':
387 if verbose:
403 if verbose:
388 repo.ui.note(_("removing %s\n") % f)
404 repo.ui.note(_("removing %s\n") % f)
389 audit(f)
405 audit(f)
390 try:
406 try:
391 unlink(wjoin(f), ignoremissing=True)
407 unlink(wjoin(f), ignoremissing=True)
392 except OSError, inst:
408 except OSError, inst:
393 repo.ui.warn(_("update failed to remove %s: %s!\n") %
409 repo.ui.warn(_("update failed to remove %s: %s!\n") %
394 (f, inst.strerror))
410 (f, inst.strerror))
395 else:
411 else:
396 if verbose:
412 if verbose:
397 repo.ui.note(_("getting %s\n") % f)
413 repo.ui.note(_("getting %s\n") % f)
398 wwrite(f, fctx(f).data(), arg[2][0])
414 wwrite(f, fctx(f).data(), arg[2][0])
399 if i == 100:
415 if i == 100:
400 yield i, f
416 yield i, f
401 i = 0
417 i = 0
402 i += 1
418 i += 1
403 if i > 0:
419 if i > 0:
404 yield i, f
420 yield i, f
405
421
406 def applyupdates(repo, actions, wctx, mctx, actx, overwrite):
422 def applyupdates(repo, actions, wctx, mctx, actx, overwrite):
407 """apply the merge action list to the working directory
423 """apply the merge action list to the working directory
408
424
409 wctx is the working copy context
425 wctx is the working copy context
410 mctx is the context to be merged into the working copy
426 mctx is the context to be merged into the working copy
411 actx is the context of the common ancestor
427 actx is the context of the common ancestor
412
428
413 Return a tuple of counts (updated, merged, removed, unresolved) that
429 Return a tuple of counts (updated, merged, removed, unresolved) that
414 describes how many files were affected by the update.
430 describes how many files were affected by the update.
415 """
431 """
416
432
417 updated, merged, removed, unresolved = 0, 0, 0, 0
433 updated, merged, removed, unresolved = 0, 0, 0, 0
418 ms = mergestate(repo)
434 ms = mergestate(repo)
419 ms.reset(wctx.p1().node())
435 ms.reset(wctx.p1().node())
420 moves = []
436 moves = []
421 actions.sort(key=actionkey)
437 actions.sort(key=actionkey)
422
438
423 # prescan for merges
439 # prescan for merges
424 for a in actions:
440 for a in actions:
425 f, m, args, msg = a
441 f, m, args, msg = a
426 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
442 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
427 if m == "m": # merge
443 if m == "m": # merge
428 f2, fd, move = args
444 f2, fd, move = args
429 if fd == '.hgsubstate': # merged internally
445 if fd == '.hgsubstate': # merged internally
430 continue
446 continue
431 repo.ui.debug(" preserving %s for resolve of %s\n" % (f, fd))
447 repo.ui.debug(" preserving %s for resolve of %s\n" % (f, fd))
432 fcl = wctx[f]
448 fcl = wctx[f]
433 fco = mctx[f2]
449 fco = mctx[f2]
434 if mctx == actx: # backwards, use working dir parent as ancestor
450 if mctx == actx: # backwards, use working dir parent as ancestor
435 if fcl.parents():
451 if fcl.parents():
436 fca = fcl.p1()
452 fca = fcl.p1()
437 else:
453 else:
438 fca = repo.filectx(f, fileid=nullrev)
454 fca = repo.filectx(f, fileid=nullrev)
439 else:
455 else:
440 fca = fcl.ancestor(fco, actx)
456 fca = fcl.ancestor(fco, actx)
441 if not fca:
457 if not fca:
442 fca = repo.filectx(f, fileid=nullrev)
458 fca = repo.filectx(f, fileid=nullrev)
443 ms.add(fcl, fco, fca, fd)
459 ms.add(fcl, fco, fca, fd)
444 if f != fd and move:
460 if f != fd and move:
445 moves.append(f)
461 moves.append(f)
446
462
447 audit = repo.wopener.audit
463 audit = repo.wopener.audit
448
464
449 # remove renamed files after safely stored
465 # remove renamed files after safely stored
450 for f in moves:
466 for f in moves:
451 if os.path.lexists(repo.wjoin(f)):
467 if os.path.lexists(repo.wjoin(f)):
452 repo.ui.debug("removing %s\n" % f)
468 repo.ui.debug("removing %s\n" % f)
453 audit(f)
469 audit(f)
454 util.unlinkpath(repo.wjoin(f))
470 util.unlinkpath(repo.wjoin(f))
455
471
456 numupdates = len(actions)
472 numupdates = len(actions)
457 workeractions = [a for a in actions if a[1] in 'gr']
473 workeractions = [a for a in actions if a[1] in 'gr']
458 updateactions = [a for a in workeractions if a[1] == 'g']
474 updateactions = [a for a in workeractions if a[1] == 'g']
459 updated = len(updateactions)
475 updated = len(updateactions)
460 removeactions = [a for a in workeractions if a[1] == 'r']
476 removeactions = [a for a in workeractions if a[1] == 'r']
461 removed = len(removeactions)
477 removed = len(removeactions)
462 actions = [a for a in actions if a[1] not in 'gr']
478 actions = [a for a in actions if a[1] not in 'gr']
463
479
464 hgsub = [a[1] for a in workeractions if a[0] == '.hgsubstate']
480 hgsub = [a[1] for a in workeractions if a[0] == '.hgsubstate']
465 if hgsub and hgsub[0] == 'r':
481 if hgsub and hgsub[0] == 'r':
466 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
482 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
467
483
468 z = 0
484 z = 0
469 prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite),
485 prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite),
470 removeactions)
486 removeactions)
471 for i, item in prog:
487 for i, item in prog:
472 z += i
488 z += i
473 repo.ui.progress(_('updating'), z, item=item, total=numupdates,
489 repo.ui.progress(_('updating'), z, item=item, total=numupdates,
474 unit=_('files'))
490 unit=_('files'))
475 prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite),
491 prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite),
476 updateactions)
492 updateactions)
477 for i, item in prog:
493 for i, item in prog:
478 z += i
494 z += i
479 repo.ui.progress(_('updating'), z, item=item, total=numupdates,
495 repo.ui.progress(_('updating'), z, item=item, total=numupdates,
480 unit=_('files'))
496 unit=_('files'))
481
497
482 if hgsub and hgsub[0] == 'g':
498 if hgsub and hgsub[0] == 'g':
483 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
499 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
484
500
485 _updating = _('updating')
501 _updating = _('updating')
486 _files = _('files')
502 _files = _('files')
487 progress = repo.ui.progress
503 progress = repo.ui.progress
488
504
489 for i, a in enumerate(actions):
505 for i, a in enumerate(actions):
490 f, m, args, msg = a
506 f, m, args, msg = a
491 progress(_updating, z + i + 1, item=f, total=numupdates, unit=_files)
507 progress(_updating, z + i + 1, item=f, total=numupdates, unit=_files)
492 if m == "m": # merge
508 if m == "m": # merge
493 f2, fd, move = args
509 f2, fd, move = args
494 if fd == '.hgsubstate': # subrepo states need updating
510 if fd == '.hgsubstate': # subrepo states need updating
495 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
511 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
496 overwrite)
512 overwrite)
497 continue
513 continue
498 audit(fd)
514 audit(fd)
499 r = ms.resolve(fd, wctx, mctx)
515 r = ms.resolve(fd, wctx, mctx)
500 if r is not None and r > 0:
516 if r is not None and r > 0:
501 unresolved += 1
517 unresolved += 1
502 else:
518 else:
503 if r is None:
519 if r is None:
504 updated += 1
520 updated += 1
505 else:
521 else:
506 merged += 1
522 merged += 1
507 elif m == "d": # directory rename
523 elif m == "d": # directory rename
508 f2, fd, flags = args
524 f2, fd, flags = args
509 if f:
525 if f:
510 repo.ui.note(_("moving %s to %s\n") % (f, fd))
526 repo.ui.note(_("moving %s to %s\n") % (f, fd))
511 audit(f)
527 audit(f)
512 repo.wwrite(fd, wctx.filectx(f).data(), flags)
528 repo.wwrite(fd, wctx.filectx(f).data(), flags)
513 util.unlinkpath(repo.wjoin(f))
529 util.unlinkpath(repo.wjoin(f))
514 if f2:
530 if f2:
515 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
531 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
516 repo.wwrite(fd, mctx.filectx(f2).data(), flags)
532 repo.wwrite(fd, mctx.filectx(f2).data(), flags)
517 updated += 1
533 updated += 1
518 elif m == "dr": # divergent renames
534 elif m == "dr": # divergent renames
519 fl, = args
535 fl, = args
520 repo.ui.warn(_("note: possible conflict - %s was renamed "
536 repo.ui.warn(_("note: possible conflict - %s was renamed "
521 "multiple times to:\n") % f)
537 "multiple times to:\n") % f)
522 for nf in fl:
538 for nf in fl:
523 repo.ui.warn(" %s\n" % nf)
539 repo.ui.warn(" %s\n" % nf)
524 elif m == "rd": # rename and delete
540 elif m == "rd": # rename and delete
525 fl, = args
541 fl, = args
526 repo.ui.warn(_("note: possible conflict - %s was deleted "
542 repo.ui.warn(_("note: possible conflict - %s was deleted "
527 "and renamed to:\n") % f)
543 "and renamed to:\n") % f)
528 for nf in fl:
544 for nf in fl:
529 repo.ui.warn(" %s\n" % nf)
545 repo.ui.warn(" %s\n" % nf)
530 elif m == "e": # exec
546 elif m == "e": # exec
531 flags, = args
547 flags, = args
532 audit(f)
548 audit(f)
533 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
549 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
534 updated += 1
550 updated += 1
535 ms.commit()
551 ms.commit()
536 progress(_updating, None, total=numupdates, unit=_files)
552 progress(_updating, None, total=numupdates, unit=_files)
537
553
538 return updated, merged, removed, unresolved
554 return updated, merged, removed, unresolved
539
555
540 def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial,
556 def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial,
541 acceptremote=False):
557 acceptremote=False):
542 "Calculate the actions needed to merge mctx into tctx"
558 "Calculate the actions needed to merge mctx into tctx"
543 actions = []
559 actions = []
544 folding = not util.checkcase(repo.path)
545 if folding:
546 # collision check is not needed for clean update
547 if (not branchmerge and
548 (force or not tctx.dirty(missing=True, branch=False))):
549 _checkcollision(mctx, None)
550 else:
551 _checkcollision(mctx, (tctx, ancestor))
552 actions += manifestmerge(repo, tctx, mctx,
560 actions += manifestmerge(repo, tctx, mctx,
553 ancestor,
561 ancestor,
554 branchmerge, force,
562 branchmerge, force,
555 partial, acceptremote)
563 partial, acceptremote)
556 if tctx.rev() is None:
564 if tctx.rev() is None:
557 actions += _forgetremoved(tctx, mctx, branchmerge)
565 actions += _forgetremoved(tctx, mctx, branchmerge)
558 return actions
566 return actions
559
567
560 def recordupdates(repo, actions, branchmerge):
568 def recordupdates(repo, actions, branchmerge):
561 "record merge actions to the dirstate"
569 "record merge actions to the dirstate"
562
570
563 for a in actions:
571 for a in actions:
564 f, m, args, msg = a
572 f, m, args, msg = a
565 if m == "r": # remove
573 if m == "r": # remove
566 if branchmerge:
574 if branchmerge:
567 repo.dirstate.remove(f)
575 repo.dirstate.remove(f)
568 else:
576 else:
569 repo.dirstate.drop(f)
577 repo.dirstate.drop(f)
570 elif m == "a": # re-add
578 elif m == "a": # re-add
571 if not branchmerge:
579 if not branchmerge:
572 repo.dirstate.add(f)
580 repo.dirstate.add(f)
573 elif m == "f": # forget
581 elif m == "f": # forget
574 repo.dirstate.drop(f)
582 repo.dirstate.drop(f)
575 elif m == "e": # exec change
583 elif m == "e": # exec change
576 repo.dirstate.normallookup(f)
584 repo.dirstate.normallookup(f)
577 elif m == "g": # get
585 elif m == "g": # get
578 if branchmerge:
586 if branchmerge:
579 repo.dirstate.otherparent(f)
587 repo.dirstate.otherparent(f)
580 else:
588 else:
581 repo.dirstate.normal(f)
589 repo.dirstate.normal(f)
582 elif m == "m": # merge
590 elif m == "m": # merge
583 f2, fd, move = args
591 f2, fd, move = args
584 if branchmerge:
592 if branchmerge:
585 # We've done a branch merge, mark this file as merged
593 # We've done a branch merge, mark this file as merged
586 # so that we properly record the merger later
594 # so that we properly record the merger later
587 repo.dirstate.merge(fd)
595 repo.dirstate.merge(fd)
588 if f != f2: # copy/rename
596 if f != f2: # copy/rename
589 if move:
597 if move:
590 repo.dirstate.remove(f)
598 repo.dirstate.remove(f)
591 if f != fd:
599 if f != fd:
592 repo.dirstate.copy(f, fd)
600 repo.dirstate.copy(f, fd)
593 else:
601 else:
594 repo.dirstate.copy(f2, fd)
602 repo.dirstate.copy(f2, fd)
595 else:
603 else:
596 # We've update-merged a locally modified file, so
604 # We've update-merged a locally modified file, so
597 # we set the dirstate to emulate a normal checkout
605 # we set the dirstate to emulate a normal checkout
598 # of that file some time in the past. Thus our
606 # of that file some time in the past. Thus our
599 # merge will appear as a normal local file
607 # merge will appear as a normal local file
600 # modification.
608 # modification.
601 if f2 == fd: # file not locally copied/moved
609 if f2 == fd: # file not locally copied/moved
602 repo.dirstate.normallookup(fd)
610 repo.dirstate.normallookup(fd)
603 if move:
611 if move:
604 repo.dirstate.drop(f)
612 repo.dirstate.drop(f)
605 elif m == "d": # directory rename
613 elif m == "d": # directory rename
606 f2, fd, flag = args
614 f2, fd, flag = args
607 if not f2 and f not in repo.dirstate:
615 if not f2 and f not in repo.dirstate:
608 # untracked file moved
616 # untracked file moved
609 continue
617 continue
610 if branchmerge:
618 if branchmerge:
611 repo.dirstate.add(fd)
619 repo.dirstate.add(fd)
612 if f:
620 if f:
613 repo.dirstate.remove(f)
621 repo.dirstate.remove(f)
614 repo.dirstate.copy(f, fd)
622 repo.dirstate.copy(f, fd)
615 if f2:
623 if f2:
616 repo.dirstate.copy(f2, fd)
624 repo.dirstate.copy(f2, fd)
617 else:
625 else:
618 repo.dirstate.normal(fd)
626 repo.dirstate.normal(fd)
619 if f:
627 if f:
620 repo.dirstate.drop(f)
628 repo.dirstate.drop(f)
621
629
622 def update(repo, node, branchmerge, force, partial, ancestor=None,
630 def update(repo, node, branchmerge, force, partial, ancestor=None,
623 mergeancestor=False):
631 mergeancestor=False):
624 """
632 """
625 Perform a merge between the working directory and the given node
633 Perform a merge between the working directory and the given node
626
634
627 node = the node to update to, or None if unspecified
635 node = the node to update to, or None if unspecified
628 branchmerge = whether to merge between branches
636 branchmerge = whether to merge between branches
629 force = whether to force branch merging or file overwriting
637 force = whether to force branch merging or file overwriting
630 partial = a function to filter file lists (dirstate not updated)
638 partial = a function to filter file lists (dirstate not updated)
631 mergeancestor = whether it is merging with an ancestor. If true,
639 mergeancestor = whether it is merging with an ancestor. If true,
632 we should accept the incoming changes for any prompts that occur.
640 we should accept the incoming changes for any prompts that occur.
633 If false, merging with an ancestor (fast-forward) is only allowed
641 If false, merging with an ancestor (fast-forward) is only allowed
634 between different named branches. This flag is used by rebase extension
642 between different named branches. This flag is used by rebase extension
635 as a temporary fix and should be avoided in general.
643 as a temporary fix and should be avoided in general.
636
644
637 The table below shows all the behaviors of the update command
645 The table below shows all the behaviors of the update command
638 given the -c and -C or no options, whether the working directory
646 given the -c and -C or no options, whether the working directory
639 is dirty, whether a revision is specified, and the relationship of
647 is dirty, whether a revision is specified, and the relationship of
640 the parent rev to the target rev (linear, on the same named
648 the parent rev to the target rev (linear, on the same named
641 branch, or on another named branch).
649 branch, or on another named branch).
642
650
643 This logic is tested by test-update-branches.t.
651 This logic is tested by test-update-branches.t.
644
652
645 -c -C dirty rev | linear same cross
653 -c -C dirty rev | linear same cross
646 n n n n | ok (1) x
654 n n n n | ok (1) x
647 n n n y | ok ok ok
655 n n n y | ok ok ok
648 n n y * | merge (2) (2)
656 n n y * | merge (2) (2)
649 n y * * | --- discard ---
657 n y * * | --- discard ---
650 y n y * | --- (3) ---
658 y n y * | --- (3) ---
651 y n n * | --- ok ---
659 y n n * | --- ok ---
652 y y * * | --- (4) ---
660 y y * * | --- (4) ---
653
661
654 x = can't happen
662 x = can't happen
655 * = don't-care
663 * = don't-care
656 1 = abort: crosses branches (use 'hg merge' or 'hg update -c')
664 1 = abort: crosses branches (use 'hg merge' or 'hg update -c')
657 2 = abort: crosses branches (use 'hg merge' to merge or
665 2 = abort: crosses branches (use 'hg merge' to merge or
658 use 'hg update -C' to discard changes)
666 use 'hg update -C' to discard changes)
659 3 = abort: uncommitted local changes
667 3 = abort: uncommitted local changes
660 4 = incompatible options (checked in commands.py)
668 4 = incompatible options (checked in commands.py)
661
669
662 Return the same tuple as applyupdates().
670 Return the same tuple as applyupdates().
663 """
671 """
664
672
665 onode = node
673 onode = node
666 wlock = repo.wlock()
674 wlock = repo.wlock()
667 try:
675 try:
668 wc = repo[None]
676 wc = repo[None]
669 if node is None:
677 if node is None:
670 # tip of current branch
678 # tip of current branch
671 try:
679 try:
672 node = repo.branchtip(wc.branch())
680 node = repo.branchtip(wc.branch())
673 except error.RepoLookupError:
681 except error.RepoLookupError:
674 if wc.branch() == "default": # no default branch!
682 if wc.branch() == "default": # no default branch!
675 node = repo.lookup("tip") # update to tip
683 node = repo.lookup("tip") # update to tip
676 else:
684 else:
677 raise util.Abort(_("branch %s not found") % wc.branch())
685 raise util.Abort(_("branch %s not found") % wc.branch())
678 overwrite = force and not branchmerge
686 overwrite = force and not branchmerge
679 pl = wc.parents()
687 pl = wc.parents()
680 p1, p2 = pl[0], repo[node]
688 p1, p2 = pl[0], repo[node]
681 if ancestor:
689 if ancestor:
682 pa = repo[ancestor]
690 pa = repo[ancestor]
683 else:
691 else:
684 pa = p1.ancestor(p2)
692 pa = p1.ancestor(p2)
685
693
686 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
694 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
687
695
688 ### check phase
696 ### check phase
689 if not overwrite and len(pl) > 1:
697 if not overwrite and len(pl) > 1:
690 raise util.Abort(_("outstanding uncommitted merges"))
698 raise util.Abort(_("outstanding uncommitted merges"))
691 if branchmerge:
699 if branchmerge:
692 if pa == p2:
700 if pa == p2:
693 raise util.Abort(_("merging with a working directory ancestor"
701 raise util.Abort(_("merging with a working directory ancestor"
694 " has no effect"))
702 " has no effect"))
695 elif pa == p1:
703 elif pa == p1:
696 if not mergeancestor and p1.branch() == p2.branch():
704 if not mergeancestor and p1.branch() == p2.branch():
697 raise util.Abort(_("nothing to merge"),
705 raise util.Abort(_("nothing to merge"),
698 hint=_("use 'hg update' "
706 hint=_("use 'hg update' "
699 "or check 'hg heads'"))
707 "or check 'hg heads'"))
700 if not force and (wc.files() or wc.deleted()):
708 if not force and (wc.files() or wc.deleted()):
701 raise util.Abort(_("outstanding uncommitted changes"),
709 raise util.Abort(_("outstanding uncommitted changes"),
702 hint=_("use 'hg status' to list changes"))
710 hint=_("use 'hg status' to list changes"))
703 for s in sorted(wc.substate):
711 for s in sorted(wc.substate):
704 if wc.sub(s).dirty():
712 if wc.sub(s).dirty():
705 raise util.Abort(_("outstanding uncommitted changes in "
713 raise util.Abort(_("outstanding uncommitted changes in "
706 "subrepository '%s'") % s)
714 "subrepository '%s'") % s)
707
715
708 elif not overwrite:
716 elif not overwrite:
709 if pa not in (p1, p2): # nolinear
717 if pa not in (p1, p2): # nolinear
710 dirty = wc.dirty(missing=True)
718 dirty = wc.dirty(missing=True)
711 if dirty or onode is None:
719 if dirty or onode is None:
712 # Branching is a bit strange to ensure we do the minimal
720 # Branching is a bit strange to ensure we do the minimal
713 # amount of call to obsolete.background.
721 # amount of call to obsolete.background.
714 foreground = obsolete.foreground(repo, [p1.node()])
722 foreground = obsolete.foreground(repo, [p1.node()])
715 # note: the <node> variable contains a random identifier
723 # note: the <node> variable contains a random identifier
716 if repo[node].node() in foreground:
724 if repo[node].node() in foreground:
717 pa = p1 # allow updating to successors
725 pa = p1 # allow updating to successors
718 elif dirty:
726 elif dirty:
719 msg = _("crosses branches (merge branches or use"
727 msg = _("crosses branches (merge branches or use"
720 " --clean to discard changes)")
728 " --clean to discard changes)")
721 raise util.Abort(msg)
729 raise util.Abort(msg)
722 else: # node is none
730 else: # node is none
723 msg = _("crosses branches (merge branches or update"
731 msg = _("crosses branches (merge branches or update"
724 " --check to force update)")
732 " --check to force update)")
725 raise util.Abort(msg)
733 raise util.Abort(msg)
726 else:
734 else:
727 # Allow jumping branches if clean and specific rev given
735 # Allow jumping branches if clean and specific rev given
728 pa = p1
736 pa = p1
729
737
730 ### calculate phase
738 ### calculate phase
731 actions = calculateupdates(repo, wc, p2, pa,
739 actions = calculateupdates(repo, wc, p2, pa,
732 branchmerge, force, partial, mergeancestor)
740 branchmerge, force, partial, mergeancestor)
733
741
734 ### apply phase
742 ### apply phase
735 if not branchmerge: # just jump to the new rev
743 if not branchmerge: # just jump to the new rev
736 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
744 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
737 if not partial:
745 if not partial:
738 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
746 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
739
747
740 stats = applyupdates(repo, actions, wc, p2, pa, overwrite)
748 stats = applyupdates(repo, actions, wc, p2, pa, overwrite)
741
749
742 if not partial:
750 if not partial:
743 repo.setparents(fp1, fp2)
751 repo.setparents(fp1, fp2)
744 recordupdates(repo, actions, branchmerge)
752 recordupdates(repo, actions, branchmerge)
745 if not branchmerge:
753 if not branchmerge:
746 repo.dirstate.setbranch(p2.branch())
754 repo.dirstate.setbranch(p2.branch())
747 finally:
755 finally:
748 wlock.release()
756 wlock.release()
749
757
750 if not partial:
758 if not partial:
751 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
759 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
752 return stats
760 return stats
@@ -1,225 +1,268 b''
1 run only on case-insensitive filesystems
1 run only on case-insensitive filesystems
2
2
3 $ "$TESTDIR/hghave" icasefs || exit 80
3 $ "$TESTDIR/hghave" icasefs || exit 80
4
4
5 ################################
5 ################################
6 test for branch merging
6 test for branch merging
7 ################################
7 ################################
8
8
9 test for rename awareness of case-folding collision check:
9 test for rename awareness of case-folding collision check:
10
10
11 (1) colliding file is one renamed from collided file:
11 (1) colliding file is one renamed from collided file:
12 this is also case for issue3370.
12 this is also case for issue3370.
13
13
14 $ hg init branch_merge_renaming
14 $ hg init branch_merge_renaming
15 $ cd branch_merge_renaming
15 $ cd branch_merge_renaming
16
16
17 $ echo a > a
17 $ echo a > a
18 $ hg add a
18 $ hg add a
19 $ hg commit -m '#0'
19 $ hg commit -m '#0'
20 $ hg tag -l A
20 $ hg rename a tmp
21 $ hg rename a tmp
21 $ hg rename tmp A
22 $ hg rename tmp A
22 $ hg commit -m '#1'
23 $ hg commit -m '#1'
24 $ hg tag -l B
23 $ hg update -q 0
25 $ hg update -q 0
24 $ touch x
26 $ touch x
25 $ hg add x
27 $ hg add x
26 $ hg commit -m '#2'
28 $ hg commit -m '#2'
27 created new head
29 created new head
30 $ hg tag -l C
28
31
29 $ hg merge -q
32 $ hg merge -q
30 $ hg status -A
33 $ hg status -A
31 M A
34 M A
32 R a
35 R a
33 C x
36 C x
34
37
35 $ hg update -q --clean 1
38 $ hg update -q --clean 1
36 $ hg merge -q
39 $ hg merge -q
37 $ hg status -A
40 $ hg status -A
38 M x
41 M x
39 C A
42 C A
43 $ hg commit -m '(D)'
44 $ hg tag -l D
45
46 additional test for issue3452:
47
48 | this assumes the history below.
49 |
50 | (A) -- (C) -- (E) -------
51 | \ \ \
52 | \ \ \
53 | (B) -- (D) -- (F) -- (G)
54 |
55 | A: add file 'a'
56 | B: rename from 'a' to 'A'
57 | C: add 'x' (or operation other than modification of 'a')
58 | D: merge C into B
59 | E: modify 'a'
60 | F: modify 'A'
61 | G: merge E into F
62 |
63 | issue3452 occurs when (B) is recorded before (C)
64
65 $ hg update -q --clean C
66 $ echo "modify 'a' at (E)" > a
67 $ hg commit -m '(E)'
68 created new head
69 $ hg tag -l E
70
71 $ hg update -q --clean D
72 $ echo "modify 'A' at (F)" > A
73 $ hg commit -m '(F)'
74 $ hg tag -l F
75
76 $ hg merge -q --tool internal:other E
77 $ hg status -A
78 M A
79 a
80 C x
81 $ cat A
82 modify 'a' at (E)
40
83
41 $ cd ..
84 $ cd ..
42
85
43 (2) colliding file is not related to collided file
86 (2) colliding file is not related to collided file
44
87
45 $ hg init branch_merge_collding
88 $ hg init branch_merge_collding
46 $ cd branch_merge_collding
89 $ cd branch_merge_collding
47
90
48 $ echo a > a
91 $ echo a > a
49 $ hg add a
92 $ hg add a
50 $ hg commit -m '#0'
93 $ hg commit -m '#0'
51 $ hg remove a
94 $ hg remove a
52 $ hg commit -m '#1'
95 $ hg commit -m '#1'
53 $ echo A > A
96 $ echo A > A
54 $ hg add A
97 $ hg add A
55 $ hg commit -m '#2'
98 $ hg commit -m '#2'
56 $ hg update --clean 0
99 $ hg update --clean 0
57 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
100 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
58 $ echo x > x
101 $ echo x > x
59 $ hg add x
102 $ hg add x
60 $ hg commit -m '#3'
103 $ hg commit -m '#3'
61 created new head
104 created new head
62 $ echo 'modified at #4' > a
105 $ echo 'modified at #4' > a
63 $ hg commit -m '#4'
106 $ hg commit -m '#4'
64
107
65 $ hg merge
108 $ hg merge
66 abort: case-folding collision between A and a
109 abort: case-folding collision between a and A
67 [255]
110 [255]
68 $ hg parents --template '{rev}\n'
111 $ hg parents --template '{rev}\n'
69 4
112 4
70 $ hg status -A
113 $ hg status -A
71 C a
114 C a
72 C x
115 C x
73 $ cat a
116 $ cat a
74 modified at #4
117 modified at #4
75
118
76 $ hg update --clean 2
119 $ hg update --clean 2
77 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
120 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
78 $ hg merge
121 $ hg merge
79 abort: case-folding collision between a and A
122 abort: case-folding collision between a and A
80 [255]
123 [255]
81 $ hg parents --template '{rev}\n'
124 $ hg parents --template '{rev}\n'
82 2
125 2
83 $ hg status -A
126 $ hg status -A
84 C A
127 C A
85 $ cat A
128 $ cat A
86 A
129 A
87
130
88 test for deletion awareness of case-folding collision check (issue3648):
131 test for deletion awareness of case-folding collision check (issue3648):
89 revision '#3' doesn't change 'a', so 'a' should be recognized as
132 revision '#3' doesn't change 'a', so 'a' should be recognized as
90 safely removed in merging between #2 and #3.
133 safely removed in merging between #2 and #3.
91
134
92 $ hg update --clean 3
135 $ hg update --clean 3
93 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
136 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
94 $ hg merge 2
137 $ hg merge 2
95 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
138 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
96 (branch merge, don't forget to commit)
139 (branch merge, don't forget to commit)
97 $ hg status -A
140 $ hg status -A
98 M A
141 M A
99 R a
142 R a
100 C x
143 C x
101
144
102 $ hg update --clean 2
145 $ hg update --clean 2
103 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
146 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
104 $ hg merge 3
147 $ hg merge 3
105 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
148 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
106 (branch merge, don't forget to commit)
149 (branch merge, don't forget to commit)
107 $ hg status -A
150 $ hg status -A
108 M x
151 M x
109 C A
152 C A
110
153
111 $ cd ..
154 $ cd ..
112
155
113
156
114 ################################
157 ################################
115 test for linear updates
158 test for linear updates
116 ################################
159 ################################
117
160
118 test for rename awareness of case-folding collision check:
161 test for rename awareness of case-folding collision check:
119
162
120 (1) colliding file is one renamed from collided file
163 (1) colliding file is one renamed from collided file
121
164
122 $ hg init linearupdate_renameaware_1
165 $ hg init linearupdate_renameaware_1
123 $ cd linearupdate_renameaware_1
166 $ cd linearupdate_renameaware_1
124
167
125 $ echo a > a
168 $ echo a > a
126 $ hg add a
169 $ hg add a
127 $ hg commit -m '#0'
170 $ hg commit -m '#0'
128 $ hg rename a tmp
171 $ hg rename a tmp
129 $ hg rename tmp A
172 $ hg rename tmp A
130 $ hg commit -m '#1'
173 $ hg commit -m '#1'
131
174
132 $ hg update 0
175 $ hg update 0
133 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
176 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
134
177
135 $ echo 'this is added line' >> a
178 $ echo 'this is added line' >> a
136 $ hg update 1
179 $ hg update 1
137 merging a and A to A
180 merging a and A to A
138 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
181 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
139 $ hg status -A
182 $ hg status -A
140 M A
183 M A
141 $ cat A
184 $ cat A
142 a
185 a
143 this is added line
186 this is added line
144
187
145 $ cd ..
188 $ cd ..
146
189
147 (2) colliding file is not related to collided file
190 (2) colliding file is not related to collided file
148
191
149 $ hg init linearupdate_renameaware_2
192 $ hg init linearupdate_renameaware_2
150 $ cd linearupdate_renameaware_2
193 $ cd linearupdate_renameaware_2
151
194
152 $ echo a > a
195 $ echo a > a
153 $ hg add a
196 $ hg add a
154 $ hg commit -m '#0'
197 $ hg commit -m '#0'
155 $ hg remove a
198 $ hg remove a
156 $ hg commit -m '#1'
199 $ hg commit -m '#1'
157 $ echo A > A
200 $ echo A > A
158 $ hg add A
201 $ hg add A
159 $ hg commit -m '#2'
202 $ hg commit -m '#2'
160
203
161 $ hg update 0
204 $ hg update 0
162 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
205 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
163 $ hg parents --template '{rev}\n'
206 $ hg parents --template '{rev}\n'
164 0
207 0
165 $ hg status -A
208 $ hg status -A
166 C a
209 C a
167 $ cat A
210 $ cat A
168 a
211 a
169 $ hg up -qC 2
212 $ hg up -qC 2
170
213
171 $ hg update --check 0
214 $ hg update --check 0
172 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
215 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
173 $ hg parents --template '{rev}\n'
216 $ hg parents --template '{rev}\n'
174 0
217 0
175 $ hg status -A
218 $ hg status -A
176 C a
219 C a
177 $ cat a
220 $ cat a
178 a
221 a
179
222
180 $ hg update --clean 2
223 $ hg update --clean 2
181 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
224 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
182 $ hg parents --template '{rev}\n'
225 $ hg parents --template '{rev}\n'
183 2
226 2
184 $ hg status -A
227 $ hg status -A
185 C A
228 C A
186 $ cat A
229 $ cat A
187 A
230 A
188
231
189 $ cd ..
232 $ cd ..
190
233
191 (3) colliding file is not related to collided file: added in working dir
234 (3) colliding file is not related to collided file: added in working dir
192
235
193 $ hg init linearupdate_renameaware_3
236 $ hg init linearupdate_renameaware_3
194 $ cd linearupdate_renameaware_3
237 $ cd linearupdate_renameaware_3
195
238
196 $ echo a > a
239 $ echo a > a
197 $ hg add a
240 $ hg add a
198 $ hg commit -m '#0'
241 $ hg commit -m '#0'
199 $ hg rename a b
242 $ hg rename a b
200 $ hg commit -m '#1'
243 $ hg commit -m '#1'
201 $ hg update 0
244 $ hg update 0
202 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
245 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
203
246
204 $ echo B > B
247 $ echo B > B
205 $ hg add B
248 $ hg add B
206 $ hg status
249 $ hg status
207 A B
250 A B
208 $ hg update
251 $ hg update
209 abort: case-folding collision between b and B
252 abort: case-folding collision between b and B
210 [255]
253 [255]
211
254
212 $ hg update --check
255 $ hg update --check
213 abort: uncommitted local changes
256 abort: uncommitted local changes
214 [255]
257 [255]
215
258
216 $ hg update --clean
259 $ hg update --clean
217 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
260 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
218 $ hg parents --template '{rev}\n'
261 $ hg parents --template '{rev}\n'
219 1
262 1
220 $ hg status -A
263 $ hg status -A
221 C b
264 C b
222 $ cat b
265 $ cat b
223 a
266 a
224
267
225 $ cd ..
268 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now