##// END OF EJS Templates
merge: process files in sorted order
Mads Kiilerich -
r18360:760c0d67 default
parent child Browse files
Show More
@@ -1,636 +1,636 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 error, util, filemerge, copies, subrepo
10 import error, 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 f.close()
35 f.close()
36 except IOError, err:
36 except IOError, err:
37 if err.errno != errno.ENOENT:
37 if err.errno != errno.ENOENT:
38 raise
38 raise
39 self._dirty = False
39 self._dirty = False
40 def commit(self):
40 def commit(self):
41 if self._dirty:
41 if self._dirty:
42 f = self._repo.opener("merge/state", "w")
42 f = self._repo.opener("merge/state", "w")
43 f.write(hex(self._local) + "\n")
43 f.write(hex(self._local) + "\n")
44 for d, v in self._state.iteritems():
44 for d, v in self._state.iteritems():
45 f.write("\0".join([d] + v) + "\n")
45 f.write("\0".join([d] + v) + "\n")
46 f.close()
46 f.close()
47 self._dirty = False
47 self._dirty = False
48 def add(self, fcl, fco, fca, fd):
48 def add(self, fcl, fco, fca, fd):
49 hash = util.sha1(fcl.path()).hexdigest()
49 hash = util.sha1(fcl.path()).hexdigest()
50 self._repo.opener.write("merge/" + hash, fcl.data())
50 self._repo.opener.write("merge/" + hash, fcl.data())
51 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
51 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
52 hex(fca.filenode()), fco.path(), fcl.flags()]
52 hex(fca.filenode()), fco.path(), fcl.flags()]
53 self._dirty = True
53 self._dirty = True
54 def __contains__(self, dfile):
54 def __contains__(self, dfile):
55 return dfile in self._state
55 return dfile in self._state
56 def __getitem__(self, dfile):
56 def __getitem__(self, dfile):
57 return self._state[dfile][0]
57 return self._state[dfile][0]
58 def __iter__(self):
58 def __iter__(self):
59 l = self._state.keys()
59 l = self._state.keys()
60 l.sort()
60 l.sort()
61 for f in l:
61 for f in l:
62 yield f
62 yield f
63 def mark(self, dfile, state):
63 def mark(self, dfile, state):
64 self._state[dfile][0] = state
64 self._state[dfile][0] = state
65 self._dirty = True
65 self._dirty = True
66 def resolve(self, dfile, wctx, octx):
66 def resolve(self, dfile, wctx, octx):
67 if self[dfile] == 'r':
67 if self[dfile] == 'r':
68 return 0
68 return 0
69 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
69 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
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 # "premerge" x flags
73 # "premerge" x flags
74 flo = fco.flags()
74 flo = fco.flags()
75 fla = fca.flags()
75 fla = fca.flags()
76 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
76 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
77 if fca.node() == nullid:
77 if fca.node() == nullid:
78 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
78 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
79 afile)
79 afile)
80 elif flags == fla:
80 elif flags == fla:
81 flags = flo
81 flags = flo
82 # restore local
82 # restore local
83 f = self._repo.opener("merge/" + hash)
83 f = self._repo.opener("merge/" + hash)
84 self._repo.wwrite(dfile, f.read(), flags)
84 self._repo.wwrite(dfile, f.read(), flags)
85 f.close()
85 f.close()
86 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
86 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
87 if r is None:
87 if r is None:
88 # no real conflict
88 # no real conflict
89 del self._state[dfile]
89 del self._state[dfile]
90 elif not r:
90 elif not r:
91 self.mark(dfile, 'r')
91 self.mark(dfile, 'r')
92 return r
92 return r
93
93
94 def _checkunknownfile(repo, wctx, mctx, f):
94 def _checkunknownfile(repo, wctx, mctx, f):
95 return (not repo.dirstate._ignore(f)
95 return (not repo.dirstate._ignore(f)
96 and os.path.isfile(repo.wjoin(f))
96 and os.path.isfile(repo.wjoin(f))
97 and repo.dirstate.normalize(f) not in repo.dirstate
97 and repo.dirstate.normalize(f) not in repo.dirstate
98 and mctx[f].cmp(wctx[f]))
98 and mctx[f].cmp(wctx[f]))
99
99
100 def _checkunknown(repo, wctx, mctx):
100 def _checkunknown(repo, wctx, mctx):
101 "check for collisions between unknown files and files in mctx"
101 "check for collisions between unknown files and files in mctx"
102
102
103 error = False
103 error = False
104 for f in mctx:
104 for f in mctx:
105 if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
105 if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
106 error = True
106 error = True
107 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
107 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
108 if error:
108 if error:
109 raise util.Abort(_("untracked files in working directory differ "
109 raise util.Abort(_("untracked files in working directory differ "
110 "from files in requested revision"))
110 "from files in requested revision"))
111
111
112 def _remains(f, m, ma, workingctx=False):
112 def _remains(f, m, ma, workingctx=False):
113 """check whether specified file remains after merge.
113 """check whether specified file remains after merge.
114
114
115 It is assumed that specified file is not contained in the manifest
115 It is assumed that specified file is not contained in the manifest
116 of the other context.
116 of the other context.
117 """
117 """
118 if f in ma:
118 if f in ma:
119 n = m[f]
119 n = m[f]
120 if n != ma[f]:
120 if n != ma[f]:
121 return True # because it is changed locally
121 return True # because it is changed locally
122 # even though it doesn't remain, if "remote deleted" is
122 # even though it doesn't remain, if "remote deleted" is
123 # chosen in manifestmerge()
123 # chosen in manifestmerge()
124 elif workingctx and n[20:] == "a":
124 elif workingctx and n[20:] == "a":
125 return True # because it is added locally (linear merge specific)
125 return True # because it is added locally (linear merge specific)
126 else:
126 else:
127 return False # because it is removed remotely
127 return False # because it is removed remotely
128 else:
128 else:
129 return True # because it is added locally
129 return True # because it is added locally
130
130
131 def _checkcollision(mctx, extractxs):
131 def _checkcollision(mctx, extractxs):
132 "check for case folding collisions in the destination context"
132 "check for case folding collisions in the destination context"
133 folded = {}
133 folded = {}
134 for fn in mctx:
134 for fn in mctx:
135 fold = util.normcase(fn)
135 fold = util.normcase(fn)
136 if fold in folded:
136 if fold in folded:
137 raise util.Abort(_("case-folding collision between %s and %s")
137 raise util.Abort(_("case-folding collision between %s and %s")
138 % (fn, folded[fold]))
138 % (fn, folded[fold]))
139 folded[fold] = fn
139 folded[fold] = fn
140
140
141 if extractxs:
141 if extractxs:
142 wctx, actx = extractxs
142 wctx, actx = extractxs
143 # class to delay looking up copy mapping
143 # class to delay looking up copy mapping
144 class pathcopies(object):
144 class pathcopies(object):
145 @util.propertycache
145 @util.propertycache
146 def map(self):
146 def map(self):
147 # {dst@mctx: src@wctx} copy mapping
147 # {dst@mctx: src@wctx} copy mapping
148 return copies.pathcopies(wctx, mctx)
148 return copies.pathcopies(wctx, mctx)
149 pc = pathcopies()
149 pc = pathcopies()
150
150
151 for fn in wctx:
151 for fn in wctx:
152 fold = util.normcase(fn)
152 fold = util.normcase(fn)
153 mfn = folded.get(fold, None)
153 mfn = folded.get(fold, None)
154 if (mfn and mfn != fn and pc.map.get(mfn) != fn and
154 if (mfn and mfn != fn and pc.map.get(mfn) != fn and
155 _remains(fn, wctx.manifest(), actx.manifest(), True) and
155 _remains(fn, wctx.manifest(), actx.manifest(), True) and
156 _remains(mfn, mctx.manifest(), actx.manifest())):
156 _remains(mfn, mctx.manifest(), actx.manifest())):
157 raise util.Abort(_("case-folding collision between %s and %s")
157 raise util.Abort(_("case-folding collision between %s and %s")
158 % (mfn, fn))
158 % (mfn, fn))
159
159
160 def _forgetremoved(wctx, mctx, branchmerge):
160 def _forgetremoved(wctx, mctx, branchmerge):
161 """
161 """
162 Forget removed files
162 Forget removed files
163
163
164 If we're jumping between revisions (as opposed to merging), and if
164 If we're jumping between revisions (as opposed to merging), and if
165 neither the working directory nor the target rev has the file,
165 neither the working directory nor the target rev has the file,
166 then we need to remove it from the dirstate, to prevent the
166 then we need to remove it from the dirstate, to prevent the
167 dirstate from listing the file when it is no longer in the
167 dirstate from listing the file when it is no longer in the
168 manifest.
168 manifest.
169
169
170 If we're merging, and the other revision has removed a file
170 If we're merging, and the other revision has removed a file
171 that is not present in the working directory, we need to mark it
171 that is not present in the working directory, we need to mark it
172 as removed.
172 as removed.
173 """
173 """
174
174
175 actions = []
175 actions = []
176 state = branchmerge and 'r' or 'f'
176 state = branchmerge and 'r' or 'f'
177 for f in wctx.deleted():
177 for f in wctx.deleted():
178 if f not in mctx:
178 if f not in mctx:
179 actions.append((f, state))
179 actions.append((f, state))
180
180
181 if not branchmerge:
181 if not branchmerge:
182 for f in wctx.removed():
182 for f in wctx.removed():
183 if f not in mctx:
183 if f not in mctx:
184 actions.append((f, "f"))
184 actions.append((f, "f"))
185
185
186 return actions
186 return actions
187
187
188 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
188 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
189 """
189 """
190 Merge p1 and p2 with ancestor pa and generate merge action list
190 Merge p1 and p2 with ancestor pa and generate merge action list
191
191
192 overwrite = whether we clobber working files
192 overwrite = whether we clobber working files
193 partial = function to filter file lists
193 partial = function to filter file lists
194 """
194 """
195
195
196 def act(msg, m, f, *args):
196 def act(msg, m, f, *args):
197 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
197 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
198 actions.append((f, m) + args)
198 actions.append((f, m) + args)
199
199
200 actions, copy, movewithdir = [], {}, {}
200 actions, copy, movewithdir = [], {}, {}
201
201
202 if overwrite:
202 if overwrite:
203 pa = p1
203 pa = p1
204 elif pa == p2: # backwards
204 elif pa == p2: # backwards
205 pa = p1.p1()
205 pa = p1.p1()
206 elif pa and repo.ui.configbool("merge", "followcopies", True):
206 elif pa and repo.ui.configbool("merge", "followcopies", True):
207 ret = copies.mergecopies(repo, p1, p2, pa)
207 ret = copies.mergecopies(repo, p1, p2, pa)
208 copy, movewithdir, diverge, renamedelete = ret
208 copy, movewithdir, diverge, renamedelete = ret
209 for of, fl in diverge.iteritems():
209 for of, fl in diverge.iteritems():
210 act("divergent renames", "dr", of, fl)
210 act("divergent renames", "dr", of, fl)
211 for of, fl in renamedelete.iteritems():
211 for of, fl in renamedelete.iteritems():
212 act("rename and delete", "rd", of, fl)
212 act("rename and delete", "rd", of, fl)
213
213
214 repo.ui.note(_("resolving manifests\n"))
214 repo.ui.note(_("resolving manifests\n"))
215 repo.ui.debug(" overwrite: %s, partial: %s\n"
215 repo.ui.debug(" overwrite: %s, partial: %s\n"
216 % (bool(overwrite), bool(partial)))
216 % (bool(overwrite), bool(partial)))
217 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, p1, p2))
217 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, p1, p2))
218
218
219 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
219 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
220 copied = set(copy.values())
220 copied = set(copy.values())
221 copied.update(movewithdir.values())
221 copied.update(movewithdir.values())
222
222
223 if '.hgsubstate' in m1:
223 if '.hgsubstate' in m1:
224 # check whether sub state is modified
224 # check whether sub state is modified
225 for s in p1.substate:
225 for s in p1.substate:
226 if p1.sub(s).dirty():
226 if p1.sub(s).dirty():
227 m1['.hgsubstate'] += "+"
227 m1['.hgsubstate'] += "+"
228 break
228 break
229
229
230 # Compare manifests
230 # Compare manifests
231 for f, n in m1.iteritems():
231 for f, n in sorted(m1.iteritems()):
232 if partial and not partial(f):
232 if partial and not partial(f):
233 continue
233 continue
234 if f in m2:
234 if f in m2:
235 n2 = m2[f]
235 n2 = m2[f]
236 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
236 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
237 nol = 'l' not in fl1 + fl2 + fla
237 nol = 'l' not in fl1 + fl2 + fla
238 a = ma.get(f, nullid)
238 a = ma.get(f, nullid)
239 if n == n2 and fl1 == fl2:
239 if n == n2 and fl1 == fl2:
240 pass # same - keep local
240 pass # same - keep local
241 elif n2 == a and fl2 == fla:
241 elif n2 == a and fl2 == fla:
242 pass # remote unchanged - keep local
242 pass # remote unchanged - keep local
243 elif n == a and fl1 == fla: # local unchanged - use remote
243 elif n == a and fl1 == fla: # local unchanged - use remote
244 if n == n2: # optimization: keep local content
244 if n == n2: # optimization: keep local content
245 act("update permissions", "e", f, fl2)
245 act("update permissions", "e", f, fl2)
246 else:
246 else:
247 act("remote is newer", "g", f, fl2)
247 act("remote is newer", "g", f, fl2)
248 elif nol and n2 == a: # remote only changed 'x'
248 elif nol and n2 == a: # remote only changed 'x'
249 act("update permissions", "e", f, fl2)
249 act("update permissions", "e", f, fl2)
250 elif nol and n == a: # local only changed 'x'
250 elif nol and n == a: # local only changed 'x'
251 act("remote is newer", "g", f, fl)
251 act("remote is newer", "g", f, fl)
252 else: # both changed something
252 else: # both changed something
253 act("versions differ", "m", f, f, f, False)
253 act("versions differ", "m", f, f, f, False)
254 elif f in copied: # files we'll deal with on m2 side
254 elif f in copied: # files we'll deal with on m2 side
255 pass
255 pass
256 elif f in movewithdir: # directory rename
256 elif f in movewithdir: # directory rename
257 f2 = movewithdir[f]
257 f2 = movewithdir[f]
258 act("remote renamed directory to " + f2, "d", f, None, f2,
258 act("remote renamed directory to " + f2, "d", f, None, f2,
259 m1.flags(f))
259 m1.flags(f))
260 elif f in copy:
260 elif f in copy:
261 f2 = copy[f]
261 f2 = copy[f]
262 act("local copied/moved to " + f2, "m", f, f2, f, False)
262 act("local copied/moved to " + f2, "m", f, f2, f, False)
263 elif f in ma: # clean, a different, no remote
263 elif f in ma: # clean, a different, no remote
264 if n != ma[f]:
264 if n != ma[f]:
265 if repo.ui.promptchoice(
265 if repo.ui.promptchoice(
266 _(" local changed %s which remote deleted\n"
266 _(" local changed %s which remote deleted\n"
267 "use (c)hanged version or (d)elete?") % f,
267 "use (c)hanged version or (d)elete?") % f,
268 (_("&Changed"), _("&Delete")), 0):
268 (_("&Changed"), _("&Delete")), 0):
269 act("prompt delete", "r", f)
269 act("prompt delete", "r", f)
270 else:
270 else:
271 act("prompt keep", "a", f)
271 act("prompt keep", "a", f)
272 elif n[20:] == "a": # added, no remote
272 elif n[20:] == "a": # added, no remote
273 act("remote deleted", "f", f)
273 act("remote deleted", "f", f)
274 else:
274 else:
275 act("other deleted", "r", f)
275 act("other deleted", "r", f)
276
276
277 for f, n in m2.iteritems():
277 for f, n in sorted(m2.iteritems()):
278 if partial and not partial(f):
278 if partial and not partial(f):
279 continue
279 continue
280 if f in m1 or f in copied: # files already visited
280 if f in m1 or f in copied: # files already visited
281 continue
281 continue
282 if f in movewithdir:
282 if f in movewithdir:
283 f2 = movewithdir[f]
283 f2 = movewithdir[f]
284 act("local renamed directory to " + f2, "d", None, f, f2,
284 act("local renamed directory to " + f2, "d", None, f, f2,
285 m2.flags(f))
285 m2.flags(f))
286 elif f in copy:
286 elif f in copy:
287 f2 = copy[f]
287 f2 = copy[f]
288 if f2 in m2:
288 if f2 in m2:
289 act("remote copied to " + f, "m",
289 act("remote copied to " + f, "m",
290 f2, f, f, False)
290 f2, f, f, False)
291 else:
291 else:
292 act("remote moved to " + f, "m",
292 act("remote moved to " + f, "m",
293 f2, f, f, True)
293 f2, f, f, True)
294 elif f not in ma:
294 elif f not in ma:
295 if (not overwrite
295 if (not overwrite
296 and _checkunknownfile(repo, p1, p2, f)):
296 and _checkunknownfile(repo, p1, p2, f)):
297 act("remote differs from untracked local",
297 act("remote differs from untracked local",
298 "m", f, f, f, False)
298 "m", f, f, f, False)
299 else:
299 else:
300 act("remote created", "g", f, m2.flags(f))
300 act("remote created", "g", f, m2.flags(f))
301 elif n != ma[f]:
301 elif n != ma[f]:
302 if repo.ui.promptchoice(
302 if repo.ui.promptchoice(
303 _("remote changed %s which local deleted\n"
303 _("remote changed %s which local deleted\n"
304 "use (c)hanged version or leave (d)eleted?") % f,
304 "use (c)hanged version or leave (d)eleted?") % f,
305 (_("&Changed"), _("&Deleted")), 0) == 0:
305 (_("&Changed"), _("&Deleted")), 0) == 0:
306 act("prompt recreating", "g", f, m2.flags(f))
306 act("prompt recreating", "g", f, m2.flags(f))
307
307
308 return actions
308 return actions
309
309
310 def actionkey(a):
310 def actionkey(a):
311 return a[1] == "r" and -1 or 0, a
311 return a[1] == "r" and -1 or 0, a
312
312
313 def applyupdates(repo, actions, wctx, mctx, actx, overwrite):
313 def applyupdates(repo, actions, wctx, mctx, actx, overwrite):
314 """apply the merge action list to the working directory
314 """apply the merge action list to the working directory
315
315
316 wctx is the working copy context
316 wctx is the working copy context
317 mctx is the context to be merged into the working copy
317 mctx is the context to be merged into the working copy
318 actx is the context of the common ancestor
318 actx is the context of the common ancestor
319
319
320 Return a tuple of counts (updated, merged, removed, unresolved) that
320 Return a tuple of counts (updated, merged, removed, unresolved) that
321 describes how many files were affected by the update.
321 describes how many files were affected by the update.
322 """
322 """
323
323
324 updated, merged, removed, unresolved = 0, 0, 0, 0
324 updated, merged, removed, unresolved = 0, 0, 0, 0
325 ms = mergestate(repo)
325 ms = mergestate(repo)
326 ms.reset(wctx.p1().node())
326 ms.reset(wctx.p1().node())
327 moves = []
327 moves = []
328 actions.sort(key=actionkey)
328 actions.sort(key=actionkey)
329
329
330 # prescan for merges
330 # prescan for merges
331 for a in actions:
331 for a in actions:
332 f, m = a[:2]
332 f, m = a[:2]
333 if m == "m": # merge
333 if m == "m": # merge
334 f2, fd, move = a[2:]
334 f2, fd, move = a[2:]
335 if fd == '.hgsubstate': # merged internally
335 if fd == '.hgsubstate': # merged internally
336 continue
336 continue
337 repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd))
337 repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd))
338 fcl = wctx[f]
338 fcl = wctx[f]
339 fco = mctx[f2]
339 fco = mctx[f2]
340 if mctx == actx: # backwards, use working dir parent as ancestor
340 if mctx == actx: # backwards, use working dir parent as ancestor
341 if fcl.parents():
341 if fcl.parents():
342 fca = fcl.p1()
342 fca = fcl.p1()
343 else:
343 else:
344 fca = repo.filectx(f, fileid=nullrev)
344 fca = repo.filectx(f, fileid=nullrev)
345 else:
345 else:
346 fca = fcl.ancestor(fco, actx)
346 fca = fcl.ancestor(fco, actx)
347 if not fca:
347 if not fca:
348 fca = repo.filectx(f, fileid=nullrev)
348 fca = repo.filectx(f, fileid=nullrev)
349 ms.add(fcl, fco, fca, fd)
349 ms.add(fcl, fco, fca, fd)
350 if f != fd and move:
350 if f != fd and move:
351 moves.append(f)
351 moves.append(f)
352
352
353 audit = repo.wopener.audit
353 audit = repo.wopener.audit
354
354
355 # remove renamed files after safely stored
355 # remove renamed files after safely stored
356 for f in moves:
356 for f in moves:
357 if os.path.lexists(repo.wjoin(f)):
357 if os.path.lexists(repo.wjoin(f)):
358 repo.ui.debug("removing %s\n" % f)
358 repo.ui.debug("removing %s\n" % f)
359 audit(f)
359 audit(f)
360 util.unlinkpath(repo.wjoin(f))
360 util.unlinkpath(repo.wjoin(f))
361
361
362 numupdates = len(actions)
362 numupdates = len(actions)
363 for i, a in enumerate(actions):
363 for i, a in enumerate(actions):
364 f, m = a[:2]
364 f, m = a[:2]
365 repo.ui.progress(_('updating'), i + 1, item=f, total=numupdates,
365 repo.ui.progress(_('updating'), i + 1, item=f, total=numupdates,
366 unit=_('files'))
366 unit=_('files'))
367 if m == "r": # remove
367 if m == "r": # remove
368 repo.ui.note(_("removing %s\n") % f)
368 repo.ui.note(_("removing %s\n") % f)
369 audit(f)
369 audit(f)
370 if f == '.hgsubstate': # subrepo states need updating
370 if f == '.hgsubstate': # subrepo states need updating
371 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
371 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
372 try:
372 try:
373 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
373 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
374 except OSError, inst:
374 except OSError, inst:
375 repo.ui.warn(_("update failed to remove %s: %s!\n") %
375 repo.ui.warn(_("update failed to remove %s: %s!\n") %
376 (f, inst.strerror))
376 (f, inst.strerror))
377 removed += 1
377 removed += 1
378 elif m == "m": # merge
378 elif m == "m": # merge
379 if fd == '.hgsubstate': # subrepo states need updating
379 if fd == '.hgsubstate': # subrepo states need updating
380 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
380 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
381 overwrite)
381 overwrite)
382 continue
382 continue
383 f2, fd, move = a[2:]
383 f2, fd, move = a[2:]
384 audit(fd)
384 audit(fd)
385 r = ms.resolve(fd, wctx, mctx)
385 r = ms.resolve(fd, wctx, mctx)
386 if r is not None and r > 0:
386 if r is not None and r > 0:
387 unresolved += 1
387 unresolved += 1
388 else:
388 else:
389 if r is None:
389 if r is None:
390 updated += 1
390 updated += 1
391 else:
391 else:
392 merged += 1
392 merged += 1
393 elif m == "g": # get
393 elif m == "g": # get
394 flags = a[2]
394 flags = a[2]
395 repo.ui.note(_("getting %s\n") % f)
395 repo.ui.note(_("getting %s\n") % f)
396 repo.wwrite(f, mctx.filectx(f).data(), flags)
396 repo.wwrite(f, mctx.filectx(f).data(), flags)
397 updated += 1
397 updated += 1
398 if f == '.hgsubstate': # subrepo states need updating
398 if f == '.hgsubstate': # subrepo states need updating
399 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
399 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
400 elif m == "d": # directory rename
400 elif m == "d": # directory rename
401 f2, fd, flags = a[2:]
401 f2, fd, flags = a[2:]
402 if f:
402 if f:
403 repo.ui.note(_("moving %s to %s\n") % (f, fd))
403 repo.ui.note(_("moving %s to %s\n") % (f, fd))
404 audit(f)
404 audit(f)
405 repo.wwrite(fd, wctx.filectx(f).data(), flags)
405 repo.wwrite(fd, wctx.filectx(f).data(), flags)
406 util.unlinkpath(repo.wjoin(f))
406 util.unlinkpath(repo.wjoin(f))
407 if f2:
407 if f2:
408 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
408 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
409 repo.wwrite(fd, mctx.filectx(f2).data(), flags)
409 repo.wwrite(fd, mctx.filectx(f2).data(), flags)
410 updated += 1
410 updated += 1
411 elif m == "dr": # divergent renames
411 elif m == "dr": # divergent renames
412 fl = a[2]
412 fl = a[2]
413 repo.ui.warn(_("note: possible conflict - %s was renamed "
413 repo.ui.warn(_("note: possible conflict - %s was renamed "
414 "multiple times to:\n") % f)
414 "multiple times to:\n") % f)
415 for nf in fl:
415 for nf in fl:
416 repo.ui.warn(" %s\n" % nf)
416 repo.ui.warn(" %s\n" % nf)
417 elif m == "rd": # rename and delete
417 elif m == "rd": # rename and delete
418 fl = a[2]
418 fl = a[2]
419 repo.ui.warn(_("note: possible conflict - %s was deleted "
419 repo.ui.warn(_("note: possible conflict - %s was deleted "
420 "and renamed to:\n") % f)
420 "and renamed to:\n") % f)
421 for nf in fl:
421 for nf in fl:
422 repo.ui.warn(" %s\n" % nf)
422 repo.ui.warn(" %s\n" % nf)
423 elif m == "e": # exec
423 elif m == "e": # exec
424 flags = a[2]
424 flags = a[2]
425 audit(f)
425 audit(f)
426 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
426 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
427 updated += 1
427 updated += 1
428 ms.commit()
428 ms.commit()
429 repo.ui.progress(_('updating'), None, total=numupdates, unit=_('files'))
429 repo.ui.progress(_('updating'), None, total=numupdates, unit=_('files'))
430
430
431 return updated, merged, removed, unresolved
431 return updated, merged, removed, unresolved
432
432
433 def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial):
433 def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial):
434 "Calculate the actions needed to merge mctx into tctx"
434 "Calculate the actions needed to merge mctx into tctx"
435 actions = []
435 actions = []
436 folding = not util.checkcase(repo.path)
436 folding = not util.checkcase(repo.path)
437 if folding:
437 if folding:
438 # collision check is not needed for clean update
438 # collision check is not needed for clean update
439 if (not branchmerge and
439 if (not branchmerge and
440 (force or not tctx.dirty(missing=True, branch=False))):
440 (force or not tctx.dirty(missing=True, branch=False))):
441 _checkcollision(mctx, None)
441 _checkcollision(mctx, None)
442 else:
442 else:
443 _checkcollision(mctx, (tctx, ancestor))
443 _checkcollision(mctx, (tctx, ancestor))
444 if not force:
444 if not force:
445 _checkunknown(repo, tctx, mctx)
445 _checkunknown(repo, tctx, mctx)
446 if tctx.rev() is None:
446 if tctx.rev() is None:
447 actions += _forgetremoved(tctx, mctx, branchmerge)
447 actions += _forgetremoved(tctx, mctx, branchmerge)
448 actions += manifestmerge(repo, tctx, mctx,
448 actions += manifestmerge(repo, tctx, mctx,
449 ancestor,
449 ancestor,
450 force and not branchmerge,
450 force and not branchmerge,
451 partial)
451 partial)
452 return actions
452 return actions
453
453
454 def recordupdates(repo, actions, branchmerge):
454 def recordupdates(repo, actions, branchmerge):
455 "record merge actions to the dirstate"
455 "record merge actions to the dirstate"
456
456
457 for a in actions:
457 for a in actions:
458 f, m = a[:2]
458 f, m = a[:2]
459 if m == "r": # remove
459 if m == "r": # remove
460 if branchmerge:
460 if branchmerge:
461 repo.dirstate.remove(f)
461 repo.dirstate.remove(f)
462 else:
462 else:
463 repo.dirstate.drop(f)
463 repo.dirstate.drop(f)
464 elif m == "a": # re-add
464 elif m == "a": # re-add
465 if not branchmerge:
465 if not branchmerge:
466 repo.dirstate.add(f)
466 repo.dirstate.add(f)
467 elif m == "f": # forget
467 elif m == "f": # forget
468 repo.dirstate.drop(f)
468 repo.dirstate.drop(f)
469 elif m == "e": # exec change
469 elif m == "e": # exec change
470 repo.dirstate.normallookup(f)
470 repo.dirstate.normallookup(f)
471 elif m == "g": # get
471 elif m == "g": # get
472 if branchmerge:
472 if branchmerge:
473 repo.dirstate.otherparent(f)
473 repo.dirstate.otherparent(f)
474 else:
474 else:
475 repo.dirstate.normal(f)
475 repo.dirstate.normal(f)
476 elif m == "m": # merge
476 elif m == "m": # merge
477 f2, fd, move = a[2:]
477 f2, fd, move = a[2:]
478 if branchmerge:
478 if branchmerge:
479 # We've done a branch merge, mark this file as merged
479 # We've done a branch merge, mark this file as merged
480 # so that we properly record the merger later
480 # so that we properly record the merger later
481 repo.dirstate.merge(fd)
481 repo.dirstate.merge(fd)
482 if f != f2: # copy/rename
482 if f != f2: # copy/rename
483 if move:
483 if move:
484 repo.dirstate.remove(f)
484 repo.dirstate.remove(f)
485 if f != fd:
485 if f != fd:
486 repo.dirstate.copy(f, fd)
486 repo.dirstate.copy(f, fd)
487 else:
487 else:
488 repo.dirstate.copy(f2, fd)
488 repo.dirstate.copy(f2, fd)
489 else:
489 else:
490 # We've update-merged a locally modified file, so
490 # We've update-merged a locally modified file, so
491 # we set the dirstate to emulate a normal checkout
491 # we set the dirstate to emulate a normal checkout
492 # of that file some time in the past. Thus our
492 # of that file some time in the past. Thus our
493 # merge will appear as a normal local file
493 # merge will appear as a normal local file
494 # modification.
494 # modification.
495 if f2 == fd: # file not locally copied/moved
495 if f2 == fd: # file not locally copied/moved
496 repo.dirstate.normallookup(fd)
496 repo.dirstate.normallookup(fd)
497 if move:
497 if move:
498 repo.dirstate.drop(f)
498 repo.dirstate.drop(f)
499 elif m == "d": # directory rename
499 elif m == "d": # directory rename
500 f2, fd, flag = a[2:]
500 f2, fd, flag = a[2:]
501 if not f2 and f not in repo.dirstate:
501 if not f2 and f not in repo.dirstate:
502 # untracked file moved
502 # untracked file moved
503 continue
503 continue
504 if branchmerge:
504 if branchmerge:
505 repo.dirstate.add(fd)
505 repo.dirstate.add(fd)
506 if f:
506 if f:
507 repo.dirstate.remove(f)
507 repo.dirstate.remove(f)
508 repo.dirstate.copy(f, fd)
508 repo.dirstate.copy(f, fd)
509 if f2:
509 if f2:
510 repo.dirstate.copy(f2, fd)
510 repo.dirstate.copy(f2, fd)
511 else:
511 else:
512 repo.dirstate.normal(fd)
512 repo.dirstate.normal(fd)
513 if f:
513 if f:
514 repo.dirstate.drop(f)
514 repo.dirstate.drop(f)
515
515
516 def update(repo, node, branchmerge, force, partial, ancestor=None,
516 def update(repo, node, branchmerge, force, partial, ancestor=None,
517 mergeancestor=False):
517 mergeancestor=False):
518 """
518 """
519 Perform a merge between the working directory and the given node
519 Perform a merge between the working directory and the given node
520
520
521 node = the node to update to, or None if unspecified
521 node = the node to update to, or None if unspecified
522 branchmerge = whether to merge between branches
522 branchmerge = whether to merge between branches
523 force = whether to force branch merging or file overwriting
523 force = whether to force branch merging or file overwriting
524 partial = a function to filter file lists (dirstate not updated)
524 partial = a function to filter file lists (dirstate not updated)
525 mergeancestor = if false, merging with an ancestor (fast-forward)
525 mergeancestor = if false, merging with an ancestor (fast-forward)
526 is only allowed between different named branches. This flag
526 is only allowed between different named branches. This flag
527 is used by rebase extension as a temporary fix and should be
527 is used by rebase extension as a temporary fix and should be
528 avoided in general.
528 avoided in general.
529
529
530 The table below shows all the behaviors of the update command
530 The table below shows all the behaviors of the update command
531 given the -c and -C or no options, whether the working directory
531 given the -c and -C or no options, whether the working directory
532 is dirty, whether a revision is specified, and the relationship of
532 is dirty, whether a revision is specified, and the relationship of
533 the parent rev to the target rev (linear, on the same named
533 the parent rev to the target rev (linear, on the same named
534 branch, or on another named branch).
534 branch, or on another named branch).
535
535
536 This logic is tested by test-update-branches.t.
536 This logic is tested by test-update-branches.t.
537
537
538 -c -C dirty rev | linear same cross
538 -c -C dirty rev | linear same cross
539 n n n n | ok (1) x
539 n n n n | ok (1) x
540 n n n y | ok ok ok
540 n n n y | ok ok ok
541 n n y * | merge (2) (2)
541 n n y * | merge (2) (2)
542 n y * * | --- discard ---
542 n y * * | --- discard ---
543 y n y * | --- (3) ---
543 y n y * | --- (3) ---
544 y n n * | --- ok ---
544 y n n * | --- ok ---
545 y y * * | --- (4) ---
545 y y * * | --- (4) ---
546
546
547 x = can't happen
547 x = can't happen
548 * = don't-care
548 * = don't-care
549 1 = abort: crosses branches (use 'hg merge' or 'hg update -c')
549 1 = abort: crosses branches (use 'hg merge' or 'hg update -c')
550 2 = abort: crosses branches (use 'hg merge' to merge or
550 2 = abort: crosses branches (use 'hg merge' to merge or
551 use 'hg update -C' to discard changes)
551 use 'hg update -C' to discard changes)
552 3 = abort: uncommitted local changes
552 3 = abort: uncommitted local changes
553 4 = incompatible options (checked in commands.py)
553 4 = incompatible options (checked in commands.py)
554
554
555 Return the same tuple as applyupdates().
555 Return the same tuple as applyupdates().
556 """
556 """
557
557
558 onode = node
558 onode = node
559 wlock = repo.wlock()
559 wlock = repo.wlock()
560 try:
560 try:
561 wc = repo[None]
561 wc = repo[None]
562 if node is None:
562 if node is None:
563 # tip of current branch
563 # tip of current branch
564 try:
564 try:
565 node = repo.branchtip(wc.branch())
565 node = repo.branchtip(wc.branch())
566 except error.RepoLookupError:
566 except error.RepoLookupError:
567 if wc.branch() == "default": # no default branch!
567 if wc.branch() == "default": # no default branch!
568 node = repo.lookup("tip") # update to tip
568 node = repo.lookup("tip") # update to tip
569 else:
569 else:
570 raise util.Abort(_("branch %s not found") % wc.branch())
570 raise util.Abort(_("branch %s not found") % wc.branch())
571 overwrite = force and not branchmerge
571 overwrite = force and not branchmerge
572 pl = wc.parents()
572 pl = wc.parents()
573 p1, p2 = pl[0], repo[node]
573 p1, p2 = pl[0], repo[node]
574 if ancestor:
574 if ancestor:
575 pa = repo[ancestor]
575 pa = repo[ancestor]
576 else:
576 else:
577 pa = p1.ancestor(p2)
577 pa = p1.ancestor(p2)
578
578
579 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
579 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
580
580
581 ### check phase
581 ### check phase
582 if not overwrite and len(pl) > 1:
582 if not overwrite and len(pl) > 1:
583 raise util.Abort(_("outstanding uncommitted merges"))
583 raise util.Abort(_("outstanding uncommitted merges"))
584 if branchmerge:
584 if branchmerge:
585 if pa == p2:
585 if pa == p2:
586 raise util.Abort(_("merging with a working directory ancestor"
586 raise util.Abort(_("merging with a working directory ancestor"
587 " has no effect"))
587 " has no effect"))
588 elif pa == p1:
588 elif pa == p1:
589 if not mergeancestor and p1.branch() == p2.branch():
589 if not mergeancestor and p1.branch() == p2.branch():
590 raise util.Abort(_("nothing to merge"),
590 raise util.Abort(_("nothing to merge"),
591 hint=_("use 'hg update' "
591 hint=_("use 'hg update' "
592 "or check 'hg heads'"))
592 "or check 'hg heads'"))
593 if not force and (wc.files() or wc.deleted()):
593 if not force and (wc.files() or wc.deleted()):
594 raise util.Abort(_("outstanding uncommitted changes"),
594 raise util.Abort(_("outstanding uncommitted changes"),
595 hint=_("use 'hg status' to list changes"))
595 hint=_("use 'hg status' to list changes"))
596 for s in wc.substate:
596 for s in wc.substate:
597 if wc.sub(s).dirty():
597 if wc.sub(s).dirty():
598 raise util.Abort(_("outstanding uncommitted changes in "
598 raise util.Abort(_("outstanding uncommitted changes in "
599 "subrepository '%s'") % s)
599 "subrepository '%s'") % s)
600
600
601 elif not overwrite:
601 elif not overwrite:
602 if pa == p1 or pa == p2: # linear
602 if pa == p1 or pa == p2: # linear
603 pass # all good
603 pass # all good
604 elif wc.dirty(missing=True):
604 elif wc.dirty(missing=True):
605 raise util.Abort(_("crosses branches (merge branches or use"
605 raise util.Abort(_("crosses branches (merge branches or use"
606 " --clean to discard changes)"))
606 " --clean to discard changes)"))
607 elif onode is None:
607 elif onode is None:
608 raise util.Abort(_("crosses branches (merge branches or update"
608 raise util.Abort(_("crosses branches (merge branches or update"
609 " --check to force update)"))
609 " --check to force update)"))
610 else:
610 else:
611 # Allow jumping branches if clean and specific rev given
611 # Allow jumping branches if clean and specific rev given
612 pa = p1
612 pa = p1
613
613
614 ### calculate phase
614 ### calculate phase
615 actions = calculateupdates(repo, wc, p2, pa,
615 actions = calculateupdates(repo, wc, p2, pa,
616 branchmerge, force, partial)
616 branchmerge, force, partial)
617
617
618 ### apply phase
618 ### apply phase
619 if not branchmerge: # just jump to the new rev
619 if not branchmerge: # just jump to the new rev
620 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
620 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
621 if not partial:
621 if not partial:
622 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
622 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
623
623
624 stats = applyupdates(repo, actions, wc, p2, pa, overwrite)
624 stats = applyupdates(repo, actions, wc, p2, pa, overwrite)
625
625
626 if not partial:
626 if not partial:
627 repo.setparents(fp1, fp2)
627 repo.setparents(fp1, fp2)
628 recordupdates(repo, actions, branchmerge)
628 recordupdates(repo, actions, branchmerge)
629 if not branchmerge:
629 if not branchmerge:
630 repo.dirstate.setbranch(p2.branch())
630 repo.dirstate.setbranch(p2.branch())
631 finally:
631 finally:
632 wlock.release()
632 wlock.release()
633
633
634 if not partial:
634 if not partial:
635 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
635 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
636 return stats
636 return stats
@@ -1,64 +1,64 b''
1 $ hg init t
1 $ hg init t
2 $ cd t
2 $ cd t
3
3
4 $ echo 1 > a
4 $ echo 1 > a
5 $ hg ci -qAm "first"
5 $ hg ci -qAm "first"
6
6
7 $ hg cp a b
7 $ hg cp a b
8 $ hg mv a c
8 $ hg mv a c
9 $ echo 2 >> b
9 $ echo 2 >> b
10 $ echo 2 >> c
10 $ echo 2 >> c
11
11
12 $ hg ci -qAm "second"
12 $ hg ci -qAm "second"
13
13
14 $ hg co -C 0
14 $ hg co -C 0
15 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
15 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
16
16
17 $ echo 0 > a
17 $ echo 0 > a
18 $ echo 1 >> a
18 $ echo 1 >> a
19
19
20 $ hg ci -qAm "other"
20 $ hg ci -qAm "other"
21
21
22 $ hg merge --debug
22 $ hg merge --debug
23 searching for copies back to rev 1
23 searching for copies back to rev 1
24 unmatched files in other:
24 unmatched files in other:
25 b
25 b
26 c
26 c
27 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
27 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
28 src: 'a' -> dst: 'c' *
28 src: 'a' -> dst: 'c' *
29 src: 'a' -> dst: 'b' *
29 src: 'a' -> dst: 'b' *
30 checking for directory renames
30 checking for directory renames
31 resolving manifests
31 resolving manifests
32 overwrite: False, partial: False
32 overwrite: False, partial: False
33 ancestor: b8bf91eeebbc, local: add3f11052fa+, remote: 17c05bb7fcb6
33 ancestor: b8bf91eeebbc, local: add3f11052fa+, remote: 17c05bb7fcb6
34 a: remote moved to b -> m
34 a: remote moved to c -> m
35 a: remote moved to c -> m
35 a: remote moved to b -> m
36 preserving a for resolve of b
36 preserving a for resolve of b
37 preserving a for resolve of c
37 preserving a for resolve of c
38 removing a
38 removing a
39 updating: a 1/2 files (50.00%)
39 updating: a 1/2 files (50.00%)
40 picked tool 'internal:merge' for b (binary False symlink False)
40 picked tool 'internal:merge' for b (binary False symlink False)
41 merging a and b to b
41 merging a and b to b
42 my b@add3f11052fa+ other b@17c05bb7fcb6 ancestor a@b8bf91eeebbc
42 my b@add3f11052fa+ other b@17c05bb7fcb6 ancestor a@b8bf91eeebbc
43 premerge successful
43 premerge successful
44 updating: a 2/2 files (100.00%)
44 updating: a 2/2 files (100.00%)
45 picked tool 'internal:merge' for c (binary False symlink False)
45 picked tool 'internal:merge' for c (binary False symlink False)
46 merging a and c to c
46 merging a and c to c
47 my c@add3f11052fa+ other c@17c05bb7fcb6 ancestor a@b8bf91eeebbc
47 my c@add3f11052fa+ other c@17c05bb7fcb6 ancestor a@b8bf91eeebbc
48 premerge successful
48 premerge successful
49 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
49 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
50 (branch merge, don't forget to commit)
50 (branch merge, don't forget to commit)
51
51
52 file b
52 file b
53 $ cat b
53 $ cat b
54 0
54 0
55 1
55 1
56 2
56 2
57
57
58 file c
58 file c
59 $ cat c
59 $ cat c
60 0
60 0
61 1
61 1
62 2
62 2
63
63
64 $ cd ..
64 $ cd ..
@@ -1,535 +1,535 b''
1 Create a repo with some stuff in it:
1 Create a repo with some stuff in it:
2
2
3 $ hg init a
3 $ hg init a
4 $ cd a
4 $ cd a
5 $ echo a > a
5 $ echo a > a
6 $ echo a > d
6 $ echo a > d
7 $ echo a > e
7 $ echo a > e
8 $ hg ci -qAm0
8 $ hg ci -qAm0
9 $ echo b > a
9 $ echo b > a
10 $ hg ci -m1 -u bar
10 $ hg ci -m1 -u bar
11 $ hg mv a b
11 $ hg mv a b
12 $ hg ci -m2
12 $ hg ci -m2
13 $ hg cp b c
13 $ hg cp b c
14 $ hg ci -m3 -u baz
14 $ hg ci -m3 -u baz
15 $ echo b > d
15 $ echo b > d
16 $ echo f > e
16 $ echo f > e
17 $ hg ci -m4
17 $ hg ci -m4
18 $ hg up -q 3
18 $ hg up -q 3
19 $ echo b > e
19 $ echo b > e
20 $ hg branch -q stable
20 $ hg branch -q stable
21 $ hg ci -m5
21 $ hg ci -m5
22 $ hg merge -q default --tool internal:local
22 $ hg merge -q default --tool internal:local
23 $ hg branch -q default
23 $ hg branch -q default
24 $ hg ci -m6
24 $ hg ci -m6
25 $ hg phase --public 3
25 $ hg phase --public 3
26 $ hg phase --force --secret 6
26 $ hg phase --force --secret 6
27
27
28 $ hg --config extensions.graphlog= log -G --template '{author}@{rev}.{phase}: {desc}\n'
28 $ hg --config extensions.graphlog= log -G --template '{author}@{rev}.{phase}: {desc}\n'
29 @ test@6.secret: 6
29 @ test@6.secret: 6
30 |\
30 |\
31 | o test@5.draft: 5
31 | o test@5.draft: 5
32 | |
32 | |
33 o | test@4.draft: 4
33 o | test@4.draft: 4
34 |/
34 |/
35 o baz@3.public: 3
35 o baz@3.public: 3
36 |
36 |
37 o test@2.public: 2
37 o test@2.public: 2
38 |
38 |
39 o bar@1.public: 1
39 o bar@1.public: 1
40 |
40 |
41 o test@0.public: 0
41 o test@0.public: 0
42
42
43
43
44 Need to specify a rev:
44 Need to specify a rev:
45
45
46 $ hg graft
46 $ hg graft
47 abort: no revisions specified
47 abort: no revisions specified
48 [255]
48 [255]
49
49
50 Can't graft ancestor:
50 Can't graft ancestor:
51
51
52 $ hg graft 1 2
52 $ hg graft 1 2
53 skipping ancestor revision 1
53 skipping ancestor revision 1
54 skipping ancestor revision 2
54 skipping ancestor revision 2
55 [255]
55 [255]
56
56
57 Specify revisions with -r:
57 Specify revisions with -r:
58
58
59 $ hg graft -r 1 -r 2
59 $ hg graft -r 1 -r 2
60 skipping ancestor revision 1
60 skipping ancestor revision 1
61 skipping ancestor revision 2
61 skipping ancestor revision 2
62 [255]
62 [255]
63
63
64 $ hg graft -r 1 2
64 $ hg graft -r 1 2
65 skipping ancestor revision 2
65 skipping ancestor revision 2
66 skipping ancestor revision 1
66 skipping ancestor revision 1
67 [255]
67 [255]
68
68
69 Can't graft with dirty wd:
69 Can't graft with dirty wd:
70
70
71 $ hg up -q 0
71 $ hg up -q 0
72 $ echo foo > a
72 $ echo foo > a
73 $ hg graft 1
73 $ hg graft 1
74 abort: outstanding uncommitted changes
74 abort: outstanding uncommitted changes
75 [255]
75 [255]
76 $ hg revert a
76 $ hg revert a
77
77
78 Graft a rename:
78 Graft a rename:
79
79
80 $ hg graft 2 -u foo
80 $ hg graft 2 -u foo
81 grafting revision 2
81 grafting revision 2
82 merging a and b to b
82 merging a and b to b
83 $ hg export tip --git
83 $ hg export tip --git
84 # HG changeset patch
84 # HG changeset patch
85 # User foo
85 # User foo
86 # Date 0 0
86 # Date 0 0
87 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
87 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
88 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
88 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
89 2
89 2
90
90
91 diff --git a/a b/b
91 diff --git a/a b/b
92 rename from a
92 rename from a
93 rename to b
93 rename to b
94
94
95 Look for extra:source
95 Look for extra:source
96
96
97 $ hg log --debug -r tip
97 $ hg log --debug -r tip
98 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
98 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
99 tag: tip
99 tag: tip
100 phase: draft
100 phase: draft
101 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
101 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
102 parent: -1:0000000000000000000000000000000000000000
102 parent: -1:0000000000000000000000000000000000000000
103 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
103 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
104 user: foo
104 user: foo
105 date: Thu Jan 01 00:00:00 1970 +0000
105 date: Thu Jan 01 00:00:00 1970 +0000
106 files+: b
106 files+: b
107 files-: a
107 files-: a
108 extra: branch=default
108 extra: branch=default
109 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
109 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
110 description:
110 description:
111 2
111 2
112
112
113
113
114
114
115 Graft out of order, skipping a merge and a duplicate
115 Graft out of order, skipping a merge and a duplicate
116
116
117 $ hg graft 1 5 4 3 'merge()' 2 -n
117 $ hg graft 1 5 4 3 'merge()' 2 -n
118 skipping ungraftable merge revision 6
118 skipping ungraftable merge revision 6
119 skipping already grafted revision 2
119 skipping already grafted revision 2
120 grafting revision 1
120 grafting revision 1
121 grafting revision 5
121 grafting revision 5
122 grafting revision 4
122 grafting revision 4
123 grafting revision 3
123 grafting revision 3
124
124
125 $ hg graft 1 5 4 3 'merge()' 2 --debug
125 $ hg graft 1 5 4 3 'merge()' 2 --debug
126 skipping ungraftable merge revision 6
126 skipping ungraftable merge revision 6
127 scanning for duplicate grafts
127 scanning for duplicate grafts
128 skipping already grafted revision 2
128 skipping already grafted revision 2
129 grafting revision 1
129 grafting revision 1
130 searching for copies back to rev 1
130 searching for copies back to rev 1
131 unmatched files in local:
131 unmatched files in local:
132 b
132 b
133 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
133 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
134 src: 'a' -> dst: 'b' *
134 src: 'a' -> dst: 'b' *
135 checking for directory renames
135 checking for directory renames
136 resolving manifests
136 resolving manifests
137 overwrite: False, partial: False
137 overwrite: False, partial: False
138 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
138 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
139 b: local copied/moved to a -> m
139 b: local copied/moved to a -> m
140 preserving b for resolve of b
140 preserving b for resolve of b
141 updating: b 1/1 files (100.00%)
141 updating: b 1/1 files (100.00%)
142 picked tool 'internal:merge' for b (binary False symlink False)
142 picked tool 'internal:merge' for b (binary False symlink False)
143 merging b and a to b
143 merging b and a to b
144 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
144 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
145 premerge successful
145 premerge successful
146 b
146 b
147 grafting revision 5
147 grafting revision 5
148 searching for copies back to rev 1
148 searching for copies back to rev 1
149 resolving manifests
149 resolving manifests
150 overwrite: False, partial: False
150 overwrite: False, partial: False
151 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
151 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
152 e: remote is newer -> g
152 e: remote is newer -> g
153 updating: e 1/1 files (100.00%)
153 updating: e 1/1 files (100.00%)
154 getting e
154 getting e
155 e
155 e
156 grafting revision 4
156 grafting revision 4
157 searching for copies back to rev 1
157 searching for copies back to rev 1
158 resolving manifests
158 resolving manifests
159 overwrite: False, partial: False
159 overwrite: False, partial: False
160 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
160 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
161 d: remote is newer -> g
161 e: versions differ -> m
162 e: versions differ -> m
162 d: remote is newer -> g
163 preserving e for resolve of e
163 preserving e for resolve of e
164 updating: d 1/2 files (50.00%)
164 updating: d 1/2 files (50.00%)
165 getting d
165 getting d
166 updating: e 2/2 files (100.00%)
166 updating: e 2/2 files (100.00%)
167 picked tool 'internal:merge' for e (binary False symlink False)
167 picked tool 'internal:merge' for e (binary False symlink False)
168 merging e
168 merging e
169 my e@1905859650ec+ other e@9c233e8e184d ancestor e@68795b066622
169 my e@1905859650ec+ other e@9c233e8e184d ancestor e@68795b066622
170 warning: conflicts during merge.
170 warning: conflicts during merge.
171 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
171 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
172 abort: unresolved conflicts, can't continue
172 abort: unresolved conflicts, can't continue
173 (use hg resolve and hg graft --continue)
173 (use hg resolve and hg graft --continue)
174 [255]
174 [255]
175
175
176 Continue without resolve should fail:
176 Continue without resolve should fail:
177
177
178 $ hg graft -c
178 $ hg graft -c
179 grafting revision 4
179 grafting revision 4
180 abort: unresolved merge conflicts (see hg help resolve)
180 abort: unresolved merge conflicts (see hg help resolve)
181 [255]
181 [255]
182
182
183 Fix up:
183 Fix up:
184
184
185 $ echo b > e
185 $ echo b > e
186 $ hg resolve -m e
186 $ hg resolve -m e
187
187
188 Continue with a revision should fail:
188 Continue with a revision should fail:
189
189
190 $ hg graft -c 6
190 $ hg graft -c 6
191 abort: can't specify --continue and revisions
191 abort: can't specify --continue and revisions
192 [255]
192 [255]
193
193
194 $ hg graft -c -r 6
194 $ hg graft -c -r 6
195 abort: can't specify --continue and revisions
195 abort: can't specify --continue and revisions
196 [255]
196 [255]
197
197
198 Continue for real, clobber usernames
198 Continue for real, clobber usernames
199
199
200 $ hg graft -c -U
200 $ hg graft -c -U
201 grafting revision 4
201 grafting revision 4
202 grafting revision 3
202 grafting revision 3
203
203
204 Compare with original:
204 Compare with original:
205
205
206 $ hg diff -r 6
206 $ hg diff -r 6
207 $ hg status --rev 0:. -C
207 $ hg status --rev 0:. -C
208 M d
208 M d
209 M e
209 M e
210 A b
210 A b
211 a
211 a
212 A c
212 A c
213 a
213 a
214 R a
214 R a
215
215
216 View graph:
216 View graph:
217
217
218 $ hg --config extensions.graphlog= log -G --template '{author}@{rev}.{phase}: {desc}\n'
218 $ hg --config extensions.graphlog= log -G --template '{author}@{rev}.{phase}: {desc}\n'
219 @ test@11.draft: 3
219 @ test@11.draft: 3
220 |
220 |
221 o test@10.draft: 4
221 o test@10.draft: 4
222 |
222 |
223 o test@9.draft: 5
223 o test@9.draft: 5
224 |
224 |
225 o bar@8.draft: 1
225 o bar@8.draft: 1
226 |
226 |
227 o foo@7.draft: 2
227 o foo@7.draft: 2
228 |
228 |
229 | o test@6.secret: 6
229 | o test@6.secret: 6
230 | |\
230 | |\
231 | | o test@5.draft: 5
231 | | o test@5.draft: 5
232 | | |
232 | | |
233 | o | test@4.draft: 4
233 | o | test@4.draft: 4
234 | |/
234 | |/
235 | o baz@3.public: 3
235 | o baz@3.public: 3
236 | |
236 | |
237 | o test@2.public: 2
237 | o test@2.public: 2
238 | |
238 | |
239 | o bar@1.public: 1
239 | o bar@1.public: 1
240 |/
240 |/
241 o test@0.public: 0
241 o test@0.public: 0
242
242
243 Graft again onto another branch should preserve the original source
243 Graft again onto another branch should preserve the original source
244 $ hg up -q 0
244 $ hg up -q 0
245 $ echo 'g'>g
245 $ echo 'g'>g
246 $ hg add g
246 $ hg add g
247 $ hg ci -m 7
247 $ hg ci -m 7
248 created new head
248 created new head
249 $ hg graft 7
249 $ hg graft 7
250 grafting revision 7
250 grafting revision 7
251
251
252 $ hg log -r 7 --template '{rev}:{node}\n'
252 $ hg log -r 7 --template '{rev}:{node}\n'
253 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
253 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
254 $ hg log -r 2 --template '{rev}:{node}\n'
254 $ hg log -r 2 --template '{rev}:{node}\n'
255 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
255 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
256
256
257 $ hg log --debug -r tip
257 $ hg log --debug -r tip
258 changeset: 13:9db0f28fd3747e92c57d015f53b5593aeec53c2d
258 changeset: 13:9db0f28fd3747e92c57d015f53b5593aeec53c2d
259 tag: tip
259 tag: tip
260 phase: draft
260 phase: draft
261 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
261 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
262 parent: -1:0000000000000000000000000000000000000000
262 parent: -1:0000000000000000000000000000000000000000
263 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
263 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
264 user: foo
264 user: foo
265 date: Thu Jan 01 00:00:00 1970 +0000
265 date: Thu Jan 01 00:00:00 1970 +0000
266 files+: b
266 files+: b
267 files-: a
267 files-: a
268 extra: branch=default
268 extra: branch=default
269 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
269 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
270 description:
270 description:
271 2
271 2
272
272
273
273
274 Disallow grafting an already grafted cset onto its original branch
274 Disallow grafting an already grafted cset onto its original branch
275 $ hg up -q 6
275 $ hg up -q 6
276 $ hg graft 7
276 $ hg graft 7
277 skipping already grafted revision 7 (was grafted from 2)
277 skipping already grafted revision 7 (was grafted from 2)
278 [255]
278 [255]
279
279
280 Disallow grafting already grafted csets with the same origin onto each other
280 Disallow grafting already grafted csets with the same origin onto each other
281 $ hg up -q 13
281 $ hg up -q 13
282 $ hg graft 2
282 $ hg graft 2
283 skipping already grafted revision 2
283 skipping already grafted revision 2
284 [255]
284 [255]
285 $ hg graft 7
285 $ hg graft 7
286 skipping already grafted revision 7 (same origin 2)
286 skipping already grafted revision 7 (same origin 2)
287 [255]
287 [255]
288
288
289 $ hg up -q 7
289 $ hg up -q 7
290 $ hg graft 2
290 $ hg graft 2
291 skipping already grafted revision 2
291 skipping already grafted revision 2
292 [255]
292 [255]
293 $ hg graft tip
293 $ hg graft tip
294 skipping already grafted revision 13 (same origin 2)
294 skipping already grafted revision 13 (same origin 2)
295 [255]
295 [255]
296
296
297 Graft with --log
297 Graft with --log
298
298
299 $ hg up -Cq 1
299 $ hg up -Cq 1
300 $ hg graft 3 --log -u foo
300 $ hg graft 3 --log -u foo
301 grafting revision 3
301 grafting revision 3
302 warning: can't find ancestor for 'c' copied from 'b'!
302 warning: can't find ancestor for 'c' copied from 'b'!
303 $ hg log --template '{rev} {parents} {desc}\n' -r tip
303 $ hg log --template '{rev} {parents} {desc}\n' -r tip
304 14 1:5d205f8b35b6 3
304 14 1:5d205f8b35b6 3
305 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
305 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
306
306
307 Resolve conflicted graft
307 Resolve conflicted graft
308 $ hg up -q 0
308 $ hg up -q 0
309 $ echo b > a
309 $ echo b > a
310 $ hg ci -m 8
310 $ hg ci -m 8
311 created new head
311 created new head
312 $ echo a > a
312 $ echo a > a
313 $ hg ci -m 9
313 $ hg ci -m 9
314 $ hg graft 1 --tool internal:fail
314 $ hg graft 1 --tool internal:fail
315 grafting revision 1
315 grafting revision 1
316 abort: unresolved conflicts, can't continue
316 abort: unresolved conflicts, can't continue
317 (use hg resolve and hg graft --continue)
317 (use hg resolve and hg graft --continue)
318 [255]
318 [255]
319 $ hg resolve --all
319 $ hg resolve --all
320 merging a
320 merging a
321 $ hg graft -c
321 $ hg graft -c
322 grafting revision 1
322 grafting revision 1
323 $ hg export tip --git
323 $ hg export tip --git
324 # HG changeset patch
324 # HG changeset patch
325 # User bar
325 # User bar
326 # Date 0 0
326 # Date 0 0
327 # Node ID 64ecd9071ce83c6e62f538d8ce7709d53f32ebf7
327 # Node ID 64ecd9071ce83c6e62f538d8ce7709d53f32ebf7
328 # Parent 4bdb9a9d0b84ffee1d30f0dfc7744cade17aa19c
328 # Parent 4bdb9a9d0b84ffee1d30f0dfc7744cade17aa19c
329 1
329 1
330
330
331 diff --git a/a b/a
331 diff --git a/a b/a
332 --- a/a
332 --- a/a
333 +++ b/a
333 +++ b/a
334 @@ -1,1 +1,1 @@
334 @@ -1,1 +1,1 @@
335 -a
335 -a
336 +b
336 +b
337
337
338 Resolve conflicted graft with rename
338 Resolve conflicted graft with rename
339 $ echo c > a
339 $ echo c > a
340 $ hg ci -m 10
340 $ hg ci -m 10
341 $ hg graft 2 --tool internal:fail
341 $ hg graft 2 --tool internal:fail
342 grafting revision 2
342 grafting revision 2
343 abort: unresolved conflicts, can't continue
343 abort: unresolved conflicts, can't continue
344 (use hg resolve and hg graft --continue)
344 (use hg resolve and hg graft --continue)
345 [255]
345 [255]
346 $ hg resolve --all
346 $ hg resolve --all
347 merging a and b to b
347 merging a and b to b
348 $ hg graft -c
348 $ hg graft -c
349 grafting revision 2
349 grafting revision 2
350 $ hg export tip --git
350 $ hg export tip --git
351 # HG changeset patch
351 # HG changeset patch
352 # User test
352 # User test
353 # Date 0 0
353 # Date 0 0
354 # Node ID 2e80e1351d6ed50302fe1e05f8bd1d4d412b6e11
354 # Node ID 2e80e1351d6ed50302fe1e05f8bd1d4d412b6e11
355 # Parent e5a51ae854a8bbaaf25cc5c6a57ff46042dadbb4
355 # Parent e5a51ae854a8bbaaf25cc5c6a57ff46042dadbb4
356 2
356 2
357
357
358 diff --git a/a b/b
358 diff --git a/a b/b
359 rename from a
359 rename from a
360 rename to b
360 rename to b
361
361
362 Test simple origin(), with and without args
362 Test simple origin(), with and without args
363 $ hg log -r 'origin()'
363 $ hg log -r 'origin()'
364 changeset: 1:5d205f8b35b6
364 changeset: 1:5d205f8b35b6
365 user: bar
365 user: bar
366 date: Thu Jan 01 00:00:00 1970 +0000
366 date: Thu Jan 01 00:00:00 1970 +0000
367 summary: 1
367 summary: 1
368
368
369 changeset: 2:5c095ad7e90f
369 changeset: 2:5c095ad7e90f
370 user: test
370 user: test
371 date: Thu Jan 01 00:00:00 1970 +0000
371 date: Thu Jan 01 00:00:00 1970 +0000
372 summary: 2
372 summary: 2
373
373
374 changeset: 3:4c60f11aa304
374 changeset: 3:4c60f11aa304
375 user: baz
375 user: baz
376 date: Thu Jan 01 00:00:00 1970 +0000
376 date: Thu Jan 01 00:00:00 1970 +0000
377 summary: 3
377 summary: 3
378
378
379 changeset: 4:9c233e8e184d
379 changeset: 4:9c233e8e184d
380 user: test
380 user: test
381 date: Thu Jan 01 00:00:00 1970 +0000
381 date: Thu Jan 01 00:00:00 1970 +0000
382 summary: 4
382 summary: 4
383
383
384 changeset: 5:97f8bfe72746
384 changeset: 5:97f8bfe72746
385 branch: stable
385 branch: stable
386 parent: 3:4c60f11aa304
386 parent: 3:4c60f11aa304
387 user: test
387 user: test
388 date: Thu Jan 01 00:00:00 1970 +0000
388 date: Thu Jan 01 00:00:00 1970 +0000
389 summary: 5
389 summary: 5
390
390
391 $ hg log -r 'origin(7)'
391 $ hg log -r 'origin(7)'
392 changeset: 2:5c095ad7e90f
392 changeset: 2:5c095ad7e90f
393 user: test
393 user: test
394 date: Thu Jan 01 00:00:00 1970 +0000
394 date: Thu Jan 01 00:00:00 1970 +0000
395 summary: 2
395 summary: 2
396
396
397 Now transplant a graft to test following through copies
397 Now transplant a graft to test following through copies
398 $ hg up -q 0
398 $ hg up -q 0
399 $ hg branch -q dev
399 $ hg branch -q dev
400 $ hg ci -qm "dev branch"
400 $ hg ci -qm "dev branch"
401 $ hg --config extensions.transplant= transplant -q 7
401 $ hg --config extensions.transplant= transplant -q 7
402 $ hg log -r 'origin(.)'
402 $ hg log -r 'origin(.)'
403 changeset: 2:5c095ad7e90f
403 changeset: 2:5c095ad7e90f
404 user: test
404 user: test
405 date: Thu Jan 01 00:00:00 1970 +0000
405 date: Thu Jan 01 00:00:00 1970 +0000
406 summary: 2
406 summary: 2
407
407
408 Test simple destination
408 Test simple destination
409 $ hg log -r 'destination()'
409 $ hg log -r 'destination()'
410 changeset: 7:ef0ef43d49e7
410 changeset: 7:ef0ef43d49e7
411 parent: 0:68795b066622
411 parent: 0:68795b066622
412 user: foo
412 user: foo
413 date: Thu Jan 01 00:00:00 1970 +0000
413 date: Thu Jan 01 00:00:00 1970 +0000
414 summary: 2
414 summary: 2
415
415
416 changeset: 8:6b9e5368ca4e
416 changeset: 8:6b9e5368ca4e
417 user: bar
417 user: bar
418 date: Thu Jan 01 00:00:00 1970 +0000
418 date: Thu Jan 01 00:00:00 1970 +0000
419 summary: 1
419 summary: 1
420
420
421 changeset: 9:1905859650ec
421 changeset: 9:1905859650ec
422 user: test
422 user: test
423 date: Thu Jan 01 00:00:00 1970 +0000
423 date: Thu Jan 01 00:00:00 1970 +0000
424 summary: 5
424 summary: 5
425
425
426 changeset: 10:52dc0b4c6907
426 changeset: 10:52dc0b4c6907
427 user: test
427 user: test
428 date: Thu Jan 01 00:00:00 1970 +0000
428 date: Thu Jan 01 00:00:00 1970 +0000
429 summary: 4
429 summary: 4
430
430
431 changeset: 11:882b35362a6b
431 changeset: 11:882b35362a6b
432 user: test
432 user: test
433 date: Thu Jan 01 00:00:00 1970 +0000
433 date: Thu Jan 01 00:00:00 1970 +0000
434 summary: 3
434 summary: 3
435
435
436 changeset: 13:9db0f28fd374
436 changeset: 13:9db0f28fd374
437 user: foo
437 user: foo
438 date: Thu Jan 01 00:00:00 1970 +0000
438 date: Thu Jan 01 00:00:00 1970 +0000
439 summary: 2
439 summary: 2
440
440
441 changeset: 14:f64defefacee
441 changeset: 14:f64defefacee
442 parent: 1:5d205f8b35b6
442 parent: 1:5d205f8b35b6
443 user: foo
443 user: foo
444 date: Thu Jan 01 00:00:00 1970 +0000
444 date: Thu Jan 01 00:00:00 1970 +0000
445 summary: 3
445 summary: 3
446
446
447 changeset: 17:64ecd9071ce8
447 changeset: 17:64ecd9071ce8
448 user: bar
448 user: bar
449 date: Thu Jan 01 00:00:00 1970 +0000
449 date: Thu Jan 01 00:00:00 1970 +0000
450 summary: 1
450 summary: 1
451
451
452 changeset: 19:2e80e1351d6e
452 changeset: 19:2e80e1351d6e
453 user: test
453 user: test
454 date: Thu Jan 01 00:00:00 1970 +0000
454 date: Thu Jan 01 00:00:00 1970 +0000
455 summary: 2
455 summary: 2
456
456
457 changeset: 21:7e61b508e709
457 changeset: 21:7e61b508e709
458 branch: dev
458 branch: dev
459 tag: tip
459 tag: tip
460 user: foo
460 user: foo
461 date: Thu Jan 01 00:00:00 1970 +0000
461 date: Thu Jan 01 00:00:00 1970 +0000
462 summary: 2
462 summary: 2
463
463
464 $ hg log -r 'destination(2)'
464 $ hg log -r 'destination(2)'
465 changeset: 7:ef0ef43d49e7
465 changeset: 7:ef0ef43d49e7
466 parent: 0:68795b066622
466 parent: 0:68795b066622
467 user: foo
467 user: foo
468 date: Thu Jan 01 00:00:00 1970 +0000
468 date: Thu Jan 01 00:00:00 1970 +0000
469 summary: 2
469 summary: 2
470
470
471 changeset: 13:9db0f28fd374
471 changeset: 13:9db0f28fd374
472 user: foo
472 user: foo
473 date: Thu Jan 01 00:00:00 1970 +0000
473 date: Thu Jan 01 00:00:00 1970 +0000
474 summary: 2
474 summary: 2
475
475
476 changeset: 19:2e80e1351d6e
476 changeset: 19:2e80e1351d6e
477 user: test
477 user: test
478 date: Thu Jan 01 00:00:00 1970 +0000
478 date: Thu Jan 01 00:00:00 1970 +0000
479 summary: 2
479 summary: 2
480
480
481 changeset: 21:7e61b508e709
481 changeset: 21:7e61b508e709
482 branch: dev
482 branch: dev
483 tag: tip
483 tag: tip
484 user: foo
484 user: foo
485 date: Thu Jan 01 00:00:00 1970 +0000
485 date: Thu Jan 01 00:00:00 1970 +0000
486 summary: 2
486 summary: 2
487
487
488 Transplants of grafts can find a destination...
488 Transplants of grafts can find a destination...
489 $ hg log -r 'destination(7)'
489 $ hg log -r 'destination(7)'
490 changeset: 21:7e61b508e709
490 changeset: 21:7e61b508e709
491 branch: dev
491 branch: dev
492 tag: tip
492 tag: tip
493 user: foo
493 user: foo
494 date: Thu Jan 01 00:00:00 1970 +0000
494 date: Thu Jan 01 00:00:00 1970 +0000
495 summary: 2
495 summary: 2
496
496
497 ... grafts of grafts unfortunately can't
497 ... grafts of grafts unfortunately can't
498 $ hg graft -q 13
498 $ hg graft -q 13
499 $ hg log -r 'destination(13)'
499 $ hg log -r 'destination(13)'
500 All copies of a cset
500 All copies of a cset
501 $ hg log -r 'origin(13) or destination(origin(13))'
501 $ hg log -r 'origin(13) or destination(origin(13))'
502 changeset: 2:5c095ad7e90f
502 changeset: 2:5c095ad7e90f
503 user: test
503 user: test
504 date: Thu Jan 01 00:00:00 1970 +0000
504 date: Thu Jan 01 00:00:00 1970 +0000
505 summary: 2
505 summary: 2
506
506
507 changeset: 7:ef0ef43d49e7
507 changeset: 7:ef0ef43d49e7
508 parent: 0:68795b066622
508 parent: 0:68795b066622
509 user: foo
509 user: foo
510 date: Thu Jan 01 00:00:00 1970 +0000
510 date: Thu Jan 01 00:00:00 1970 +0000
511 summary: 2
511 summary: 2
512
512
513 changeset: 13:9db0f28fd374
513 changeset: 13:9db0f28fd374
514 user: foo
514 user: foo
515 date: Thu Jan 01 00:00:00 1970 +0000
515 date: Thu Jan 01 00:00:00 1970 +0000
516 summary: 2
516 summary: 2
517
517
518 changeset: 19:2e80e1351d6e
518 changeset: 19:2e80e1351d6e
519 user: test
519 user: test
520 date: Thu Jan 01 00:00:00 1970 +0000
520 date: Thu Jan 01 00:00:00 1970 +0000
521 summary: 2
521 summary: 2
522
522
523 changeset: 21:7e61b508e709
523 changeset: 21:7e61b508e709
524 branch: dev
524 branch: dev
525 user: foo
525 user: foo
526 date: Thu Jan 01 00:00:00 1970 +0000
526 date: Thu Jan 01 00:00:00 1970 +0000
527 summary: 2
527 summary: 2
528
528
529 changeset: 22:1313d0a825e2
529 changeset: 22:1313d0a825e2
530 branch: dev
530 branch: dev
531 tag: tip
531 tag: tip
532 user: foo
532 user: foo
533 date: Thu Jan 01 00:00:00 1970 +0000
533 date: Thu Jan 01 00:00:00 1970 +0000
534 summary: 2
534 summary: 2
535
535
@@ -1,159 +1,159 b''
1 $ hg init t
1 $ hg init t
2 $ cd t
2 $ cd t
3
3
4 $ mkdir a
4 $ mkdir a
5 $ echo foo > a/a
5 $ echo foo > a/a
6 $ echo bar > a/b
6 $ echo bar > a/b
7 $ hg ci -Am "0"
7 $ hg ci -Am "0"
8 adding a/a
8 adding a/a
9 adding a/b
9 adding a/b
10
10
11 $ hg co -C 0
11 $ hg co -C 0
12 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
12 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
13 $ hg mv a b
13 $ hg mv a b
14 moving a/a to b/a (glob)
14 moving a/a to b/a (glob)
15 moving a/b to b/b (glob)
15 moving a/b to b/b (glob)
16 $ hg ci -m "1 mv a/ b/"
16 $ hg ci -m "1 mv a/ b/"
17
17
18 $ hg co -C 0
18 $ hg co -C 0
19 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
19 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
20 $ echo baz > a/c
20 $ echo baz > a/c
21 $ echo quux > a/d
21 $ echo quux > a/d
22 $ hg add a/c
22 $ hg add a/c
23 $ hg ci -m "2 add a/c"
23 $ hg ci -m "2 add a/c"
24 created new head
24 created new head
25
25
26 $ hg merge --debug 1
26 $ hg merge --debug 1
27 searching for copies back to rev 1
27 searching for copies back to rev 1
28 unmatched files in local:
28 unmatched files in local:
29 a/c
29 a/c
30 unmatched files in other:
30 unmatched files in other:
31 b/a
31 b/a
32 b/b
32 b/b
33 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
33 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
34 src: 'a/a' -> dst: 'b/a'
34 src: 'a/a' -> dst: 'b/a'
35 src: 'a/b' -> dst: 'b/b'
35 src: 'a/b' -> dst: 'b/b'
36 checking for directory renames
36 checking for directory renames
37 discovered dir src: 'a/' -> dst: 'b/'
37 discovered dir src: 'a/' -> dst: 'b/'
38 pending file src: 'a/c' -> dst: 'b/c'
38 pending file src: 'a/c' -> dst: 'b/c'
39 resolving manifests
39 resolving manifests
40 overwrite: False, partial: False
40 overwrite: False, partial: False
41 ancestor: f9b20c0d4c51, local: ce36d17b18fb+, remote: 397f8b00a740
41 ancestor: f9b20c0d4c51, local: ce36d17b18fb+, remote: 397f8b00a740
42 a/c: remote renamed directory to b/c -> d
42 a/a: other deleted -> r
43 a/b: other deleted -> r
43 a/b: other deleted -> r
44 a/a: other deleted -> r
44 a/c: remote renamed directory to b/c -> d
45 b/a: remote created -> g
45 b/a: remote created -> g
46 b/b: remote created -> g
46 b/b: remote created -> g
47 updating: a/a 1/5 files (20.00%)
47 updating: a/a 1/5 files (20.00%)
48 removing a/a
48 removing a/a
49 updating: a/b 2/5 files (40.00%)
49 updating: a/b 2/5 files (40.00%)
50 removing a/b
50 removing a/b
51 updating: a/c 3/5 files (60.00%)
51 updating: a/c 3/5 files (60.00%)
52 moving a/c to b/c
52 moving a/c to b/c
53 updating: b/a 4/5 files (80.00%)
53 updating: b/a 4/5 files (80.00%)
54 getting b/a
54 getting b/a
55 updating: b/b 5/5 files (100.00%)
55 updating: b/b 5/5 files (100.00%)
56 getting b/b
56 getting b/b
57 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
57 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
58 (branch merge, don't forget to commit)
58 (branch merge, don't forget to commit)
59
59
60 $ echo a/* b/*
60 $ echo a/* b/*
61 a/d b/a b/b b/c
61 a/d b/a b/b b/c
62 $ hg st -C
62 $ hg st -C
63 M b/a
63 M b/a
64 M b/b
64 M b/b
65 A b/c
65 A b/c
66 a/c
66 a/c
67 R a/a
67 R a/a
68 R a/b
68 R a/b
69 R a/c
69 R a/c
70 ? a/d
70 ? a/d
71 $ hg ci -m "3 merge 2+1"
71 $ hg ci -m "3 merge 2+1"
72 $ hg debugrename b/c
72 $ hg debugrename b/c
73 b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88 (glob)
73 b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88 (glob)
74
74
75 $ hg co -C 1
75 $ hg co -C 1
76 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
76 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
77 $ hg merge --debug 2
77 $ hg merge --debug 2
78 searching for copies back to rev 1
78 searching for copies back to rev 1
79 unmatched files in local:
79 unmatched files in local:
80 b/a
80 b/a
81 b/b
81 b/b
82 unmatched files in other:
82 unmatched files in other:
83 a/c
83 a/c
84 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
84 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
85 src: 'a/a' -> dst: 'b/a'
85 src: 'a/a' -> dst: 'b/a'
86 src: 'a/b' -> dst: 'b/b'
86 src: 'a/b' -> dst: 'b/b'
87 checking for directory renames
87 checking for directory renames
88 discovered dir src: 'a/' -> dst: 'b/'
88 discovered dir src: 'a/' -> dst: 'b/'
89 pending file src: 'a/c' -> dst: 'b/c'
89 pending file src: 'a/c' -> dst: 'b/c'
90 resolving manifests
90 resolving manifests
91 overwrite: False, partial: False
91 overwrite: False, partial: False
92 ancestor: f9b20c0d4c51, local: 397f8b00a740+, remote: ce36d17b18fb
92 ancestor: f9b20c0d4c51, local: 397f8b00a740+, remote: ce36d17b18fb
93 None: local renamed directory to b/c -> d
93 None: local renamed directory to b/c -> d
94 updating:None 1/1 files (100.00%)
94 updating:None 1/1 files (100.00%)
95 getting a/c to b/c
95 getting a/c to b/c
96 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
96 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
97 (branch merge, don't forget to commit)
97 (branch merge, don't forget to commit)
98
98
99 $ echo a/* b/*
99 $ echo a/* b/*
100 a/d b/a b/b b/c
100 a/d b/a b/b b/c
101 $ hg st -C
101 $ hg st -C
102 A b/c
102 A b/c
103 a/c
103 a/c
104 ? a/d
104 ? a/d
105 $ hg ci -m "4 merge 1+2"
105 $ hg ci -m "4 merge 1+2"
106 created new head
106 created new head
107 $ hg debugrename b/c
107 $ hg debugrename b/c
108 b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88 (glob)
108 b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88 (glob)
109
109
110
110
111 Second scenario with two repos:
111 Second scenario with two repos:
112
112
113 $ cd ..
113 $ cd ..
114 $ hg init r1
114 $ hg init r1
115 $ cd r1
115 $ cd r1
116 $ mkdir a
116 $ mkdir a
117 $ echo foo > a/f
117 $ echo foo > a/f
118 $ hg add a
118 $ hg add a
119 adding a/f (glob)
119 adding a/f (glob)
120 $ hg ci -m "a/f == foo"
120 $ hg ci -m "a/f == foo"
121 $ cd ..
121 $ cd ..
122
122
123 $ hg clone r1 r2
123 $ hg clone r1 r2
124 updating to branch default
124 updating to branch default
125 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
125 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
126 $ cd r2
126 $ cd r2
127 $ hg mv a b
127 $ hg mv a b
128 moving a/f to b/f (glob)
128 moving a/f to b/f (glob)
129 $ echo foo1 > b/f
129 $ echo foo1 > b/f
130 $ hg ci -m" a -> b, b/f == foo1"
130 $ hg ci -m" a -> b, b/f == foo1"
131 $ cd ..
131 $ cd ..
132
132
133 $ cd r1
133 $ cd r1
134 $ mkdir a/aa
134 $ mkdir a/aa
135 $ echo bar > a/aa/g
135 $ echo bar > a/aa/g
136 $ hg add a/aa
136 $ hg add a/aa
137 adding a/aa/g (glob)
137 adding a/aa/g (glob)
138 $ hg ci -m "a/aa/g"
138 $ hg ci -m "a/aa/g"
139 $ hg pull ../r2
139 $ hg pull ../r2
140 pulling from ../r2
140 pulling from ../r2
141 searching for changes
141 searching for changes
142 adding changesets
142 adding changesets
143 adding manifests
143 adding manifests
144 adding file changes
144 adding file changes
145 added 1 changesets with 1 changes to 1 files (+1 heads)
145 added 1 changesets with 1 changes to 1 files (+1 heads)
146 (run 'hg heads' to see heads, 'hg merge' to merge)
146 (run 'hg heads' to see heads, 'hg merge' to merge)
147
147
148 $ hg merge
148 $ hg merge
149 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
149 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
150 (branch merge, don't forget to commit)
150 (branch merge, don't forget to commit)
151
151
152 $ hg st -C
152 $ hg st -C
153 M b/f
153 M b/f
154 A b/aa/g
154 A b/aa/g
155 a/aa/g
155 a/aa/g
156 R a/aa/g
156 R a/aa/g
157 R a/f
157 R a/f
158
158
159 $ cd ..
159 $ cd ..
@@ -1,85 +1,85 b''
1 $ hg init
1 $ hg init
2
2
3 $ touch a
3 $ touch a
4 $ hg add a
4 $ hg add a
5 $ hg commit -m "Added a"
5 $ hg commit -m "Added a"
6
6
7 $ touch main
7 $ touch main
8 $ hg add main
8 $ hg add main
9 $ hg commit -m "Added main"
9 $ hg commit -m "Added main"
10 $ hg checkout 0
10 $ hg checkout 0
11 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
11 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
12
12
13 'main' should be gone:
13 'main' should be gone:
14
14
15 $ ls
15 $ ls
16 a
16 a
17
17
18 $ touch side1
18 $ touch side1
19 $ hg add side1
19 $ hg add side1
20 $ hg commit -m "Added side1"
20 $ hg commit -m "Added side1"
21 created new head
21 created new head
22 $ touch side2
22 $ touch side2
23 $ hg add side2
23 $ hg add side2
24 $ hg commit -m "Added side2"
24 $ hg commit -m "Added side2"
25
25
26 $ hg log
26 $ hg log
27 changeset: 3:91ebc10ed028
27 changeset: 3:91ebc10ed028
28 tag: tip
28 tag: tip
29 user: test
29 user: test
30 date: Thu Jan 01 00:00:00 1970 +0000
30 date: Thu Jan 01 00:00:00 1970 +0000
31 summary: Added side2
31 summary: Added side2
32
32
33 changeset: 2:b932d7dbb1e1
33 changeset: 2:b932d7dbb1e1
34 parent: 0:c2eda428b523
34 parent: 0:c2eda428b523
35 user: test
35 user: test
36 date: Thu Jan 01 00:00:00 1970 +0000
36 date: Thu Jan 01 00:00:00 1970 +0000
37 summary: Added side1
37 summary: Added side1
38
38
39 changeset: 1:71a760306caf
39 changeset: 1:71a760306caf
40 user: test
40 user: test
41 date: Thu Jan 01 00:00:00 1970 +0000
41 date: Thu Jan 01 00:00:00 1970 +0000
42 summary: Added main
42 summary: Added main
43
43
44 changeset: 0:c2eda428b523
44 changeset: 0:c2eda428b523
45 user: test
45 user: test
46 date: Thu Jan 01 00:00:00 1970 +0000
46 date: Thu Jan 01 00:00:00 1970 +0000
47 summary: Added a
47 summary: Added a
48
48
49
49
50 $ hg heads
50 $ hg heads
51 changeset: 3:91ebc10ed028
51 changeset: 3:91ebc10ed028
52 tag: tip
52 tag: tip
53 user: test
53 user: test
54 date: Thu Jan 01 00:00:00 1970 +0000
54 date: Thu Jan 01 00:00:00 1970 +0000
55 summary: Added side2
55 summary: Added side2
56
56
57 changeset: 1:71a760306caf
57 changeset: 1:71a760306caf
58 user: test
58 user: test
59 date: Thu Jan 01 00:00:00 1970 +0000
59 date: Thu Jan 01 00:00:00 1970 +0000
60 summary: Added main
60 summary: Added main
61
61
62 $ ls
62 $ ls
63 a
63 a
64 side1
64 side1
65 side2
65 side2
66
66
67 $ hg update --debug -C 1
67 $ hg update --debug -C 1
68 resolving manifests
68 resolving manifests
69 overwrite: True, partial: False
69 overwrite: True, partial: False
70 ancestor: 91ebc10ed028+, local: 91ebc10ed028+, remote: 71a760306caf
70 ancestor: 91ebc10ed028+, local: 91ebc10ed028+, remote: 71a760306caf
71 side1: other deleted -> r
71 side2: other deleted -> r
72 side2: other deleted -> r
72 side1: other deleted -> r
73 main: remote created -> g
73 main: remote created -> g
74 updating: side1 1/3 files (33.33%)
74 updating: side1 1/3 files (33.33%)
75 removing side1
75 removing side1
76 updating: side2 2/3 files (66.67%)
76 updating: side2 2/3 files (66.67%)
77 removing side2
77 removing side2
78 updating: main 3/3 files (100.00%)
78 updating: main 3/3 files (100.00%)
79 getting main
79 getting main
80 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
80 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
81
81
82 $ ls
82 $ ls
83 a
83 a
84 main
84 main
85
85
General Comments 0
You need to be logged in to leave comments. Login now