##// END OF EJS Templates
update: use higher level wording for "crosses branches" error...
Brodie Rao -
r12681:bc13e170 default
parent child Browse files
Show More
@@ -1,542 +1,542 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 import util, filemerge, copies, subrepo
10 import util, filemerge, copies, subrepo
11 import errno, os, shutil
11 import errno, os, shutil
12
12
13 class mergestate(object):
13 class mergestate(object):
14 '''track 3-way merge state of individual files'''
14 '''track 3-way merge state of individual files'''
15 def __init__(self, repo):
15 def __init__(self, repo):
16 self._repo = repo
16 self._repo = repo
17 self._dirty = False
17 self._dirty = False
18 self._read()
18 self._read()
19 def reset(self, node=None):
19 def reset(self, node=None):
20 self._state = {}
20 self._state = {}
21 if node:
21 if node:
22 self._local = node
22 self._local = node
23 shutil.rmtree(self._repo.join("merge"), True)
23 shutil.rmtree(self._repo.join("merge"), True)
24 self._dirty = False
24 self._dirty = False
25 def _read(self):
25 def _read(self):
26 self._state = {}
26 self._state = {}
27 try:
27 try:
28 f = self._repo.opener("merge/state")
28 f = self._repo.opener("merge/state")
29 for i, l in enumerate(f):
29 for i, l in enumerate(f):
30 if i == 0:
30 if i == 0:
31 self._local = bin(l[:-1])
31 self._local = bin(l[:-1])
32 else:
32 else:
33 bits = l[:-1].split("\0")
33 bits = l[:-1].split("\0")
34 self._state[bits[0]] = bits[1:]
34 self._state[bits[0]] = bits[1:]
35 except IOError, err:
35 except IOError, err:
36 if err.errno != errno.ENOENT:
36 if err.errno != errno.ENOENT:
37 raise
37 raise
38 self._dirty = False
38 self._dirty = False
39 def commit(self):
39 def commit(self):
40 if self._dirty:
40 if self._dirty:
41 f = self._repo.opener("merge/state", "w")
41 f = self._repo.opener("merge/state", "w")
42 f.write(hex(self._local) + "\n")
42 f.write(hex(self._local) + "\n")
43 for d, v in self._state.iteritems():
43 for d, v in self._state.iteritems():
44 f.write("\0".join([d] + v) + "\n")
44 f.write("\0".join([d] + v) + "\n")
45 self._dirty = False
45 self._dirty = False
46 def add(self, fcl, fco, fca, fd, flags):
46 def add(self, fcl, fco, fca, fd, flags):
47 hash = util.sha1(fcl.path()).hexdigest()
47 hash = util.sha1(fcl.path()).hexdigest()
48 self._repo.opener("merge/" + hash, "w").write(fcl.data())
48 self._repo.opener("merge/" + hash, "w").write(fcl.data())
49 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
49 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
50 hex(fca.filenode()), fco.path(), flags]
50 hex(fca.filenode()), fco.path(), flags]
51 self._dirty = True
51 self._dirty = True
52 def __contains__(self, dfile):
52 def __contains__(self, dfile):
53 return dfile in self._state
53 return dfile in self._state
54 def __getitem__(self, dfile):
54 def __getitem__(self, dfile):
55 return self._state[dfile][0]
55 return self._state[dfile][0]
56 def __iter__(self):
56 def __iter__(self):
57 l = self._state.keys()
57 l = self._state.keys()
58 l.sort()
58 l.sort()
59 for f in l:
59 for f in l:
60 yield f
60 yield f
61 def mark(self, dfile, state):
61 def mark(self, dfile, state):
62 self._state[dfile][0] = state
62 self._state[dfile][0] = state
63 self._dirty = True
63 self._dirty = True
64 def resolve(self, dfile, wctx, octx):
64 def resolve(self, dfile, wctx, octx):
65 if self[dfile] == 'r':
65 if self[dfile] == 'r':
66 return 0
66 return 0
67 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
67 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
68 f = self._repo.opener("merge/" + hash)
68 f = self._repo.opener("merge/" + hash)
69 self._repo.wwrite(dfile, f.read(), flags)
69 self._repo.wwrite(dfile, f.read(), flags)
70 fcd = wctx[dfile]
70 fcd = wctx[dfile]
71 fco = octx[ofile]
71 fco = octx[ofile]
72 fca = self._repo.filectx(afile, fileid=anode)
72 fca = self._repo.filectx(afile, fileid=anode)
73 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
73 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
74 if not r:
74 if not r:
75 self.mark(dfile, 'r')
75 self.mark(dfile, 'r')
76 return r
76 return r
77
77
78 def _checkunknown(wctx, mctx):
78 def _checkunknown(wctx, mctx):
79 "check for collisions between unknown files and files in mctx"
79 "check for collisions between unknown files and files in mctx"
80 for f in wctx.unknown():
80 for f in wctx.unknown():
81 if f in mctx and mctx[f].cmp(wctx[f]):
81 if f in mctx and mctx[f].cmp(wctx[f]):
82 raise util.Abort(_("untracked file in working directory differs"
82 raise util.Abort(_("untracked file in working directory differs"
83 " from file in requested revision: '%s'") % f)
83 " from file in requested revision: '%s'") % f)
84
84
85 def _checkcollision(mctx):
85 def _checkcollision(mctx):
86 "check for case folding collisions in the destination context"
86 "check for case folding collisions in the destination context"
87 folded = {}
87 folded = {}
88 for fn in mctx:
88 for fn in mctx:
89 fold = fn.lower()
89 fold = fn.lower()
90 if fold in folded:
90 if fold in folded:
91 raise util.Abort(_("case-folding collision between %s and %s")
91 raise util.Abort(_("case-folding collision between %s and %s")
92 % (fn, folded[fold]))
92 % (fn, folded[fold]))
93 folded[fold] = fn
93 folded[fold] = fn
94
94
95 def _forgetremoved(wctx, mctx, branchmerge):
95 def _forgetremoved(wctx, mctx, branchmerge):
96 """
96 """
97 Forget removed files
97 Forget removed files
98
98
99 If we're jumping between revisions (as opposed to merging), and if
99 If we're jumping between revisions (as opposed to merging), and if
100 neither the working directory nor the target rev has the file,
100 neither the working directory nor the target rev has the file,
101 then we need to remove it from the dirstate, to prevent the
101 then we need to remove it from the dirstate, to prevent the
102 dirstate from listing the file when it is no longer in the
102 dirstate from listing the file when it is no longer in the
103 manifest.
103 manifest.
104
104
105 If we're merging, and the other revision has removed a file
105 If we're merging, and the other revision has removed a file
106 that is not present in the working directory, we need to mark it
106 that is not present in the working directory, we need to mark it
107 as removed.
107 as removed.
108 """
108 """
109
109
110 action = []
110 action = []
111 state = branchmerge and 'r' or 'f'
111 state = branchmerge and 'r' or 'f'
112 for f in wctx.deleted():
112 for f in wctx.deleted():
113 if f not in mctx:
113 if f not in mctx:
114 action.append((f, state))
114 action.append((f, state))
115
115
116 if not branchmerge:
116 if not branchmerge:
117 for f in wctx.removed():
117 for f in wctx.removed():
118 if f not in mctx:
118 if f not in mctx:
119 action.append((f, "f"))
119 action.append((f, "f"))
120
120
121 return action
121 return action
122
122
123 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
123 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
124 """
124 """
125 Merge p1 and p2 with ancestor pa and generate merge action list
125 Merge p1 and p2 with ancestor pa and generate merge action list
126
126
127 overwrite = whether we clobber working files
127 overwrite = whether we clobber working files
128 partial = function to filter file lists
128 partial = function to filter file lists
129 """
129 """
130
130
131 def fmerge(f, f2, fa):
131 def fmerge(f, f2, fa):
132 """merge flags"""
132 """merge flags"""
133 a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
133 a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
134 if m == n: # flags agree
134 if m == n: # flags agree
135 return m # unchanged
135 return m # unchanged
136 if m and n and not a: # flags set, don't agree, differ from parent
136 if m and n and not a: # flags set, don't agree, differ from parent
137 r = repo.ui.promptchoice(
137 r = repo.ui.promptchoice(
138 _(" conflicting flags for %s\n"
138 _(" conflicting flags for %s\n"
139 "(n)one, e(x)ec or sym(l)ink?") % f,
139 "(n)one, e(x)ec or sym(l)ink?") % f,
140 (_("&None"), _("E&xec"), _("Sym&link")), 0)
140 (_("&None"), _("E&xec"), _("Sym&link")), 0)
141 if r == 1:
141 if r == 1:
142 return "x" # Exec
142 return "x" # Exec
143 if r == 2:
143 if r == 2:
144 return "l" # Symlink
144 return "l" # Symlink
145 return ""
145 return ""
146 if m and m != a: # changed from a to m
146 if m and m != a: # changed from a to m
147 return m
147 return m
148 if n and n != a: # changed from a to n
148 if n and n != a: # changed from a to n
149 return n
149 return n
150 return '' # flag was cleared
150 return '' # flag was cleared
151
151
152 def act(msg, m, f, *args):
152 def act(msg, m, f, *args):
153 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
153 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
154 action.append((f, m) + args)
154 action.append((f, m) + args)
155
155
156 action, copy = [], {}
156 action, copy = [], {}
157
157
158 if overwrite:
158 if overwrite:
159 pa = p1
159 pa = p1
160 elif pa == p2: # backwards
160 elif pa == p2: # backwards
161 pa = p1.p1()
161 pa = p1.p1()
162 elif pa and repo.ui.configbool("merge", "followcopies", True):
162 elif pa and repo.ui.configbool("merge", "followcopies", True):
163 dirs = repo.ui.configbool("merge", "followdirs", True)
163 dirs = repo.ui.configbool("merge", "followdirs", True)
164 copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
164 copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
165 for of, fl in diverge.iteritems():
165 for of, fl in diverge.iteritems():
166 act("divergent renames", "dr", of, fl)
166 act("divergent renames", "dr", of, fl)
167
167
168 repo.ui.note(_("resolving manifests\n"))
168 repo.ui.note(_("resolving manifests\n"))
169 repo.ui.debug(" overwrite %s partial %s\n" % (overwrite, bool(partial)))
169 repo.ui.debug(" overwrite %s partial %s\n" % (overwrite, bool(partial)))
170 repo.ui.debug(" ancestor %s local %s remote %s\n" % (pa, p1, p2))
170 repo.ui.debug(" ancestor %s local %s remote %s\n" % (pa, p1, p2))
171
171
172 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
172 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
173 copied = set(copy.values())
173 copied = set(copy.values())
174
174
175 if '.hgsubstate' in m1:
175 if '.hgsubstate' in m1:
176 # check whether sub state is modified
176 # check whether sub state is modified
177 for s in p1.substate:
177 for s in p1.substate:
178 if p1.sub(s).dirty():
178 if p1.sub(s).dirty():
179 m1['.hgsubstate'] += "+"
179 m1['.hgsubstate'] += "+"
180 break
180 break
181
181
182 # Compare manifests
182 # Compare manifests
183 for f, n in m1.iteritems():
183 for f, n in m1.iteritems():
184 if partial and not partial(f):
184 if partial and not partial(f):
185 continue
185 continue
186 if f in m2:
186 if f in m2:
187 rflags = fmerge(f, f, f)
187 rflags = fmerge(f, f, f)
188 a = ma.get(f, nullid)
188 a = ma.get(f, nullid)
189 if n == m2[f] or m2[f] == a: # same or local newer
189 if n == m2[f] or m2[f] == a: # same or local newer
190 # is file locally modified or flags need changing?
190 # is file locally modified or flags need changing?
191 # dirstate flags may need to be made current
191 # dirstate flags may need to be made current
192 if m1.flags(f) != rflags or n[20:]:
192 if m1.flags(f) != rflags or n[20:]:
193 act("update permissions", "e", f, rflags)
193 act("update permissions", "e", f, rflags)
194 elif n == a: # remote newer
194 elif n == a: # remote newer
195 act("remote is newer", "g", f, rflags)
195 act("remote is newer", "g", f, rflags)
196 else: # both changed
196 else: # both changed
197 act("versions differ", "m", f, f, f, rflags, False)
197 act("versions differ", "m", f, f, f, rflags, False)
198 elif f in copied: # files we'll deal with on m2 side
198 elif f in copied: # files we'll deal with on m2 side
199 pass
199 pass
200 elif f in copy:
200 elif f in copy:
201 f2 = copy[f]
201 f2 = copy[f]
202 if f2 not in m2: # directory rename
202 if f2 not in m2: # directory rename
203 act("remote renamed directory to " + f2, "d",
203 act("remote renamed directory to " + f2, "d",
204 f, None, f2, m1.flags(f))
204 f, None, f2, m1.flags(f))
205 else: # case 2 A,B/B/B or case 4,21 A/B/B
205 else: # case 2 A,B/B/B or case 4,21 A/B/B
206 act("local copied/moved to " + f2, "m",
206 act("local copied/moved to " + f2, "m",
207 f, f2, f, fmerge(f, f2, f2), False)
207 f, f2, f, fmerge(f, f2, f2), False)
208 elif f in ma: # clean, a different, no remote
208 elif f in ma: # clean, a different, no remote
209 if n != ma[f]:
209 if n != ma[f]:
210 if repo.ui.promptchoice(
210 if repo.ui.promptchoice(
211 _(" local changed %s which remote deleted\n"
211 _(" local changed %s which remote deleted\n"
212 "use (c)hanged version or (d)elete?") % f,
212 "use (c)hanged version or (d)elete?") % f,
213 (_("&Changed"), _("&Delete")), 0):
213 (_("&Changed"), _("&Delete")), 0):
214 act("prompt delete", "r", f)
214 act("prompt delete", "r", f)
215 else:
215 else:
216 act("prompt keep", "a", f)
216 act("prompt keep", "a", f)
217 elif n[20:] == "a": # added, no remote
217 elif n[20:] == "a": # added, no remote
218 act("remote deleted", "f", f)
218 act("remote deleted", "f", f)
219 elif n[20:] != "u":
219 elif n[20:] != "u":
220 act("other deleted", "r", f)
220 act("other deleted", "r", f)
221
221
222 for f, n in m2.iteritems():
222 for f, n in m2.iteritems():
223 if partial and not partial(f):
223 if partial and not partial(f):
224 continue
224 continue
225 if f in m1 or f in copied: # files already visited
225 if f in m1 or f in copied: # files already visited
226 continue
226 continue
227 if f in copy:
227 if f in copy:
228 f2 = copy[f]
228 f2 = copy[f]
229 if f2 not in m1: # directory rename
229 if f2 not in m1: # directory rename
230 act("local renamed directory to " + f2, "d",
230 act("local renamed directory to " + f2, "d",
231 None, f, f2, m2.flags(f))
231 None, f, f2, m2.flags(f))
232 elif f2 in m2: # rename case 1, A/A,B/A
232 elif f2 in m2: # rename case 1, A/A,B/A
233 act("remote copied to " + f, "m",
233 act("remote copied to " + f, "m",
234 f2, f, f, fmerge(f2, f, f2), False)
234 f2, f, f, fmerge(f2, f, f2), False)
235 else: # case 3,20 A/B/A
235 else: # case 3,20 A/B/A
236 act("remote moved to " + f, "m",
236 act("remote moved to " + f, "m",
237 f2, f, f, fmerge(f2, f, f2), True)
237 f2, f, f, fmerge(f2, f, f2), True)
238 elif f not in ma:
238 elif f not in ma:
239 act("remote created", "g", f, m2.flags(f))
239 act("remote created", "g", f, m2.flags(f))
240 elif n != ma[f]:
240 elif n != ma[f]:
241 if repo.ui.promptchoice(
241 if repo.ui.promptchoice(
242 _("remote changed %s which local deleted\n"
242 _("remote changed %s which local deleted\n"
243 "use (c)hanged version or leave (d)eleted?") % f,
243 "use (c)hanged version or leave (d)eleted?") % f,
244 (_("&Changed"), _("&Deleted")), 0) == 0:
244 (_("&Changed"), _("&Deleted")), 0) == 0:
245 act("prompt recreating", "g", f, m2.flags(f))
245 act("prompt recreating", "g", f, m2.flags(f))
246
246
247 return action
247 return action
248
248
249 def actionkey(a):
249 def actionkey(a):
250 return a[1] == 'r' and -1 or 0, a
250 return a[1] == 'r' and -1 or 0, a
251
251
252 def applyupdates(repo, action, wctx, mctx, actx):
252 def applyupdates(repo, action, wctx, mctx, actx):
253 """apply the merge action list to the working directory
253 """apply the merge action list to the working directory
254
254
255 wctx is the working copy context
255 wctx is the working copy context
256 mctx is the context to be merged into the working copy
256 mctx is the context to be merged into the working copy
257 actx is the context of the common ancestor
257 actx is the context of the common ancestor
258 """
258 """
259
259
260 updated, merged, removed, unresolved = 0, 0, 0, 0
260 updated, merged, removed, unresolved = 0, 0, 0, 0
261 ms = mergestate(repo)
261 ms = mergestate(repo)
262 ms.reset(wctx.parents()[0].node())
262 ms.reset(wctx.parents()[0].node())
263 moves = []
263 moves = []
264 action.sort(key=actionkey)
264 action.sort(key=actionkey)
265 substate = wctx.substate # prime
265 substate = wctx.substate # prime
266
266
267 # prescan for merges
267 # prescan for merges
268 u = repo.ui
268 u = repo.ui
269 for a in action:
269 for a in action:
270 f, m = a[:2]
270 f, m = a[:2]
271 if m == 'm': # merge
271 if m == 'm': # merge
272 f2, fd, flags, move = a[2:]
272 f2, fd, flags, move = a[2:]
273 if f == '.hgsubstate': # merged internally
273 if f == '.hgsubstate': # merged internally
274 continue
274 continue
275 repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd))
275 repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd))
276 fcl = wctx[f]
276 fcl = wctx[f]
277 fco = mctx[f2]
277 fco = mctx[f2]
278 if mctx == actx: # backwards, use working dir parent as ancestor
278 if mctx == actx: # backwards, use working dir parent as ancestor
279 if fcl.parents():
279 if fcl.parents():
280 fca = fcl.parents()[0]
280 fca = fcl.parents()[0]
281 else:
281 else:
282 fca = repo.filectx(f, fileid=nullrev)
282 fca = repo.filectx(f, fileid=nullrev)
283 else:
283 else:
284 fca = fcl.ancestor(fco, actx)
284 fca = fcl.ancestor(fco, actx)
285 if not fca:
285 if not fca:
286 fca = repo.filectx(f, fileid=nullrev)
286 fca = repo.filectx(f, fileid=nullrev)
287 ms.add(fcl, fco, fca, fd, flags)
287 ms.add(fcl, fco, fca, fd, flags)
288 if f != fd and move:
288 if f != fd and move:
289 moves.append(f)
289 moves.append(f)
290
290
291 # remove renamed files after safely stored
291 # remove renamed files after safely stored
292 for f in moves:
292 for f in moves:
293 if os.path.lexists(repo.wjoin(f)):
293 if os.path.lexists(repo.wjoin(f)):
294 repo.ui.debug("removing %s\n" % f)
294 repo.ui.debug("removing %s\n" % f)
295 os.unlink(repo.wjoin(f))
295 os.unlink(repo.wjoin(f))
296
296
297 audit_path = util.path_auditor(repo.root)
297 audit_path = util.path_auditor(repo.root)
298
298
299 numupdates = len(action)
299 numupdates = len(action)
300 for i, a in enumerate(action):
300 for i, a in enumerate(action):
301 f, m = a[:2]
301 f, m = a[:2]
302 u.progress(_('updating'), i + 1, item=f, total=numupdates, unit='files')
302 u.progress(_('updating'), i + 1, item=f, total=numupdates, unit='files')
303 if f and f[0] == "/":
303 if f and f[0] == "/":
304 continue
304 continue
305 if m == "r": # remove
305 if m == "r": # remove
306 repo.ui.note(_("removing %s\n") % f)
306 repo.ui.note(_("removing %s\n") % f)
307 audit_path(f)
307 audit_path(f)
308 if f == '.hgsubstate': # subrepo states need updating
308 if f == '.hgsubstate': # subrepo states need updating
309 subrepo.submerge(repo, wctx, mctx, wctx)
309 subrepo.submerge(repo, wctx, mctx, wctx)
310 try:
310 try:
311 util.unlink(repo.wjoin(f))
311 util.unlink(repo.wjoin(f))
312 except OSError, inst:
312 except OSError, inst:
313 if inst.errno != errno.ENOENT:
313 if inst.errno != errno.ENOENT:
314 repo.ui.warn(_("update failed to remove %s: %s!\n") %
314 repo.ui.warn(_("update failed to remove %s: %s!\n") %
315 (f, inst.strerror))
315 (f, inst.strerror))
316 removed += 1
316 removed += 1
317 elif m == "m": # merge
317 elif m == "m": # merge
318 if f == '.hgsubstate': # subrepo states need updating
318 if f == '.hgsubstate': # subrepo states need updating
319 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx))
319 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx))
320 continue
320 continue
321 f2, fd, flags, move = a[2:]
321 f2, fd, flags, move = a[2:]
322 r = ms.resolve(fd, wctx, mctx)
322 r = ms.resolve(fd, wctx, mctx)
323 if r is not None and r > 0:
323 if r is not None and r > 0:
324 unresolved += 1
324 unresolved += 1
325 else:
325 else:
326 if r is None:
326 if r is None:
327 updated += 1
327 updated += 1
328 else:
328 else:
329 merged += 1
329 merged += 1
330 util.set_flags(repo.wjoin(fd), 'l' in flags, 'x' in flags)
330 util.set_flags(repo.wjoin(fd), 'l' in flags, 'x' in flags)
331 if f != fd and move and os.path.lexists(repo.wjoin(f)):
331 if f != fd and move and os.path.lexists(repo.wjoin(f)):
332 repo.ui.debug("removing %s\n" % f)
332 repo.ui.debug("removing %s\n" % f)
333 os.unlink(repo.wjoin(f))
333 os.unlink(repo.wjoin(f))
334 elif m == "g": # get
334 elif m == "g": # get
335 flags = a[2]
335 flags = a[2]
336 repo.ui.note(_("getting %s\n") % f)
336 repo.ui.note(_("getting %s\n") % f)
337 t = mctx.filectx(f).data()
337 t = mctx.filectx(f).data()
338 repo.wwrite(f, t, flags)
338 repo.wwrite(f, t, flags)
339 t = None
339 t = None
340 updated += 1
340 updated += 1
341 if f == '.hgsubstate': # subrepo states need updating
341 if f == '.hgsubstate': # subrepo states need updating
342 subrepo.submerge(repo, wctx, mctx, wctx)
342 subrepo.submerge(repo, wctx, mctx, wctx)
343 elif m == "d": # directory rename
343 elif m == "d": # directory rename
344 f2, fd, flags = a[2:]
344 f2, fd, flags = a[2:]
345 if f:
345 if f:
346 repo.ui.note(_("moving %s to %s\n") % (f, fd))
346 repo.ui.note(_("moving %s to %s\n") % (f, fd))
347 t = wctx.filectx(f).data()
347 t = wctx.filectx(f).data()
348 repo.wwrite(fd, t, flags)
348 repo.wwrite(fd, t, flags)
349 util.unlink(repo.wjoin(f))
349 util.unlink(repo.wjoin(f))
350 if f2:
350 if f2:
351 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
351 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
352 t = mctx.filectx(f2).data()
352 t = mctx.filectx(f2).data()
353 repo.wwrite(fd, t, flags)
353 repo.wwrite(fd, t, flags)
354 updated += 1
354 updated += 1
355 elif m == "dr": # divergent renames
355 elif m == "dr": # divergent renames
356 fl = a[2]
356 fl = a[2]
357 repo.ui.warn(_("warning: detected divergent renames of %s to:\n") % f)
357 repo.ui.warn(_("warning: detected divergent renames of %s to:\n") % f)
358 for nf in fl:
358 for nf in fl:
359 repo.ui.warn(" %s\n" % nf)
359 repo.ui.warn(" %s\n" % nf)
360 elif m == "e": # exec
360 elif m == "e": # exec
361 flags = a[2]
361 flags = a[2]
362 util.set_flags(repo.wjoin(f), 'l' in flags, 'x' in flags)
362 util.set_flags(repo.wjoin(f), 'l' in flags, 'x' in flags)
363 ms.commit()
363 ms.commit()
364 u.progress(_('updating'), None, total=numupdates, unit='files')
364 u.progress(_('updating'), None, total=numupdates, unit='files')
365
365
366 return updated, merged, removed, unresolved
366 return updated, merged, removed, unresolved
367
367
368 def recordupdates(repo, action, branchmerge):
368 def recordupdates(repo, action, branchmerge):
369 "record merge actions to the dirstate"
369 "record merge actions to the dirstate"
370
370
371 for a in action:
371 for a in action:
372 f, m = a[:2]
372 f, m = a[:2]
373 if m == "r": # remove
373 if m == "r": # remove
374 if branchmerge:
374 if branchmerge:
375 repo.dirstate.remove(f)
375 repo.dirstate.remove(f)
376 else:
376 else:
377 repo.dirstate.forget(f)
377 repo.dirstate.forget(f)
378 elif m == "a": # re-add
378 elif m == "a": # re-add
379 if not branchmerge:
379 if not branchmerge:
380 repo.dirstate.add(f)
380 repo.dirstate.add(f)
381 elif m == "f": # forget
381 elif m == "f": # forget
382 repo.dirstate.forget(f)
382 repo.dirstate.forget(f)
383 elif m == "e": # exec change
383 elif m == "e": # exec change
384 repo.dirstate.normallookup(f)
384 repo.dirstate.normallookup(f)
385 elif m == "g": # get
385 elif m == "g": # get
386 if branchmerge:
386 if branchmerge:
387 repo.dirstate.otherparent(f)
387 repo.dirstate.otherparent(f)
388 else:
388 else:
389 repo.dirstate.normal(f)
389 repo.dirstate.normal(f)
390 elif m == "m": # merge
390 elif m == "m": # merge
391 f2, fd, flag, move = a[2:]
391 f2, fd, flag, move = a[2:]
392 if branchmerge:
392 if branchmerge:
393 # We've done a branch merge, mark this file as merged
393 # We've done a branch merge, mark this file as merged
394 # so that we properly record the merger later
394 # so that we properly record the merger later
395 repo.dirstate.merge(fd)
395 repo.dirstate.merge(fd)
396 if f != f2: # copy/rename
396 if f != f2: # copy/rename
397 if move:
397 if move:
398 repo.dirstate.remove(f)
398 repo.dirstate.remove(f)
399 if f != fd:
399 if f != fd:
400 repo.dirstate.copy(f, fd)
400 repo.dirstate.copy(f, fd)
401 else:
401 else:
402 repo.dirstate.copy(f2, fd)
402 repo.dirstate.copy(f2, fd)
403 else:
403 else:
404 # We've update-merged a locally modified file, so
404 # We've update-merged a locally modified file, so
405 # we set the dirstate to emulate a normal checkout
405 # we set the dirstate to emulate a normal checkout
406 # of that file some time in the past. Thus our
406 # of that file some time in the past. Thus our
407 # merge will appear as a normal local file
407 # merge will appear as a normal local file
408 # modification.
408 # modification.
409 if f2 == fd: # file not locally copied/moved
409 if f2 == fd: # file not locally copied/moved
410 repo.dirstate.normallookup(fd)
410 repo.dirstate.normallookup(fd)
411 if move:
411 if move:
412 repo.dirstate.forget(f)
412 repo.dirstate.forget(f)
413 elif m == "d": # directory rename
413 elif m == "d": # directory rename
414 f2, fd, flag = a[2:]
414 f2, fd, flag = a[2:]
415 if not f2 and f not in repo.dirstate:
415 if not f2 and f not in repo.dirstate:
416 # untracked file moved
416 # untracked file moved
417 continue
417 continue
418 if branchmerge:
418 if branchmerge:
419 repo.dirstate.add(fd)
419 repo.dirstate.add(fd)
420 if f:
420 if f:
421 repo.dirstate.remove(f)
421 repo.dirstate.remove(f)
422 repo.dirstate.copy(f, fd)
422 repo.dirstate.copy(f, fd)
423 if f2:
423 if f2:
424 repo.dirstate.copy(f2, fd)
424 repo.dirstate.copy(f2, fd)
425 else:
425 else:
426 repo.dirstate.normal(fd)
426 repo.dirstate.normal(fd)
427 if f:
427 if f:
428 repo.dirstate.forget(f)
428 repo.dirstate.forget(f)
429
429
430 def update(repo, node, branchmerge, force, partial):
430 def update(repo, node, branchmerge, force, partial):
431 """
431 """
432 Perform a merge between the working directory and the given node
432 Perform a merge between the working directory and the given node
433
433
434 node = the node to update to, or None if unspecified
434 node = the node to update to, or None if unspecified
435 branchmerge = whether to merge between branches
435 branchmerge = whether to merge between branches
436 force = whether to force branch merging or file overwriting
436 force = whether to force branch merging or file overwriting
437 partial = a function to filter file lists (dirstate not updated)
437 partial = a function to filter file lists (dirstate not updated)
438
438
439 The table below shows all the behaviors of the update command
439 The table below shows all the behaviors of the update command
440 given the -c and -C or no options, whether the working directory
440 given the -c and -C or no options, whether the working directory
441 is dirty, whether a revision is specified, and the relationship of
441 is dirty, whether a revision is specified, and the relationship of
442 the parent rev to the target rev (linear, on the same named
442 the parent rev to the target rev (linear, on the same named
443 branch, or on another named branch).
443 branch, or on another named branch).
444
444
445 This logic is tested by test-update-branches.t.
445 This logic is tested by test-update-branches.t.
446
446
447 -c -C dirty rev | linear same cross
447 -c -C dirty rev | linear same cross
448 n n n n | ok (1) x
448 n n n n | ok (1) x
449 n n n y | ok ok ok
449 n n n y | ok ok ok
450 n n y * | merge (2) (2)
450 n n y * | merge (2) (2)
451 n y * * | --- discard ---
451 n y * * | --- discard ---
452 y n y * | --- (3) ---
452 y n y * | --- (3) ---
453 y n n * | --- ok ---
453 y n n * | --- ok ---
454 y y * * | --- (4) ---
454 y y * * | --- (4) ---
455
455
456 x = can't happen
456 x = can't happen
457 * = don't-care
457 * = don't-care
458 1 = abort: crosses branches (use 'hg merge' or 'hg update -c')
458 1 = abort: crosses branches (use 'hg merge' or 'hg update -c')
459 2 = abort: crosses branches (use 'hg merge' to merge or
459 2 = abort: crosses branches (use 'hg merge' to merge or
460 use 'hg update -C' to discard changes)
460 use 'hg update -C' to discard changes)
461 3 = abort: uncommitted local changes
461 3 = abort: uncommitted local changes
462 4 = incompatible options (checked in commands.py)
462 4 = incompatible options (checked in commands.py)
463 """
463 """
464
464
465 onode = node
465 onode = node
466 wlock = repo.wlock()
466 wlock = repo.wlock()
467 try:
467 try:
468 wc = repo[None]
468 wc = repo[None]
469 if node is None:
469 if node is None:
470 # tip of current branch
470 # tip of current branch
471 try:
471 try:
472 node = repo.branchtags()[wc.branch()]
472 node = repo.branchtags()[wc.branch()]
473 except KeyError:
473 except KeyError:
474 if wc.branch() == "default": # no default branch!
474 if wc.branch() == "default": # no default branch!
475 node = repo.lookup("tip") # update to tip
475 node = repo.lookup("tip") # update to tip
476 else:
476 else:
477 raise util.Abort(_("branch %s not found") % wc.branch())
477 raise util.Abort(_("branch %s not found") % wc.branch())
478 overwrite = force and not branchmerge
478 overwrite = force and not branchmerge
479 pl = wc.parents()
479 pl = wc.parents()
480 p1, p2 = pl[0], repo[node]
480 p1, p2 = pl[0], repo[node]
481 pa = p1.ancestor(p2)
481 pa = p1.ancestor(p2)
482 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
482 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
483 fastforward = False
483 fastforward = False
484
484
485 ### check phase
485 ### check phase
486 if not overwrite and len(pl) > 1:
486 if not overwrite and len(pl) > 1:
487 raise util.Abort(_("outstanding uncommitted merges"))
487 raise util.Abort(_("outstanding uncommitted merges"))
488 if branchmerge:
488 if branchmerge:
489 if pa == p2:
489 if pa == p2:
490 raise util.Abort(_("merging with a working directory ancestor"
490 raise util.Abort(_("merging with a working directory ancestor"
491 " has no effect"))
491 " has no effect"))
492 elif pa == p1:
492 elif pa == p1:
493 if p1.branch() != p2.branch():
493 if p1.branch() != p2.branch():
494 fastforward = True
494 fastforward = True
495 else:
495 else:
496 raise util.Abort(_("nothing to merge (use 'hg update'"
496 raise util.Abort(_("nothing to merge (use 'hg update'"
497 " or check 'hg heads')"))
497 " or check 'hg heads')"))
498 if not force and (wc.files() or wc.deleted()):
498 if not force and (wc.files() or wc.deleted()):
499 raise util.Abort(_("outstanding uncommitted changes "
499 raise util.Abort(_("outstanding uncommitted changes "
500 "(use 'hg status' to list changes)"))
500 "(use 'hg status' to list changes)"))
501 elif not overwrite:
501 elif not overwrite:
502 if pa == p1 or pa == p2: # linear
502 if pa == p1 or pa == p2: # linear
503 pass # all good
503 pass # all good
504 elif wc.files() or wc.deleted():
504 elif wc.files() or wc.deleted():
505 raise util.Abort(_("crosses branches (use 'hg merge' to merge "
505 raise util.Abort(_("crosses branches (merge branches or use"
506 "or use 'hg update -C' to discard changes)"))
506 " --clean to discard changes)"))
507 elif onode is None:
507 elif onode is None:
508 raise util.Abort(_("crosses branches (use 'hg merge' or use "
508 raise util.Abort(_("crosses branches (merge branches or use"
509 "'hg update -c')"))
509 " --check to force update)"))
510 else:
510 else:
511 # Allow jumping branches if clean and specific rev given
511 # Allow jumping branches if clean and specific rev given
512 overwrite = True
512 overwrite = True
513
513
514 ### calculate phase
514 ### calculate phase
515 action = []
515 action = []
516 wc.status(unknown=True) # prime cache
516 wc.status(unknown=True) # prime cache
517 if not force:
517 if not force:
518 _checkunknown(wc, p2)
518 _checkunknown(wc, p2)
519 if not util.checkcase(repo.path):
519 if not util.checkcase(repo.path):
520 _checkcollision(p2)
520 _checkcollision(p2)
521 action += _forgetremoved(wc, p2, branchmerge)
521 action += _forgetremoved(wc, p2, branchmerge)
522 action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
522 action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
523
523
524 ### apply phase
524 ### apply phase
525 if not branchmerge: # just jump to the new rev
525 if not branchmerge: # just jump to the new rev
526 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
526 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
527 if not partial:
527 if not partial:
528 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
528 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
529
529
530 stats = applyupdates(repo, action, wc, p2, pa)
530 stats = applyupdates(repo, action, wc, p2, pa)
531
531
532 if not partial:
532 if not partial:
533 repo.dirstate.setparents(fp1, fp2)
533 repo.dirstate.setparents(fp1, fp2)
534 recordupdates(repo, action, branchmerge)
534 recordupdates(repo, action, branchmerge)
535 if not branchmerge and not fastforward:
535 if not branchmerge and not fastforward:
536 repo.dirstate.setbranch(p2.branch())
536 repo.dirstate.setbranch(p2.branch())
537 finally:
537 finally:
538 wlock.release()
538 wlock.release()
539
539
540 if not partial:
540 if not partial:
541 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
541 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
542 return stats
542 return stats
@@ -1,37 +1,37 b''
1 $ hg init
1 $ hg init
2 $ echo This is file a1 > a
2 $ echo This is file a1 > a
3 $ echo This is file b1 > b
3 $ echo This is file b1 > b
4 $ hg add a b
4 $ hg add a b
5 $ hg commit -m "commit #0"
5 $ hg commit -m "commit #0"
6 $ echo This is file b22 > b
6 $ echo This is file b22 > b
7 $ hg commit -m "comment #1"
7 $ hg commit -m "comment #1"
8 $ hg update 0
8 $ hg update 0
9 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
9 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
10 $ rm b
10 $ rm b
11 $ hg commit -A -m "comment #2"
11 $ hg commit -A -m "comment #2"
12 removing b
12 removing b
13 created new head
13 created new head
14 $ hg update 1
14 $ hg update 1
15 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
15 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
16 $ hg update
16 $ hg update
17 abort: crosses branches (use 'hg merge' or use 'hg update -c')
17 abort: crosses branches (merge branches or use --check to force update)
18 [255]
18 [255]
19 $ hg update -c
19 $ hg update -c
20 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
20 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
21 $ mv a c
21 $ mv a c
22
22
23 In theory, we shouldn't need the "-y" below, but it prevents this test
23 In theory, we shouldn't need the "-y" below, but it prevents this test
24 from hanging when "hg update" erroneously prompts the user for "keep
24 from hanging when "hg update" erroneously prompts the user for "keep
25 or delete".
25 or delete".
26
26
27 Should abort:
27 Should abort:
28
28
29 $ hg update -y 1
29 $ hg update -y 1
30 abort: crosses branches (use 'hg merge' to merge or use 'hg update -C' to discard changes)
30 abort: crosses branches (merge branches or use --clean to discard changes)
31 [255]
31 [255]
32 $ mv c a
32 $ mv c a
33
33
34 Should succeed:
34 Should succeed:
35
35
36 $ hg update -y 1
36 $ hg update -y 1
37 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
37 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -1,235 +1,235 b''
1 $ HGMERGE=true; export HGMERGE
1 $ HGMERGE=true; export HGMERGE
2
2
3 $ mkdir r1
3 $ mkdir r1
4 $ cd r1
4 $ cd r1
5 $ hg init
5 $ hg init
6 $ echo a > a
6 $ echo a > a
7 $ hg addremove
7 $ hg addremove
8 adding a
8 adding a
9 $ hg commit -m "1"
9 $ hg commit -m "1"
10
10
11 $ hg clone . ../r2
11 $ hg clone . ../r2
12 updating to branch default
12 updating to branch default
13 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
13 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
14 $ cd ../r2
14 $ cd ../r2
15 $ hg up
15 $ hg up
16 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
16 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
17 $ echo abc > a
17 $ echo abc > a
18 $ hg diff --nodates
18 $ hg diff --nodates
19 diff -r c19d34741b0a a
19 diff -r c19d34741b0a a
20 --- a/a
20 --- a/a
21 +++ b/a
21 +++ b/a
22 @@ -1,1 +1,1 @@
22 @@ -1,1 +1,1 @@
23 -a
23 -a
24 +abc
24 +abc
25
25
26 $ cd ../r1
26 $ cd ../r1
27 $ echo b > b
27 $ echo b > b
28 $ echo a2 > a
28 $ echo a2 > a
29 $ hg addremove
29 $ hg addremove
30 adding b
30 adding b
31 $ hg commit -m "2"
31 $ hg commit -m "2"
32
32
33 $ cd ../r2
33 $ cd ../r2
34 $ hg -q pull ../r1
34 $ hg -q pull ../r1
35 $ hg status
35 $ hg status
36 M a
36 M a
37 $ hg parents
37 $ hg parents
38 changeset: 0:c19d34741b0a
38 changeset: 0:c19d34741b0a
39 user: test
39 user: test
40 date: Thu Jan 01 00:00:00 1970 +0000
40 date: Thu Jan 01 00:00:00 1970 +0000
41 summary: 1
41 summary: 1
42
42
43 $ hg --debug up
43 $ hg --debug up
44 searching for copies back to rev 1
44 searching for copies back to rev 1
45 unmatched files in other:
45 unmatched files in other:
46 b
46 b
47 resolving manifests
47 resolving manifests
48 overwrite False partial False
48 overwrite False partial False
49 ancestor c19d34741b0a local c19d34741b0a+ remote 1e71731e6fbb
49 ancestor c19d34741b0a local c19d34741b0a+ remote 1e71731e6fbb
50 a: versions differ -> m
50 a: versions differ -> m
51 b: remote created -> g
51 b: remote created -> g
52 preserving a for resolve of a
52 preserving a for resolve of a
53 updating: a 1/2 files (50.00%)
53 updating: a 1/2 files (50.00%)
54 picked tool 'true' for a (binary False symlink False)
54 picked tool 'true' for a (binary False symlink False)
55 merging a
55 merging a
56 my a@c19d34741b0a+ other a@1e71731e6fbb ancestor a@c19d34741b0a
56 my a@c19d34741b0a+ other a@1e71731e6fbb ancestor a@c19d34741b0a
57 updating: b 2/2 files (100.00%)
57 updating: b 2/2 files (100.00%)
58 getting b
58 getting b
59 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
59 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
60 $ hg parents
60 $ hg parents
61 changeset: 1:1e71731e6fbb
61 changeset: 1:1e71731e6fbb
62 tag: tip
62 tag: tip
63 user: test
63 user: test
64 date: Thu Jan 01 00:00:00 1970 +0000
64 date: Thu Jan 01 00:00:00 1970 +0000
65 summary: 2
65 summary: 2
66
66
67 $ hg --debug up 0
67 $ hg --debug up 0
68 resolving manifests
68 resolving manifests
69 overwrite False partial False
69 overwrite False partial False
70 ancestor 1e71731e6fbb local 1e71731e6fbb+ remote c19d34741b0a
70 ancestor 1e71731e6fbb local 1e71731e6fbb+ remote c19d34741b0a
71 a: versions differ -> m
71 a: versions differ -> m
72 b: other deleted -> r
72 b: other deleted -> r
73 preserving a for resolve of a
73 preserving a for resolve of a
74 updating: b 1/2 files (50.00%)
74 updating: b 1/2 files (50.00%)
75 removing b
75 removing b
76 updating: a 2/2 files (100.00%)
76 updating: a 2/2 files (100.00%)
77 picked tool 'true' for a (binary False symlink False)
77 picked tool 'true' for a (binary False symlink False)
78 merging a
78 merging a
79 my a@1e71731e6fbb+ other a@c19d34741b0a ancestor a@1e71731e6fbb
79 my a@1e71731e6fbb+ other a@c19d34741b0a ancestor a@1e71731e6fbb
80 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
80 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
81 $ hg parents
81 $ hg parents
82 changeset: 0:c19d34741b0a
82 changeset: 0:c19d34741b0a
83 user: test
83 user: test
84 date: Thu Jan 01 00:00:00 1970 +0000
84 date: Thu Jan 01 00:00:00 1970 +0000
85 summary: 1
85 summary: 1
86
86
87 $ hg --debug merge
87 $ hg --debug merge
88 abort: there is nothing to merge - use "hg update" instead
88 abort: there is nothing to merge - use "hg update" instead
89 [255]
89 [255]
90 $ hg parents
90 $ hg parents
91 changeset: 0:c19d34741b0a
91 changeset: 0:c19d34741b0a
92 user: test
92 user: test
93 date: Thu Jan 01 00:00:00 1970 +0000
93 date: Thu Jan 01 00:00:00 1970 +0000
94 summary: 1
94 summary: 1
95
95
96 $ hg --debug up
96 $ hg --debug up
97 searching for copies back to rev 1
97 searching for copies back to rev 1
98 unmatched files in other:
98 unmatched files in other:
99 b
99 b
100 resolving manifests
100 resolving manifests
101 overwrite False partial False
101 overwrite False partial False
102 ancestor c19d34741b0a local c19d34741b0a+ remote 1e71731e6fbb
102 ancestor c19d34741b0a local c19d34741b0a+ remote 1e71731e6fbb
103 a: versions differ -> m
103 a: versions differ -> m
104 b: remote created -> g
104 b: remote created -> g
105 preserving a for resolve of a
105 preserving a for resolve of a
106 updating: a 1/2 files (50.00%)
106 updating: a 1/2 files (50.00%)
107 picked tool 'true' for a (binary False symlink False)
107 picked tool 'true' for a (binary False symlink False)
108 merging a
108 merging a
109 my a@c19d34741b0a+ other a@1e71731e6fbb ancestor a@c19d34741b0a
109 my a@c19d34741b0a+ other a@1e71731e6fbb ancestor a@c19d34741b0a
110 updating: b 2/2 files (100.00%)
110 updating: b 2/2 files (100.00%)
111 getting b
111 getting b
112 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
112 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
113 $ hg parents
113 $ hg parents
114 changeset: 1:1e71731e6fbb
114 changeset: 1:1e71731e6fbb
115 tag: tip
115 tag: tip
116 user: test
116 user: test
117 date: Thu Jan 01 00:00:00 1970 +0000
117 date: Thu Jan 01 00:00:00 1970 +0000
118 summary: 2
118 summary: 2
119
119
120 $ hg -v history
120 $ hg -v history
121 changeset: 1:1e71731e6fbb
121 changeset: 1:1e71731e6fbb
122 tag: tip
122 tag: tip
123 user: test
123 user: test
124 date: Thu Jan 01 00:00:00 1970 +0000
124 date: Thu Jan 01 00:00:00 1970 +0000
125 files: a b
125 files: a b
126 description:
126 description:
127 2
127 2
128
128
129
129
130 changeset: 0:c19d34741b0a
130 changeset: 0:c19d34741b0a
131 user: test
131 user: test
132 date: Thu Jan 01 00:00:00 1970 +0000
132 date: Thu Jan 01 00:00:00 1970 +0000
133 files: a
133 files: a
134 description:
134 description:
135 1
135 1
136
136
137
137
138 $ hg diff --nodates
138 $ hg diff --nodates
139 diff -r 1e71731e6fbb a
139 diff -r 1e71731e6fbb a
140 --- a/a
140 --- a/a
141 +++ b/a
141 +++ b/a
142 @@ -1,1 +1,1 @@
142 @@ -1,1 +1,1 @@
143 -a2
143 -a2
144 +abc
144 +abc
145
145
146
146
147 create a second head
147 create a second head
148
148
149 $ cd ../r1
149 $ cd ../r1
150 $ hg up 0
150 $ hg up 0
151 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
151 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
152 $ echo b2 > b
152 $ echo b2 > b
153 $ echo a3 > a
153 $ echo a3 > a
154 $ hg addremove
154 $ hg addremove
155 adding b
155 adding b
156 $ hg commit -m "3"
156 $ hg commit -m "3"
157 created new head
157 created new head
158
158
159 $ cd ../r2
159 $ cd ../r2
160 $ hg -q pull ../r1
160 $ hg -q pull ../r1
161 $ hg status
161 $ hg status
162 M a
162 M a
163 $ hg parents
163 $ hg parents
164 changeset: 1:1e71731e6fbb
164 changeset: 1:1e71731e6fbb
165 user: test
165 user: test
166 date: Thu Jan 01 00:00:00 1970 +0000
166 date: Thu Jan 01 00:00:00 1970 +0000
167 summary: 2
167 summary: 2
168
168
169 $ hg --debug up
169 $ hg --debug up
170 abort: crosses branches (use 'hg merge' to merge or use 'hg update -C' to discard changes)
170 abort: crosses branches (merge branches or use --clean to discard changes)
171 [255]
171 [255]
172 $ hg --debug merge
172 $ hg --debug merge
173 abort: outstanding uncommitted changes (use 'hg status' to list changes)
173 abort: outstanding uncommitted changes (use 'hg status' to list changes)
174 [255]
174 [255]
175 $ hg --debug merge -f
175 $ hg --debug merge -f
176 searching for copies back to rev 1
176 searching for copies back to rev 1
177 resolving manifests
177 resolving manifests
178 overwrite False partial False
178 overwrite False partial False
179 ancestor c19d34741b0a local 1e71731e6fbb+ remote 83c51d0caff4
179 ancestor c19d34741b0a local 1e71731e6fbb+ remote 83c51d0caff4
180 a: versions differ -> m
180 a: versions differ -> m
181 b: versions differ -> m
181 b: versions differ -> m
182 preserving a for resolve of a
182 preserving a for resolve of a
183 preserving b for resolve of b
183 preserving b for resolve of b
184 updating: a 1/2 files (50.00%)
184 updating: a 1/2 files (50.00%)
185 picked tool 'true' for a (binary False symlink False)
185 picked tool 'true' for a (binary False symlink False)
186 merging a
186 merging a
187 my a@1e71731e6fbb+ other a@83c51d0caff4 ancestor a@c19d34741b0a
187 my a@1e71731e6fbb+ other a@83c51d0caff4 ancestor a@c19d34741b0a
188 updating: b 2/2 files (100.00%)
188 updating: b 2/2 files (100.00%)
189 picked tool 'true' for b (binary False symlink False)
189 picked tool 'true' for b (binary False symlink False)
190 merging b
190 merging b
191 my b@1e71731e6fbb+ other b@83c51d0caff4 ancestor b@000000000000
191 my b@1e71731e6fbb+ other b@83c51d0caff4 ancestor b@000000000000
192 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
192 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
193 (branch merge, don't forget to commit)
193 (branch merge, don't forget to commit)
194 $ hg parents
194 $ hg parents
195 changeset: 1:1e71731e6fbb
195 changeset: 1:1e71731e6fbb
196 user: test
196 user: test
197 date: Thu Jan 01 00:00:00 1970 +0000
197 date: Thu Jan 01 00:00:00 1970 +0000
198 summary: 2
198 summary: 2
199
199
200 changeset: 2:83c51d0caff4
200 changeset: 2:83c51d0caff4
201 tag: tip
201 tag: tip
202 parent: 0:c19d34741b0a
202 parent: 0:c19d34741b0a
203 user: test
203 user: test
204 date: Thu Jan 01 00:00:00 1970 +0000
204 date: Thu Jan 01 00:00:00 1970 +0000
205 summary: 3
205 summary: 3
206
206
207 $ hg diff --nodates
207 $ hg diff --nodates
208 diff -r 1e71731e6fbb a
208 diff -r 1e71731e6fbb a
209 --- a/a
209 --- a/a
210 +++ b/a
210 +++ b/a
211 @@ -1,1 +1,1 @@
211 @@ -1,1 +1,1 @@
212 -a2
212 -a2
213 +abc
213 +abc
214
214
215
215
216 test a local add
216 test a local add
217
217
218 $ cd ..
218 $ cd ..
219 $ hg init a
219 $ hg init a
220 $ hg init b
220 $ hg init b
221 $ echo a > a/a
221 $ echo a > a/a
222 $ echo a > b/a
222 $ echo a > b/a
223 $ hg --cwd a commit -A -m a
223 $ hg --cwd a commit -A -m a
224 adding a
224 adding a
225 $ cd b
225 $ cd b
226 $ hg add a
226 $ hg add a
227 $ hg pull -u ../a
227 $ hg pull -u ../a
228 pulling from ../a
228 pulling from ../a
229 requesting all changes
229 requesting all changes
230 adding changesets
230 adding changesets
231 adding manifests
231 adding manifests
232 adding file changes
232 adding file changes
233 added 1 changesets with 1 changes to 1 files
233 added 1 changesets with 1 changes to 1 files
234 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
234 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
235 $ hg st
235 $ hg st
@@ -1,130 +1,130 b''
1 # Construct the following history tree:
1 # Construct the following history tree:
2 #
2 #
3 # @ 5:e1bb631146ca b1
3 # @ 5:e1bb631146ca b1
4 # |
4 # |
5 # o 4:a4fdb3b883c4 0:b608b9236435 b1
5 # o 4:a4fdb3b883c4 0:b608b9236435 b1
6 # |
6 # |
7 # | o 3:4b57d2520816 1:44592833ba9f
7 # | o 3:4b57d2520816 1:44592833ba9f
8 # | |
8 # | |
9 # | | o 2:063f31070f65
9 # | | o 2:063f31070f65
10 # | |/
10 # | |/
11 # | o 1:44592833ba9f
11 # | o 1:44592833ba9f
12 # |/
12 # |/
13 # o 0:b608b9236435
13 # o 0:b608b9236435
14
14
15 $ hg init
15 $ hg init
16 $ echo foo > foo
16 $ echo foo > foo
17 $ echo zero > a
17 $ echo zero > a
18 $ hg ci -qAm0
18 $ hg ci -qAm0
19 $ echo one > a ; hg ci -m1
19 $ echo one > a ; hg ci -m1
20 $ echo two > a ; hg ci -m2
20 $ echo two > a ; hg ci -m2
21 $ hg up -q 1
21 $ hg up -q 1
22 $ echo three > a ; hg ci -qm3
22 $ echo three > a ; hg ci -qm3
23 $ hg up -q 0
23 $ hg up -q 0
24 $ hg branch -q b1
24 $ hg branch -q b1
25 $ echo four > a ; hg ci -qm4
25 $ echo four > a ; hg ci -qm4
26 $ echo five > a ; hg ci -qm5
26 $ echo five > a ; hg ci -qm5
27
27
28 Initial repo state:
28 Initial repo state:
29
29
30 $ hg --config 'extensions.graphlog=' \
30 $ hg --config 'extensions.graphlog=' \
31 > glog --template '{rev}:{node|short} {parents} {branches}\n'
31 > glog --template '{rev}:{node|short} {parents} {branches}\n'
32 @ 5:e1bb631146ca b1
32 @ 5:e1bb631146ca b1
33 |
33 |
34 o 4:a4fdb3b883c4 0:b608b9236435 b1
34 o 4:a4fdb3b883c4 0:b608b9236435 b1
35 |
35 |
36 | o 3:4b57d2520816 1:44592833ba9f
36 | o 3:4b57d2520816 1:44592833ba9f
37 | |
37 | |
38 | | o 2:063f31070f65
38 | | o 2:063f31070f65
39 | |/
39 | |/
40 | o 1:44592833ba9f
40 | o 1:44592833ba9f
41 |/
41 |/
42 o 0:b608b9236435
42 o 0:b608b9236435
43
43
44
44
45 Test helper functions:
45 Test helper functions:
46
46
47 $ revtest () {
47 $ revtest () {
48 > msg=$1
48 > msg=$1
49 > dirtyflag=$2 # 'clean' or 'dirty'
49 > dirtyflag=$2 # 'clean' or 'dirty'
50 > startrev=$3
50 > startrev=$3
51 > targetrev=$4
51 > targetrev=$4
52 > opt=$5
52 > opt=$5
53 > hg up -qC $startrev
53 > hg up -qC $startrev
54 > test $dirtyflag = dirty && echo dirty > foo
54 > test $dirtyflag = dirty && echo dirty > foo
55 > hg up $opt $targetrev
55 > hg up $opt $targetrev
56 > hg parent --template 'parent={rev}\n'
56 > hg parent --template 'parent={rev}\n'
57 > hg stat
57 > hg stat
58 > }
58 > }
59
59
60 $ norevtest () {
60 $ norevtest () {
61 > msg=$1
61 > msg=$1
62 > dirtyflag=$2 # 'clean' or 'dirty'
62 > dirtyflag=$2 # 'clean' or 'dirty'
63 > startrev=$3
63 > startrev=$3
64 > opt=$4
64 > opt=$4
65 > hg up -qC $startrev
65 > hg up -qC $startrev
66 > test $dirtyflag = dirty && echo dirty > foo
66 > test $dirtyflag = dirty && echo dirty > foo
67 > hg up $opt
67 > hg up $opt
68 > hg parent --template 'parent={rev}\n'
68 > hg parent --template 'parent={rev}\n'
69 > hg stat
69 > hg stat
70 > }
70 > }
71
71
72 Test cases are documented in a table in the update function of merge.py.
72 Test cases are documented in a table in the update function of merge.py.
73 Cases are run as shown in that table, row by row.
73 Cases are run as shown in that table, row by row.
74
74
75 $ norevtest 'none clean linear' clean 4
75 $ norevtest 'none clean linear' clean 4
76 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
76 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
77 parent=5
77 parent=5
78
78
79 $ norevtest 'none clean same' clean 2
79 $ norevtest 'none clean same' clean 2
80 abort: crosses branches (use 'hg merge' or use 'hg update -c')
80 abort: crosses branches (merge branches or use --check to force update)
81 parent=2
81 parent=2
82
82
83
83
84 $ revtest 'none clean linear' clean 1 2
84 $ revtest 'none clean linear' clean 1 2
85 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
85 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
86 parent=2
86 parent=2
87
87
88 $ revtest 'none clean same' clean 2 3
88 $ revtest 'none clean same' clean 2 3
89 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
89 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 parent=3
90 parent=3
91
91
92 $ revtest 'none clean cross' clean 3 4
92 $ revtest 'none clean cross' clean 3 4
93 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
93 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
94 parent=4
94 parent=4
95
95
96
96
97 $ revtest 'none dirty linear' dirty 1 2
97 $ revtest 'none dirty linear' dirty 1 2
98 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
99 parent=2
99 parent=2
100 M foo
100 M foo
101
101
102 $ revtest 'none dirty same' dirty 2 3
102 $ revtest 'none dirty same' dirty 2 3
103 abort: crosses branches (use 'hg merge' to merge or use 'hg update -C' to discard changes)
103 abort: crosses branches (merge branches or use --clean to discard changes)
104 parent=2
104 parent=2
105 M foo
105 M foo
106
106
107 $ revtest 'none dirty cross' dirty 3 4
107 $ revtest 'none dirty cross' dirty 3 4
108 abort: crosses branches (use 'hg merge' to merge or use 'hg update -C' to discard changes)
108 abort: crosses branches (merge branches or use --clean to discard changes)
109 parent=3
109 parent=3
110 M foo
110 M foo
111
111
112
112
113 $ revtest '-C dirty linear' dirty 1 2 -C
113 $ revtest '-C dirty linear' dirty 1 2 -C
114 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
114 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
115 parent=2
115 parent=2
116
116
117 $ revtest '-c dirty linear' dirty 1 2 -c
117 $ revtest '-c dirty linear' dirty 1 2 -c
118 abort: uncommitted local changes
118 abort: uncommitted local changes
119 parent=1
119 parent=1
120 M foo
120 M foo
121
121
122 $ norevtest '-c clean same' clean 2 -c
122 $ norevtest '-c clean same' clean 2 -c
123 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
123 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
124 parent=3
124 parent=3
125
125
126 $ revtest '-cC dirty linear' dirty 1 2 -cC
126 $ revtest '-cC dirty linear' dirty 1 2 -cC
127 abort: cannot specify both -c/--check and -C/--clean
127 abort: cannot specify both -c/--check and -C/--clean
128 parent=1
128 parent=1
129 M foo
129 M foo
130
130
General Comments 0
You need to be logged in to leave comments. Login now