##// END OF EJS Templates
imerge: gussy up dispatcher to support subcommand opts....
Brendan Cully -
r5111:12930b97 default
parent child Browse files
Show More
@@ -1,344 +1,361 b''
1 # Copyright (C) 2007 Brendan Cully <brendan@kublai.com>
1 # Copyright (C) 2007 Brendan Cully <brendan@kublai.com>
2 # Published under the GNU GPL
2 # Published under the GNU GPL
3
3
4 '''
4 '''
5 imerge - interactive merge
5 imerge - interactive merge
6 '''
6 '''
7
7
8 from mercurial.i18n import _
8 from mercurial.i18n import _
9 from mercurial.node import *
9 from mercurial.node import *
10 from mercurial import commands, cmdutil, hg, merge, util
10 from mercurial import commands, cmdutil, fancyopts, hg, merge, util
11 import os, tarfile
11 import os, tarfile
12
12
13 class InvalidStateFileException(Exception): pass
13 class InvalidStateFileException(Exception): pass
14
14
15 class ImergeStateFile(object):
15 class ImergeStateFile(object):
16 def __init__(self, im):
16 def __init__(self, im):
17 self.im = im
17 self.im = im
18
18
19 def save(self, dest):
19 def save(self, dest):
20 tf = tarfile.open(dest, 'w:gz')
20 tf = tarfile.open(dest, 'w:gz')
21
21
22 st = os.path.join(self.im.path, 'status')
22 st = os.path.join(self.im.path, 'status')
23 tf.add(st, os.path.join('.hg', 'imerge', 'status'))
23 tf.add(st, os.path.join('.hg', 'imerge', 'status'))
24
24
25 for f in self.im.resolved:
25 for f in self.im.resolved:
26 (fd, fo) = self.im.conflicts[f]
26 (fd, fo) = self.im.conflicts[f]
27 abssrc = self.im.repo.wjoin(fd)
27 abssrc = self.im.repo.wjoin(fd)
28 tf.add(abssrc, fd)
28 tf.add(abssrc, fd)
29
29
30 tf.close()
30 tf.close()
31
31
32 def load(self, source):
32 def load(self, source):
33 wlock = self.im.repo.wlock()
33 wlock = self.im.repo.wlock()
34 lock = self.im.repo.lock()
34 lock = self.im.repo.lock()
35
35
36 tf = tarfile.open(source, 'r')
36 tf = tarfile.open(source, 'r')
37 contents = tf.getnames()
37 contents = tf.getnames()
38 statusfile = os.path.join('.hg', 'imerge', 'status')
38 statusfile = os.path.join('.hg', 'imerge', 'status')
39 if statusfile not in contents:
39 if statusfile not in contents:
40 raise InvalidStateFileException('no status file')
40 raise InvalidStateFileException('no status file')
41
41
42 tf.extract(statusfile, self.im.repo.root)
42 tf.extract(statusfile, self.im.repo.root)
43 p1, p2 = self.im.load()
43 p1, p2 = self.im.load()
44 if self.im.repo.dirstate.parents()[0] != p1.node():
44 if self.im.repo.dirstate.parents()[0] != p1.node():
45 hg.clean(self.im.repo, p1.node())
45 hg.clean(self.im.repo, p1.node())
46 self.im.start(p2.node())
46 self.im.start(p2.node())
47 for tarinfo in tf:
47 for tarinfo in tf:
48 tf.extract(tarinfo, self.im.repo.root)
48 tf.extract(tarinfo, self.im.repo.root)
49 self.im.load()
49 self.im.load()
50
50
51 class Imerge(object):
51 class Imerge(object):
52 def __init__(self, ui, repo):
52 def __init__(self, ui, repo):
53 self.ui = ui
53 self.ui = ui
54 self.repo = repo
54 self.repo = repo
55
55
56 self.path = repo.join('imerge')
56 self.path = repo.join('imerge')
57 self.opener = util.opener(self.path)
57 self.opener = util.opener(self.path)
58
58
59 self.wctx = self.repo.workingctx()
59 self.wctx = self.repo.workingctx()
60 self.conflicts = {}
60 self.conflicts = {}
61 self.resolved = []
61 self.resolved = []
62
62
63 def merging(self):
63 def merging(self):
64 return len(self.wctx.parents()) > 1
64 return len(self.wctx.parents()) > 1
65
65
66 def load(self):
66 def load(self):
67 # status format. \0-delimited file, fields are
67 # status format. \0-delimited file, fields are
68 # p1, p2, conflict count, conflict filenames, resolved filenames
68 # p1, p2, conflict count, conflict filenames, resolved filenames
69 # conflict filenames are tuples of localname, remoteorig, remotenew
69 # conflict filenames are tuples of localname, remoteorig, remotenew
70
70
71 statusfile = self.opener('status')
71 statusfile = self.opener('status')
72
72
73 status = statusfile.read().split('\0')
73 status = statusfile.read().split('\0')
74 if len(status) < 3:
74 if len(status) < 3:
75 raise util.Abort('invalid imerge status file')
75 raise util.Abort('invalid imerge status file')
76
76
77 try:
77 try:
78 parents = [self.repo.changectx(n) for n in status[:2]]
78 parents = [self.repo.changectx(n) for n in status[:2]]
79 except LookupError:
79 except LookupError:
80 raise util.Abort('merge parent %s not in repository' % short(p))
80 raise util.Abort('merge parent %s not in repository' % short(p))
81
81
82 status = status[2:]
82 status = status[2:]
83 conflicts = int(status.pop(0)) * 3
83 conflicts = int(status.pop(0)) * 3
84 self.resolved = status[conflicts:]
84 self.resolved = status[conflicts:]
85 for i in xrange(0, conflicts, 3):
85 for i in xrange(0, conflicts, 3):
86 self.conflicts[status[i]] = (status[i+1], status[i+2])
86 self.conflicts[status[i]] = (status[i+1], status[i+2])
87
87
88 return parents
88 return parents
89
89
90 def save(self):
90 def save(self):
91 lock = self.repo.lock()
91 lock = self.repo.lock()
92
92
93 if not os.path.isdir(self.path):
93 if not os.path.isdir(self.path):
94 os.mkdir(self.path)
94 os.mkdir(self.path)
95 statusfile = self.opener('status', 'wb')
95 statusfile = self.opener('status', 'wb')
96
96
97 out = [hex(n.node()) for n in self.wctx.parents()]
97 out = [hex(n.node()) for n in self.wctx.parents()]
98 out.append(str(len(self.conflicts)))
98 out.append(str(len(self.conflicts)))
99 conflicts = self.conflicts.items()
99 conflicts = self.conflicts.items()
100 conflicts.sort()
100 conflicts.sort()
101 for fw, fd_fo in conflicts:
101 for fw, fd_fo in conflicts:
102 out.append(fw)
102 out.append(fw)
103 out.extend(fd_fo)
103 out.extend(fd_fo)
104 out.extend(self.resolved)
104 out.extend(self.resolved)
105
105
106 statusfile.write('\0'.join(out))
106 statusfile.write('\0'.join(out))
107
107
108 def remaining(self):
108 def remaining(self):
109 return [f for f in self.conflicts if f not in self.resolved]
109 return [f for f in self.conflicts if f not in self.resolved]
110
110
111 def filemerge(self, fn):
111 def filemerge(self, fn):
112 wlock = self.repo.wlock()
112 wlock = self.repo.wlock()
113
113
114 (fd, fo) = self.conflicts[fn]
114 (fd, fo) = self.conflicts[fn]
115 p2 = self.wctx.parents()[1]
115 p2 = self.wctx.parents()[1]
116 return merge.filemerge(self.repo, fn, fd, fo, self.wctx, p2)
116 return merge.filemerge(self.repo, fn, fd, fo, self.wctx, p2)
117
117
118 def start(self, rev=None):
118 def start(self, rev=None):
119 _filemerge = merge.filemerge
119 _filemerge = merge.filemerge
120 def filemerge(repo, fw, fd, fo, wctx, mctx):
120 def filemerge(repo, fw, fd, fo, wctx, mctx):
121 self.conflicts[fw] = (fd, fo)
121 self.conflicts[fw] = (fd, fo)
122
122
123 merge.filemerge = filemerge
123 merge.filemerge = filemerge
124 commands.merge(self.ui, self.repo, rev=rev)
124 commands.merge(self.ui, self.repo, rev=rev)
125 merge.filemerge = _filemerge
125 merge.filemerge = _filemerge
126
126
127 self.wctx = self.repo.workingctx()
127 self.wctx = self.repo.workingctx()
128 self.save()
128 self.save()
129
129
130 def resume(self):
130 def resume(self):
131 self.load()
131 self.load()
132
132
133 dp = self.repo.dirstate.parents()
133 dp = self.repo.dirstate.parents()
134 p1, p2 = self.wctx.parents()
134 p1, p2 = self.wctx.parents()
135 if p1.node() != dp[0] or p2.node() != dp[1]:
135 if p1.node() != dp[0] or p2.node() != dp[1]:
136 raise util.Abort('imerge state does not match working directory')
136 raise util.Abort('imerge state does not match working directory')
137
137
138 def status(self):
139 p1, p2 = self.wctx.parents()
140 self.ui.write('merging %s and %s\n' % \
141 (short(p1.node()), short(p2.node())))
142
143 if self.resolved:
144 self.ui.write('resolved:\n')
145 for fn in self.resolved:
146 self.ui.write(' %s\n' % fn)
147 remaining = [f for f in self.conflicts if f not in self.resolved]
148 if remaining:
149 self.ui.write('remaining:\n')
150 for fn in remaining:
151 (fd, fo) = self.conflicts[fn]
152 if fn == fo:
153 self.ui.write(' %s\n' % (fn,))
154 else:
155 self.ui.write(' %s (%s)\n' % (fn, fd))
156 else:
157 self.ui.write('all conflicts resolved\n')
158
159 def next(self):
138 def next(self):
160 remaining = self.remaining()
139 remaining = self.remaining()
161 return remaining and remaining[0]
140 return remaining and remaining[0]
162
141
163 def resolve(self, files):
142 def resolve(self, files):
164 resolved = dict.fromkeys(self.resolved)
143 resolved = dict.fromkeys(self.resolved)
165 for fn in files:
144 for fn in files:
166 if fn not in self.conflicts:
145 if fn not in self.conflicts:
167 raise util.Abort('%s is not in the merge set' % fn)
146 raise util.Abort('%s is not in the merge set' % fn)
168 resolved[fn] = True
147 resolved[fn] = True
169 self.resolved = resolved.keys()
148 self.resolved = resolved.keys()
170 self.resolved.sort()
149 self.resolved.sort()
171 self.save()
150 self.save()
172 return 0
151 return 0
173
152
174 def unresolve(self, files):
153 def unresolve(self, files):
175 resolved = dict.fromkeys(self.resolved)
154 resolved = dict.fromkeys(self.resolved)
176 for fn in files:
155 for fn in files:
177 if fn not in resolved:
156 if fn not in resolved:
178 raise util.Abort('%s is not resolved' % fn)
157 raise util.Abort('%s is not resolved' % fn)
179 del resolved[fn]
158 del resolved[fn]
180 self.resolved = resolved.keys()
159 self.resolved = resolved.keys()
181 self.resolved.sort()
160 self.resolved.sort()
182 self.save()
161 self.save()
183 return 0
162 return 0
184
163
185 def pickle(self, dest):
164 def pickle(self, dest):
186 '''write current merge state to file to be resumed elsewhere'''
165 '''write current merge state to file to be resumed elsewhere'''
187 state = ImergeStateFile(self)
166 state = ImergeStateFile(self)
188 return state.save(dest)
167 return state.save(dest)
189
168
190 def unpickle(self, source):
169 def unpickle(self, source):
191 '''read merge state from file'''
170 '''read merge state from file'''
192 state = ImergeStateFile(self)
171 state = ImergeStateFile(self)
193 return state.load(source)
172 return state.load(source)
194
173
195 def load(im, source):
174 def load(im, source):
196 if im.merging():
175 if im.merging():
197 raise util.Abort('there is already a merge in progress '
176 raise util.Abort('there is already a merge in progress '
198 '(update -C <rev> to abort it)' )
177 '(update -C <rev> to abort it)' )
199 m, a, r, d = im.repo.status()[:4]
178 m, a, r, d = im.repo.status()[:4]
200 if m or a or r or d:
179 if m or a or r or d:
201 raise util.Abort('working directory has uncommitted changes')
180 raise util.Abort('working directory has uncommitted changes')
202
181
203 rc = im.unpickle(source)
182 rc = im.unpickle(source)
204 if not rc:
183 if not rc:
205 im.status()
184 status(im)
206 return rc
185 return rc
207
186
208 def merge_(im, filename=None):
187 def merge_(im, filename=None):
209 if not filename:
188 if not filename:
210 filename = im.next()
189 filename = im.next()
211 if not filename:
190 if not filename:
212 im.ui.write('all conflicts resolved\n')
191 im.ui.write('all conflicts resolved\n')
213 return 0
192 return 0
214
193
215 rc = im.filemerge(filename)
194 rc = im.filemerge(filename)
216 if not rc:
195 if not rc:
217 im.resolve([filename])
196 im.resolve([filename])
218 if not im.next():
197 if not im.next():
219 im.ui.write('all conflicts resolved\n')
198 im.ui.write('all conflicts resolved\n')
220 return 0
199 return 0
221 return rc
200 return rc
222
201
223 def next(im):
202 def next(im):
224 n = im.next()
203 n = im.next()
225 if n:
204 if n:
226 im.ui.write('%s\n' % n)
205 im.ui.write('%s\n' % n)
227 else:
206 else:
228 im.ui.write('all conflicts resolved\n')
207 im.ui.write('all conflicts resolved\n')
229 return 0
208 return 0
230
209
231 def resolve(im, *files):
210 def resolve(im, *files):
232 if not files:
211 if not files:
233 raise util.Abort('resolve requires at least one filename')
212 raise util.Abort('resolve requires at least one filename')
234 return im.resolve(files)
213 return im.resolve(files)
235
214
236 def save(im, dest):
215 def save(im, dest):
237 return im.pickle(dest)
216 return im.pickle(dest)
238
217
239 def status(im):
218 def status(im, **opts):
240 im.status()
219 if not opts.get('resolved') and not opts.get('unresolved'):
220 opts['resolved'] = True
221 opts['unresolved'] = True
222
223 if im.ui.verbose:
224 p1, p2 = [short(p.node()) for p in im.wctx.parents()]
225 im.ui.note(_('merging %s and %s\n') % (p1, p2))
226
227 conflicts = im.conflicts.keys()
228 conflicts.sort()
229 remaining = dict.fromkeys(im.remaining())
230 st = []
231 for fn in conflicts:
232 if opts.get('no_status'):
233 mode = ''
234 elif fn in remaining:
235 mode = 'U '
236 else:
237 mode = 'R '
238 if ((opts.get('resolved') and fn not in remaining)
239 or (opts.get('unresolved') and fn in remaining)):
240 st.append((mode, fn))
241 st.sort()
242 for (mode, fn) in st:
243 if im.ui.verbose:
244 fo, fd = im.conflicts[fn]
245 if fd != fn:
246 fn = '%s (%s)' % (fn, fd)
247 im.ui.write('%s%s\n' % (mode, fn))
248 if opts.get('unresolved') and not remaining:
249 im.ui.write(_('all conflicts resolved\n'))
250
241 return 0
251 return 0
242
252
243 def unresolve(im, *files):
253 def unresolve(im, *files):
244 if not files:
254 if not files:
245 raise util.Abort('unresolve requires at least one filename')
255 raise util.Abort('unresolve requires at least one filename')
246 return im.unresolve(files)
256 return im.unresolve(files)
247
257
248 subcmdtable = {
258 subcmdtable = {
249 'load': load,
259 'load': (load, []),
250 'merge': merge_,
260 'merge': (merge_, []),
251 'next': next,
261 'next': (next, []),
252 'resolve': resolve,
262 'resolve': (resolve, []),
253 'save': save,
263 'save': (save, []),
254 'status': status,
264 'status': (status,
255 'unresolve': unresolve
265 [('n', 'no-status', None, _('hide status prefix')),
266 ('', 'resolved', None, _('only show resolved conflicts')),
267 ('', 'unresolved', None, _('only show unresolved conflicts'))]),
268 'unresolve': (unresolve, [])
256 }
269 }
257
270
258 def dispatch(im, args, opts):
271 def dispatch(im, args, opts):
259 def complete(s, choices):
272 def complete(s, choices):
260 candidates = []
273 candidates = []
261 for choice in choices:
274 for choice in choices:
262 if choice.startswith(s):
275 if choice.startswith(s):
263 candidates.append(choice)
276 candidates.append(choice)
264 return candidates
277 return candidates
265
278
266 c, args = args[0], args[1:]
279 c, args = args[0], list(args[1:])
267 cmd = complete(c, subcmdtable.keys())
280 cmd = complete(c, subcmdtable.keys())
268 if not cmd:
281 if not cmd:
269 raise cmdutil.UnknownCommand('imerge ' + c)
282 raise cmdutil.UnknownCommand('imerge ' + c)
270 if len(cmd) > 1:
283 if len(cmd) > 1:
271 cmd.sort()
284 cmd.sort()
272 raise cmdutil.AmbiguousCommand('imerge ' + c, cmd)
285 raise cmdutil.AmbiguousCommand('imerge ' + c, cmd)
273 cmd = cmd[0]
286 cmd = cmd[0]
274
287
275 func = subcmdtable[cmd]
288 func, optlist = subcmdtable[cmd]
289 opts = {}
276 try:
290 try:
277 return func(im, *args)
291 args = fancyopts.fancyopts(args, optlist, opts)
292 return func(im, *args, **opts)
293 except fancyopts.getopt.GetoptError, inst:
294 raise cmdutil.ParseError('imerge', '%s: %s' % (cmd, inst))
278 except TypeError:
295 except TypeError:
279 raise cmdutil.ParseError('imerge', '%s: invalid arguments' % cmd)
296 raise cmdutil.ParseError('imerge', _('%s: invalid arguments') % cmd)
280
297
281 def imerge(ui, repo, *args, **opts):
298 def imerge(ui, repo, *args, **opts):
282 '''interactive merge
299 '''interactive merge
283
300
284 imerge lets you split a merge into pieces. When you start a merge
301 imerge lets you split a merge into pieces. When you start a merge
285 with imerge, the names of all files with conflicts are recorded.
302 with imerge, the names of all files with conflicts are recorded.
286 You can then merge any of these files, and if the merge is
303 You can then merge any of these files, and if the merge is
287 successful, they will be marked as resolved. When all files are
304 successful, they will be marked as resolved. When all files are
288 resolved, the merge is complete.
305 resolved, the merge is complete.
289
306
290 If no merge is in progress, hg imerge [rev] will merge the working
307 If no merge is in progress, hg imerge [rev] will merge the working
291 directory with rev (defaulting to the other head if the repository
308 directory with rev (defaulting to the other head if the repository
292 only has two heads). You may also resume a saved merge with
309 only has two heads). You may also resume a saved merge with
293 hg imerge load <file>.
310 hg imerge load <file>.
294
311
295 If a merge is in progress, hg imerge will default to merging the
312 If a merge is in progress, hg imerge will default to merging the
296 next unresolved file.
313 next unresolved file.
297
314
298 The following subcommands are available:
315 The following subcommands are available:
299
316
300 status:
317 status:
301 show the current state of the merge
318 show the current state of the merge
302 next:
319 next:
303 show the next unresolved file merge
320 show the next unresolved file merge
304 merge [<file>]:
321 merge [<file>]:
305 merge <file>. If the file merge is successful, the file will be
322 merge <file>. If the file merge is successful, the file will be
306 recorded as resolved. If no file is given, the next unresolved
323 recorded as resolved. If no file is given, the next unresolved
307 file will be merged.
324 file will be merged.
308 resolve <file>...:
325 resolve <file>...:
309 mark files as successfully merged
326 mark files as successfully merged
310 unresolve <file>...:
327 unresolve <file>...:
311 mark files as requiring merging.
328 mark files as requiring merging.
312 save <file>:
329 save <file>:
313 save the state of the merge to a file to be resumed elsewhere
330 save the state of the merge to a file to be resumed elsewhere
314 load <file>:
331 load <file>:
315 load the state of the merge from a file created by save
332 load the state of the merge from a file created by save
316 '''
333 '''
317
334
318 im = Imerge(ui, repo)
335 im = Imerge(ui, repo)
319
336
320 if im.merging():
337 if im.merging():
321 im.resume()
338 im.resume()
322 else:
339 else:
323 rev = opts.get('rev')
340 rev = opts.get('rev')
324 if rev and args:
341 if rev and args:
325 raise util.Abort('please specify just one revision')
342 raise util.Abort('please specify just one revision')
326
343
327 if len(args) == 2 and args[0] == 'load':
344 if len(args) == 2 and args[0] == 'load':
328 pass
345 pass
329 else:
346 else:
330 if args:
347 if args:
331 rev = args[0]
348 rev = args[0]
332 im.start(rev=rev)
349 im.start(rev=rev)
333 args = ['status']
350 args = ['status']
334
351
335 if not args:
352 if not args:
336 args = ['merge']
353 args = ['merge']
337
354
338 return dispatch(im, args, opts)
355 return dispatch(im, args, opts)
339
356
340 cmdtable = {
357 cmdtable = {
341 '^imerge':
358 '^imerge':
342 (imerge,
359 (imerge,
343 [('r', 'rev', '', _('revision to merge'))], 'hg imerge [command]')
360 [('r', 'rev', '', _('revision to merge'))], 'hg imerge [command]')
344 }
361 }
@@ -1,60 +1,60 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 echo "[extensions]" >> $HGRCPATH
3 echo "[extensions]" >> $HGRCPATH
4 echo "imerge=" >> $HGRCPATH
4 echo "imerge=" >> $HGRCPATH
5 HGMERGE=true
5 HGMERGE=true
6 export HGMERGE
6 export HGMERGE
7
7
8 hg init base
8 hg init base
9 cd base
9 cd base
10
10
11 echo foo > foo
11 echo foo > foo
12 echo bar > bar
12 echo bar > bar
13 hg ci -Am0 -d '0 0'
13 hg ci -Am0 -d '0 0'
14
14
15 hg mv foo foo2
15 hg mv foo foo2
16 echo foo >> foo2
16 echo foo >> foo2
17 hg ci -m1 -d '1 0'
17 hg ci -m1 -d '1 0'
18
18
19 hg up -C 0
19 hg up -C 0
20 echo bar >> foo
20 echo bar >> foo
21 echo bar >> bar
21 echo bar >> bar
22 hg ci -m2 -d '2 0'
22 hg ci -m2 -d '2 0'
23
23
24 echo % start imerge
24 echo % start imerge
25 hg imerge
25 hg imerge
26
26
27 cat foo2
27 cat foo2
28 cat bar
28 cat bar
29
29
30 echo % status
30 echo % status -v
31 hg imerge st
31 hg -v imerge st
32
32
33 echo % next
33 echo % next
34 hg imerge next
34 hg imerge next
35
35
36 echo % merge next
36 echo % merge next
37 hg --traceback imerge
37 hg --traceback imerge
38
38
39 echo % unresolve
39 echo % unresolve
40 hg imerge unres foo
40 hg imerge unres foo
41
41
42 echo % merge foo
42 echo % merge foo
43 hg imerge merge foo
43 hg imerge merge foo
44
44
45 echo % save
45 echo % save
46 echo foo > foo2
46 echo foo > foo2
47 hg imerge save ../savedmerge
47 hg imerge save ../savedmerge
48
48
49 echo % load
49 echo % load
50 hg up -C 0
50 hg up -C 0
51 hg imerge --traceback load ../savedmerge
51 hg imerge --traceback load ../savedmerge
52 cat foo2
52 cat foo2
53
53
54 hg ci -m'merged' -d '3 0'
54 hg ci -m'merged' -d '3 0'
55 hg tip -v
55 hg tip -v
56
56
57 echo % nothing to merge
57 echo % nothing to merge
58 hg imerge
58 hg imerge
59
59
60 exit 0
60 exit 0
@@ -1,50 +1,45 b''
1 adding bar
1 adding bar
2 adding foo
2 adding foo
3 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
3 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
4 % start imerge
4 % start imerge
5 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
5 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
6 (branch merge, don't forget to commit)
6 (branch merge, don't forget to commit)
7 merging e6da46716401 and 30d266f502e7
7 U foo
8 remaining:
9 foo (foo2)
10 foo
8 foo
11 bar
9 bar
12 bar
10 bar
13 bar
11 bar
14 % status
12 % status -v
15 merging e6da46716401 and 30d266f502e7
13 merging e6da46716401 and 30d266f502e7
16 remaining:
14 U foo (foo2)
17 foo (foo2)
18 % next
15 % next
19 foo
16 foo
20 % merge next
17 % merge next
21 merging foo and foo2
18 merging foo and foo2
22 all conflicts resolved
19 all conflicts resolved
23 % unresolve
20 % unresolve
24 % merge foo
21 % merge foo
25 merging foo and foo2
22 merging foo and foo2
26 all conflicts resolved
23 all conflicts resolved
27 % save
24 % save
28 % load
25 % load
29 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
26 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
30 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
27 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
31 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
28 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
32 (branch merge, don't forget to commit)
29 (branch merge, don't forget to commit)
33 merging e6da46716401 and 30d266f502e7
30 R foo
34 resolved:
35 foo
36 all conflicts resolved
31 all conflicts resolved
37 foo
32 foo
38 changeset: 3:fa9a6defdcaf
33 changeset: 3:fa9a6defdcaf
39 tag: tip
34 tag: tip
40 parent: 2:e6da46716401
35 parent: 2:e6da46716401
41 parent: 1:30d266f502e7
36 parent: 1:30d266f502e7
42 user: test
37 user: test
43 date: Thu Jan 01 00:00:03 1970 +0000
38 date: Thu Jan 01 00:00:03 1970 +0000
44 files: foo foo2
39 files: foo foo2
45 description:
40 description:
46 merged
41 merged
47
42
48
43
49 % nothing to merge
44 % nothing to merge
50 abort: there is nothing to merge - use "hg update" instead
45 abort: there is nothing to merge - use "hg update" instead
General Comments 0
You need to be logged in to leave comments. Login now