##// END OF EJS Templates
debugobsolete: add formatter support (issue5134)...
Yuya Nishihara -
r29795:142ae018 default
parent child Browse files
Show More
@@ -1,3562 +1,3563 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-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 __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import os
11 import os
12 import re
12 import re
13 import sys
13 import sys
14 import tempfile
14 import tempfile
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import (
17 from .node import (
18 bin,
18 bin,
19 hex,
19 hex,
20 nullid,
20 nullid,
21 nullrev,
21 nullrev,
22 short,
22 short,
23 )
23 )
24
24
25 from . import (
25 from . import (
26 bookmarks,
26 bookmarks,
27 changelog,
27 changelog,
28 copies,
28 copies,
29 crecord as crecordmod,
29 crecord as crecordmod,
30 encoding,
30 encoding,
31 error,
31 error,
32 formatter,
32 formatter,
33 graphmod,
33 graphmod,
34 lock as lockmod,
34 lock as lockmod,
35 match as matchmod,
35 match as matchmod,
36 obsolete,
36 obsolete,
37 patch,
37 patch,
38 pathutil,
38 pathutil,
39 phases,
39 phases,
40 repair,
40 repair,
41 revlog,
41 revlog,
42 revset,
42 revset,
43 scmutil,
43 scmutil,
44 templatekw,
44 templatekw,
45 templater,
45 templater,
46 util,
46 util,
47 )
47 )
48 stringio = util.stringio
48 stringio = util.stringio
49
49
50 def ishunk(x):
50 def ishunk(x):
51 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
51 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
52 return isinstance(x, hunkclasses)
52 return isinstance(x, hunkclasses)
53
53
54 def newandmodified(chunks, originalchunks):
54 def newandmodified(chunks, originalchunks):
55 newlyaddedandmodifiedfiles = set()
55 newlyaddedandmodifiedfiles = set()
56 for chunk in chunks:
56 for chunk in chunks:
57 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
57 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
58 originalchunks:
58 originalchunks:
59 newlyaddedandmodifiedfiles.add(chunk.header.filename())
59 newlyaddedandmodifiedfiles.add(chunk.header.filename())
60 return newlyaddedandmodifiedfiles
60 return newlyaddedandmodifiedfiles
61
61
62 def parsealiases(cmd):
62 def parsealiases(cmd):
63 return cmd.lstrip("^").split("|")
63 return cmd.lstrip("^").split("|")
64
64
65 def setupwrapcolorwrite(ui):
65 def setupwrapcolorwrite(ui):
66 # wrap ui.write so diff output can be labeled/colorized
66 # wrap ui.write so diff output can be labeled/colorized
67 def wrapwrite(orig, *args, **kw):
67 def wrapwrite(orig, *args, **kw):
68 label = kw.pop('label', '')
68 label = kw.pop('label', '')
69 for chunk, l in patch.difflabel(lambda: args):
69 for chunk, l in patch.difflabel(lambda: args):
70 orig(chunk, label=label + l)
70 orig(chunk, label=label + l)
71
71
72 oldwrite = ui.write
72 oldwrite = ui.write
73 def wrap(*args, **kwargs):
73 def wrap(*args, **kwargs):
74 return wrapwrite(oldwrite, *args, **kwargs)
74 return wrapwrite(oldwrite, *args, **kwargs)
75 setattr(ui, 'write', wrap)
75 setattr(ui, 'write', wrap)
76 return oldwrite
76 return oldwrite
77
77
78 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
78 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
79 if usecurses:
79 if usecurses:
80 if testfile:
80 if testfile:
81 recordfn = crecordmod.testdecorator(testfile,
81 recordfn = crecordmod.testdecorator(testfile,
82 crecordmod.testchunkselector)
82 crecordmod.testchunkselector)
83 else:
83 else:
84 recordfn = crecordmod.chunkselector
84 recordfn = crecordmod.chunkselector
85
85
86 return crecordmod.filterpatch(ui, originalhunks, recordfn)
86 return crecordmod.filterpatch(ui, originalhunks, recordfn)
87
87
88 else:
88 else:
89 return patch.filterpatch(ui, originalhunks, operation)
89 return patch.filterpatch(ui, originalhunks, operation)
90
90
91 def recordfilter(ui, originalhunks, operation=None):
91 def recordfilter(ui, originalhunks, operation=None):
92 """ Prompts the user to filter the originalhunks and return a list of
92 """ Prompts the user to filter the originalhunks and return a list of
93 selected hunks.
93 selected hunks.
94 *operation* is used for to build ui messages to indicate the user what
94 *operation* is used for to build ui messages to indicate the user what
95 kind of filtering they are doing: reverting, committing, shelving, etc.
95 kind of filtering they are doing: reverting, committing, shelving, etc.
96 (see patch.filterpatch).
96 (see patch.filterpatch).
97 """
97 """
98 usecurses = crecordmod.checkcurses(ui)
98 usecurses = crecordmod.checkcurses(ui)
99 testfile = ui.config('experimental', 'crecordtest', None)
99 testfile = ui.config('experimental', 'crecordtest', None)
100 oldwrite = setupwrapcolorwrite(ui)
100 oldwrite = setupwrapcolorwrite(ui)
101 try:
101 try:
102 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
102 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
103 testfile, operation)
103 testfile, operation)
104 finally:
104 finally:
105 ui.write = oldwrite
105 ui.write = oldwrite
106 return newchunks, newopts
106 return newchunks, newopts
107
107
108 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
108 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
109 filterfn, *pats, **opts):
109 filterfn, *pats, **opts):
110 from . import merge as mergemod
110 from . import merge as mergemod
111 if not ui.interactive():
111 if not ui.interactive():
112 if cmdsuggest:
112 if cmdsuggest:
113 msg = _('running non-interactively, use %s instead') % cmdsuggest
113 msg = _('running non-interactively, use %s instead') % cmdsuggest
114 else:
114 else:
115 msg = _('running non-interactively')
115 msg = _('running non-interactively')
116 raise error.Abort(msg)
116 raise error.Abort(msg)
117
117
118 # make sure username is set before going interactive
118 # make sure username is set before going interactive
119 if not opts.get('user'):
119 if not opts.get('user'):
120 ui.username() # raise exception, username not provided
120 ui.username() # raise exception, username not provided
121
121
122 def recordfunc(ui, repo, message, match, opts):
122 def recordfunc(ui, repo, message, match, opts):
123 """This is generic record driver.
123 """This is generic record driver.
124
124
125 Its job is to interactively filter local changes, and
125 Its job is to interactively filter local changes, and
126 accordingly prepare working directory into a state in which the
126 accordingly prepare working directory into a state in which the
127 job can be delegated to a non-interactive commit command such as
127 job can be delegated to a non-interactive commit command such as
128 'commit' or 'qrefresh'.
128 'commit' or 'qrefresh'.
129
129
130 After the actual job is done by non-interactive command, the
130 After the actual job is done by non-interactive command, the
131 working directory is restored to its original state.
131 working directory is restored to its original state.
132
132
133 In the end we'll record interesting changes, and everything else
133 In the end we'll record interesting changes, and everything else
134 will be left in place, so the user can continue working.
134 will be left in place, so the user can continue working.
135 """
135 """
136
136
137 checkunfinished(repo, commit=True)
137 checkunfinished(repo, commit=True)
138 wctx = repo[None]
138 wctx = repo[None]
139 merge = len(wctx.parents()) > 1
139 merge = len(wctx.parents()) > 1
140 if merge:
140 if merge:
141 raise error.Abort(_('cannot partially commit a merge '
141 raise error.Abort(_('cannot partially commit a merge '
142 '(use "hg commit" instead)'))
142 '(use "hg commit" instead)'))
143
143
144 def fail(f, msg):
144 def fail(f, msg):
145 raise error.Abort('%s: %s' % (f, msg))
145 raise error.Abort('%s: %s' % (f, msg))
146
146
147 force = opts.get('force')
147 force = opts.get('force')
148 if not force:
148 if not force:
149 vdirs = []
149 vdirs = []
150 match.explicitdir = vdirs.append
150 match.explicitdir = vdirs.append
151 match.bad = fail
151 match.bad = fail
152
152
153 status = repo.status(match=match)
153 status = repo.status(match=match)
154 if not force:
154 if not force:
155 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
155 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
156 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
156 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
157 diffopts.nodates = True
157 diffopts.nodates = True
158 diffopts.git = True
158 diffopts.git = True
159 diffopts.showfunc = True
159 diffopts.showfunc = True
160 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
160 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
161 originalchunks = patch.parsepatch(originaldiff)
161 originalchunks = patch.parsepatch(originaldiff)
162
162
163 # 1. filter patch, since we are intending to apply subset of it
163 # 1. filter patch, since we are intending to apply subset of it
164 try:
164 try:
165 chunks, newopts = filterfn(ui, originalchunks)
165 chunks, newopts = filterfn(ui, originalchunks)
166 except patch.PatchError as err:
166 except patch.PatchError as err:
167 raise error.Abort(_('error parsing patch: %s') % err)
167 raise error.Abort(_('error parsing patch: %s') % err)
168 opts.update(newopts)
168 opts.update(newopts)
169
169
170 # We need to keep a backup of files that have been newly added and
170 # We need to keep a backup of files that have been newly added and
171 # modified during the recording process because there is a previous
171 # modified during the recording process because there is a previous
172 # version without the edit in the workdir
172 # version without the edit in the workdir
173 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
173 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
174 contenders = set()
174 contenders = set()
175 for h in chunks:
175 for h in chunks:
176 try:
176 try:
177 contenders.update(set(h.files()))
177 contenders.update(set(h.files()))
178 except AttributeError:
178 except AttributeError:
179 pass
179 pass
180
180
181 changed = status.modified + status.added + status.removed
181 changed = status.modified + status.added + status.removed
182 newfiles = [f for f in changed if f in contenders]
182 newfiles = [f for f in changed if f in contenders]
183 if not newfiles:
183 if not newfiles:
184 ui.status(_('no changes to record\n'))
184 ui.status(_('no changes to record\n'))
185 return 0
185 return 0
186
186
187 modified = set(status.modified)
187 modified = set(status.modified)
188
188
189 # 2. backup changed files, so we can restore them in the end
189 # 2. backup changed files, so we can restore them in the end
190
190
191 if backupall:
191 if backupall:
192 tobackup = changed
192 tobackup = changed
193 else:
193 else:
194 tobackup = [f for f in newfiles if f in modified or f in \
194 tobackup = [f for f in newfiles if f in modified or f in \
195 newlyaddedandmodifiedfiles]
195 newlyaddedandmodifiedfiles]
196 backups = {}
196 backups = {}
197 if tobackup:
197 if tobackup:
198 backupdir = repo.join('record-backups')
198 backupdir = repo.join('record-backups')
199 try:
199 try:
200 os.mkdir(backupdir)
200 os.mkdir(backupdir)
201 except OSError as err:
201 except OSError as err:
202 if err.errno != errno.EEXIST:
202 if err.errno != errno.EEXIST:
203 raise
203 raise
204 try:
204 try:
205 # backup continues
205 # backup continues
206 for f in tobackup:
206 for f in tobackup:
207 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
207 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
208 dir=backupdir)
208 dir=backupdir)
209 os.close(fd)
209 os.close(fd)
210 ui.debug('backup %r as %r\n' % (f, tmpname))
210 ui.debug('backup %r as %r\n' % (f, tmpname))
211 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
211 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
212 backups[f] = tmpname
212 backups[f] = tmpname
213
213
214 fp = stringio()
214 fp = stringio()
215 for c in chunks:
215 for c in chunks:
216 fname = c.filename()
216 fname = c.filename()
217 if fname in backups:
217 if fname in backups:
218 c.write(fp)
218 c.write(fp)
219 dopatch = fp.tell()
219 dopatch = fp.tell()
220 fp.seek(0)
220 fp.seek(0)
221
221
222 # 2.5 optionally review / modify patch in text editor
222 # 2.5 optionally review / modify patch in text editor
223 if opts.get('review', False):
223 if opts.get('review', False):
224 patchtext = (crecordmod.diffhelptext
224 patchtext = (crecordmod.diffhelptext
225 + crecordmod.patchhelptext
225 + crecordmod.patchhelptext
226 + fp.read())
226 + fp.read())
227 reviewedpatch = ui.edit(patchtext, "",
227 reviewedpatch = ui.edit(patchtext, "",
228 extra={"suffix": ".diff"})
228 extra={"suffix": ".diff"})
229 fp.truncate(0)
229 fp.truncate(0)
230 fp.write(reviewedpatch)
230 fp.write(reviewedpatch)
231 fp.seek(0)
231 fp.seek(0)
232
232
233 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
233 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
234 # 3a. apply filtered patch to clean repo (clean)
234 # 3a. apply filtered patch to clean repo (clean)
235 if backups:
235 if backups:
236 # Equivalent to hg.revert
236 # Equivalent to hg.revert
237 m = scmutil.matchfiles(repo, backups.keys())
237 m = scmutil.matchfiles(repo, backups.keys())
238 mergemod.update(repo, repo.dirstate.p1(),
238 mergemod.update(repo, repo.dirstate.p1(),
239 False, True, matcher=m)
239 False, True, matcher=m)
240
240
241 # 3b. (apply)
241 # 3b. (apply)
242 if dopatch:
242 if dopatch:
243 try:
243 try:
244 ui.debug('applying patch\n')
244 ui.debug('applying patch\n')
245 ui.debug(fp.getvalue())
245 ui.debug(fp.getvalue())
246 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
246 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
247 except patch.PatchError as err:
247 except patch.PatchError as err:
248 raise error.Abort(str(err))
248 raise error.Abort(str(err))
249 del fp
249 del fp
250
250
251 # 4. We prepared working directory according to filtered
251 # 4. We prepared working directory according to filtered
252 # patch. Now is the time to delegate the job to
252 # patch. Now is the time to delegate the job to
253 # commit/qrefresh or the like!
253 # commit/qrefresh or the like!
254
254
255 # Make all of the pathnames absolute.
255 # Make all of the pathnames absolute.
256 newfiles = [repo.wjoin(nf) for nf in newfiles]
256 newfiles = [repo.wjoin(nf) for nf in newfiles]
257 return commitfunc(ui, repo, *newfiles, **opts)
257 return commitfunc(ui, repo, *newfiles, **opts)
258 finally:
258 finally:
259 # 5. finally restore backed-up files
259 # 5. finally restore backed-up files
260 try:
260 try:
261 dirstate = repo.dirstate
261 dirstate = repo.dirstate
262 for realname, tmpname in backups.iteritems():
262 for realname, tmpname in backups.iteritems():
263 ui.debug('restoring %r to %r\n' % (tmpname, realname))
263 ui.debug('restoring %r to %r\n' % (tmpname, realname))
264
264
265 if dirstate[realname] == 'n':
265 if dirstate[realname] == 'n':
266 # without normallookup, restoring timestamp
266 # without normallookup, restoring timestamp
267 # may cause partially committed files
267 # may cause partially committed files
268 # to be treated as unmodified
268 # to be treated as unmodified
269 dirstate.normallookup(realname)
269 dirstate.normallookup(realname)
270
270
271 # copystat=True here and above are a hack to trick any
271 # copystat=True here and above are a hack to trick any
272 # editors that have f open that we haven't modified them.
272 # editors that have f open that we haven't modified them.
273 #
273 #
274 # Also note that this racy as an editor could notice the
274 # Also note that this racy as an editor could notice the
275 # file's mtime before we've finished writing it.
275 # file's mtime before we've finished writing it.
276 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
276 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
277 os.unlink(tmpname)
277 os.unlink(tmpname)
278 if tobackup:
278 if tobackup:
279 os.rmdir(backupdir)
279 os.rmdir(backupdir)
280 except OSError:
280 except OSError:
281 pass
281 pass
282
282
283 def recordinwlock(ui, repo, message, match, opts):
283 def recordinwlock(ui, repo, message, match, opts):
284 with repo.wlock():
284 with repo.wlock():
285 return recordfunc(ui, repo, message, match, opts)
285 return recordfunc(ui, repo, message, match, opts)
286
286
287 return commit(ui, repo, recordinwlock, pats, opts)
287 return commit(ui, repo, recordinwlock, pats, opts)
288
288
289 def findpossible(cmd, table, strict=False):
289 def findpossible(cmd, table, strict=False):
290 """
290 """
291 Return cmd -> (aliases, command table entry)
291 Return cmd -> (aliases, command table entry)
292 for each matching command.
292 for each matching command.
293 Return debug commands (or their aliases) only if no normal command matches.
293 Return debug commands (or their aliases) only if no normal command matches.
294 """
294 """
295 choice = {}
295 choice = {}
296 debugchoice = {}
296 debugchoice = {}
297
297
298 if cmd in table:
298 if cmd in table:
299 # short-circuit exact matches, "log" alias beats "^log|history"
299 # short-circuit exact matches, "log" alias beats "^log|history"
300 keys = [cmd]
300 keys = [cmd]
301 else:
301 else:
302 keys = table.keys()
302 keys = table.keys()
303
303
304 allcmds = []
304 allcmds = []
305 for e in keys:
305 for e in keys:
306 aliases = parsealiases(e)
306 aliases = parsealiases(e)
307 allcmds.extend(aliases)
307 allcmds.extend(aliases)
308 found = None
308 found = None
309 if cmd in aliases:
309 if cmd in aliases:
310 found = cmd
310 found = cmd
311 elif not strict:
311 elif not strict:
312 for a in aliases:
312 for a in aliases:
313 if a.startswith(cmd):
313 if a.startswith(cmd):
314 found = a
314 found = a
315 break
315 break
316 if found is not None:
316 if found is not None:
317 if aliases[0].startswith("debug") or found.startswith("debug"):
317 if aliases[0].startswith("debug") or found.startswith("debug"):
318 debugchoice[found] = (aliases, table[e])
318 debugchoice[found] = (aliases, table[e])
319 else:
319 else:
320 choice[found] = (aliases, table[e])
320 choice[found] = (aliases, table[e])
321
321
322 if not choice and debugchoice:
322 if not choice and debugchoice:
323 choice = debugchoice
323 choice = debugchoice
324
324
325 return choice, allcmds
325 return choice, allcmds
326
326
327 def findcmd(cmd, table, strict=True):
327 def findcmd(cmd, table, strict=True):
328 """Return (aliases, command table entry) for command string."""
328 """Return (aliases, command table entry) for command string."""
329 choice, allcmds = findpossible(cmd, table, strict)
329 choice, allcmds = findpossible(cmd, table, strict)
330
330
331 if cmd in choice:
331 if cmd in choice:
332 return choice[cmd]
332 return choice[cmd]
333
333
334 if len(choice) > 1:
334 if len(choice) > 1:
335 clist = choice.keys()
335 clist = choice.keys()
336 clist.sort()
336 clist.sort()
337 raise error.AmbiguousCommand(cmd, clist)
337 raise error.AmbiguousCommand(cmd, clist)
338
338
339 if choice:
339 if choice:
340 return choice.values()[0]
340 return choice.values()[0]
341
341
342 raise error.UnknownCommand(cmd, allcmds)
342 raise error.UnknownCommand(cmd, allcmds)
343
343
344 def findrepo(p):
344 def findrepo(p):
345 while not os.path.isdir(os.path.join(p, ".hg")):
345 while not os.path.isdir(os.path.join(p, ".hg")):
346 oldp, p = p, os.path.dirname(p)
346 oldp, p = p, os.path.dirname(p)
347 if p == oldp:
347 if p == oldp:
348 return None
348 return None
349
349
350 return p
350 return p
351
351
352 def bailifchanged(repo, merge=True):
352 def bailifchanged(repo, merge=True):
353 if merge and repo.dirstate.p2() != nullid:
353 if merge and repo.dirstate.p2() != nullid:
354 raise error.Abort(_('outstanding uncommitted merge'))
354 raise error.Abort(_('outstanding uncommitted merge'))
355 modified, added, removed, deleted = repo.status()[:4]
355 modified, added, removed, deleted = repo.status()[:4]
356 if modified or added or removed or deleted:
356 if modified or added or removed or deleted:
357 raise error.Abort(_('uncommitted changes'))
357 raise error.Abort(_('uncommitted changes'))
358 ctx = repo[None]
358 ctx = repo[None]
359 for s in sorted(ctx.substate):
359 for s in sorted(ctx.substate):
360 ctx.sub(s).bailifchanged()
360 ctx.sub(s).bailifchanged()
361
361
362 def logmessage(ui, opts):
362 def logmessage(ui, opts):
363 """ get the log message according to -m and -l option """
363 """ get the log message according to -m and -l option """
364 message = opts.get('message')
364 message = opts.get('message')
365 logfile = opts.get('logfile')
365 logfile = opts.get('logfile')
366
366
367 if message and logfile:
367 if message and logfile:
368 raise error.Abort(_('options --message and --logfile are mutually '
368 raise error.Abort(_('options --message and --logfile are mutually '
369 'exclusive'))
369 'exclusive'))
370 if not message and logfile:
370 if not message and logfile:
371 try:
371 try:
372 if logfile == '-':
372 if logfile == '-':
373 message = ui.fin.read()
373 message = ui.fin.read()
374 else:
374 else:
375 message = '\n'.join(util.readfile(logfile).splitlines())
375 message = '\n'.join(util.readfile(logfile).splitlines())
376 except IOError as inst:
376 except IOError as inst:
377 raise error.Abort(_("can't read commit message '%s': %s") %
377 raise error.Abort(_("can't read commit message '%s': %s") %
378 (logfile, inst.strerror))
378 (logfile, inst.strerror))
379 return message
379 return message
380
380
381 def mergeeditform(ctxorbool, baseformname):
381 def mergeeditform(ctxorbool, baseformname):
382 """return appropriate editform name (referencing a committemplate)
382 """return appropriate editform name (referencing a committemplate)
383
383
384 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
384 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
385 merging is committed.
385 merging is committed.
386
386
387 This returns baseformname with '.merge' appended if it is a merge,
387 This returns baseformname with '.merge' appended if it is a merge,
388 otherwise '.normal' is appended.
388 otherwise '.normal' is appended.
389 """
389 """
390 if isinstance(ctxorbool, bool):
390 if isinstance(ctxorbool, bool):
391 if ctxorbool:
391 if ctxorbool:
392 return baseformname + ".merge"
392 return baseformname + ".merge"
393 elif 1 < len(ctxorbool.parents()):
393 elif 1 < len(ctxorbool.parents()):
394 return baseformname + ".merge"
394 return baseformname + ".merge"
395
395
396 return baseformname + ".normal"
396 return baseformname + ".normal"
397
397
398 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
398 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
399 editform='', **opts):
399 editform='', **opts):
400 """get appropriate commit message editor according to '--edit' option
400 """get appropriate commit message editor according to '--edit' option
401
401
402 'finishdesc' is a function to be called with edited commit message
402 'finishdesc' is a function to be called with edited commit message
403 (= 'description' of the new changeset) just after editing, but
403 (= 'description' of the new changeset) just after editing, but
404 before checking empty-ness. It should return actual text to be
404 before checking empty-ness. It should return actual text to be
405 stored into history. This allows to change description before
405 stored into history. This allows to change description before
406 storing.
406 storing.
407
407
408 'extramsg' is a extra message to be shown in the editor instead of
408 'extramsg' is a extra message to be shown in the editor instead of
409 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
409 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
410 is automatically added.
410 is automatically added.
411
411
412 'editform' is a dot-separated list of names, to distinguish
412 'editform' is a dot-separated list of names, to distinguish
413 the purpose of commit text editing.
413 the purpose of commit text editing.
414
414
415 'getcommiteditor' returns 'commitforceeditor' regardless of
415 'getcommiteditor' returns 'commitforceeditor' regardless of
416 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
416 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
417 they are specific for usage in MQ.
417 they are specific for usage in MQ.
418 """
418 """
419 if edit or finishdesc or extramsg:
419 if edit or finishdesc or extramsg:
420 return lambda r, c, s: commitforceeditor(r, c, s,
420 return lambda r, c, s: commitforceeditor(r, c, s,
421 finishdesc=finishdesc,
421 finishdesc=finishdesc,
422 extramsg=extramsg,
422 extramsg=extramsg,
423 editform=editform)
423 editform=editform)
424 elif editform:
424 elif editform:
425 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
425 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
426 else:
426 else:
427 return commiteditor
427 return commiteditor
428
428
429 def loglimit(opts):
429 def loglimit(opts):
430 """get the log limit according to option -l/--limit"""
430 """get the log limit according to option -l/--limit"""
431 limit = opts.get('limit')
431 limit = opts.get('limit')
432 if limit:
432 if limit:
433 try:
433 try:
434 limit = int(limit)
434 limit = int(limit)
435 except ValueError:
435 except ValueError:
436 raise error.Abort(_('limit must be a positive integer'))
436 raise error.Abort(_('limit must be a positive integer'))
437 if limit <= 0:
437 if limit <= 0:
438 raise error.Abort(_('limit must be positive'))
438 raise error.Abort(_('limit must be positive'))
439 else:
439 else:
440 limit = None
440 limit = None
441 return limit
441 return limit
442
442
443 def makefilename(repo, pat, node, desc=None,
443 def makefilename(repo, pat, node, desc=None,
444 total=None, seqno=None, revwidth=None, pathname=None):
444 total=None, seqno=None, revwidth=None, pathname=None):
445 node_expander = {
445 node_expander = {
446 'H': lambda: hex(node),
446 'H': lambda: hex(node),
447 'R': lambda: str(repo.changelog.rev(node)),
447 'R': lambda: str(repo.changelog.rev(node)),
448 'h': lambda: short(node),
448 'h': lambda: short(node),
449 'm': lambda: re.sub('[^\w]', '_', str(desc))
449 'm': lambda: re.sub('[^\w]', '_', str(desc))
450 }
450 }
451 expander = {
451 expander = {
452 '%': lambda: '%',
452 '%': lambda: '%',
453 'b': lambda: os.path.basename(repo.root),
453 'b': lambda: os.path.basename(repo.root),
454 }
454 }
455
455
456 try:
456 try:
457 if node:
457 if node:
458 expander.update(node_expander)
458 expander.update(node_expander)
459 if node:
459 if node:
460 expander['r'] = (lambda:
460 expander['r'] = (lambda:
461 str(repo.changelog.rev(node)).zfill(revwidth or 0))
461 str(repo.changelog.rev(node)).zfill(revwidth or 0))
462 if total is not None:
462 if total is not None:
463 expander['N'] = lambda: str(total)
463 expander['N'] = lambda: str(total)
464 if seqno is not None:
464 if seqno is not None:
465 expander['n'] = lambda: str(seqno)
465 expander['n'] = lambda: str(seqno)
466 if total is not None and seqno is not None:
466 if total is not None and seqno is not None:
467 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
467 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
468 if pathname is not None:
468 if pathname is not None:
469 expander['s'] = lambda: os.path.basename(pathname)
469 expander['s'] = lambda: os.path.basename(pathname)
470 expander['d'] = lambda: os.path.dirname(pathname) or '.'
470 expander['d'] = lambda: os.path.dirname(pathname) or '.'
471 expander['p'] = lambda: pathname
471 expander['p'] = lambda: pathname
472
472
473 newname = []
473 newname = []
474 patlen = len(pat)
474 patlen = len(pat)
475 i = 0
475 i = 0
476 while i < patlen:
476 while i < patlen:
477 c = pat[i]
477 c = pat[i]
478 if c == '%':
478 if c == '%':
479 i += 1
479 i += 1
480 c = pat[i]
480 c = pat[i]
481 c = expander[c]()
481 c = expander[c]()
482 newname.append(c)
482 newname.append(c)
483 i += 1
483 i += 1
484 return ''.join(newname)
484 return ''.join(newname)
485 except KeyError as inst:
485 except KeyError as inst:
486 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
486 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
487 inst.args[0])
487 inst.args[0])
488
488
489 class _unclosablefile(object):
489 class _unclosablefile(object):
490 def __init__(self, fp):
490 def __init__(self, fp):
491 self._fp = fp
491 self._fp = fp
492
492
493 def close(self):
493 def close(self):
494 pass
494 pass
495
495
496 def __iter__(self):
496 def __iter__(self):
497 return iter(self._fp)
497 return iter(self._fp)
498
498
499 def __getattr__(self, attr):
499 def __getattr__(self, attr):
500 return getattr(self._fp, attr)
500 return getattr(self._fp, attr)
501
501
502 def makefileobj(repo, pat, node=None, desc=None, total=None,
502 def makefileobj(repo, pat, node=None, desc=None, total=None,
503 seqno=None, revwidth=None, mode='wb', modemap=None,
503 seqno=None, revwidth=None, mode='wb', modemap=None,
504 pathname=None):
504 pathname=None):
505
505
506 writable = mode not in ('r', 'rb')
506 writable = mode not in ('r', 'rb')
507
507
508 if not pat or pat == '-':
508 if not pat or pat == '-':
509 if writable:
509 if writable:
510 fp = repo.ui.fout
510 fp = repo.ui.fout
511 else:
511 else:
512 fp = repo.ui.fin
512 fp = repo.ui.fin
513 return _unclosablefile(fp)
513 return _unclosablefile(fp)
514 if util.safehasattr(pat, 'write') and writable:
514 if util.safehasattr(pat, 'write') and writable:
515 return pat
515 return pat
516 if util.safehasattr(pat, 'read') and 'r' in mode:
516 if util.safehasattr(pat, 'read') and 'r' in mode:
517 return pat
517 return pat
518 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
518 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
519 if modemap is not None:
519 if modemap is not None:
520 mode = modemap.get(fn, mode)
520 mode = modemap.get(fn, mode)
521 if mode == 'wb':
521 if mode == 'wb':
522 modemap[fn] = 'ab'
522 modemap[fn] = 'ab'
523 return open(fn, mode)
523 return open(fn, mode)
524
524
525 def openrevlog(repo, cmd, file_, opts):
525 def openrevlog(repo, cmd, file_, opts):
526 """opens the changelog, manifest, a filelog or a given revlog"""
526 """opens the changelog, manifest, a filelog or a given revlog"""
527 cl = opts['changelog']
527 cl = opts['changelog']
528 mf = opts['manifest']
528 mf = opts['manifest']
529 dir = opts['dir']
529 dir = opts['dir']
530 msg = None
530 msg = None
531 if cl and mf:
531 if cl and mf:
532 msg = _('cannot specify --changelog and --manifest at the same time')
532 msg = _('cannot specify --changelog and --manifest at the same time')
533 elif cl and dir:
533 elif cl and dir:
534 msg = _('cannot specify --changelog and --dir at the same time')
534 msg = _('cannot specify --changelog and --dir at the same time')
535 elif cl or mf or dir:
535 elif cl or mf or dir:
536 if file_:
536 if file_:
537 msg = _('cannot specify filename with --changelog or --manifest')
537 msg = _('cannot specify filename with --changelog or --manifest')
538 elif not repo:
538 elif not repo:
539 msg = _('cannot specify --changelog or --manifest or --dir '
539 msg = _('cannot specify --changelog or --manifest or --dir '
540 'without a repository')
540 'without a repository')
541 if msg:
541 if msg:
542 raise error.Abort(msg)
542 raise error.Abort(msg)
543
543
544 r = None
544 r = None
545 if repo:
545 if repo:
546 if cl:
546 if cl:
547 r = repo.unfiltered().changelog
547 r = repo.unfiltered().changelog
548 elif dir:
548 elif dir:
549 if 'treemanifest' not in repo.requirements:
549 if 'treemanifest' not in repo.requirements:
550 raise error.Abort(_("--dir can only be used on repos with "
550 raise error.Abort(_("--dir can only be used on repos with "
551 "treemanifest enabled"))
551 "treemanifest enabled"))
552 dirlog = repo.manifest.dirlog(dir)
552 dirlog = repo.manifest.dirlog(dir)
553 if len(dirlog):
553 if len(dirlog):
554 r = dirlog
554 r = dirlog
555 elif mf:
555 elif mf:
556 r = repo.manifest
556 r = repo.manifest
557 elif file_:
557 elif file_:
558 filelog = repo.file(file_)
558 filelog = repo.file(file_)
559 if len(filelog):
559 if len(filelog):
560 r = filelog
560 r = filelog
561 if not r:
561 if not r:
562 if not file_:
562 if not file_:
563 raise error.CommandError(cmd, _('invalid arguments'))
563 raise error.CommandError(cmd, _('invalid arguments'))
564 if not os.path.isfile(file_):
564 if not os.path.isfile(file_):
565 raise error.Abort(_("revlog '%s' not found") % file_)
565 raise error.Abort(_("revlog '%s' not found") % file_)
566 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
566 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
567 file_[:-2] + ".i")
567 file_[:-2] + ".i")
568 return r
568 return r
569
569
570 def copy(ui, repo, pats, opts, rename=False):
570 def copy(ui, repo, pats, opts, rename=False):
571 # called with the repo lock held
571 # called with the repo lock held
572 #
572 #
573 # hgsep => pathname that uses "/" to separate directories
573 # hgsep => pathname that uses "/" to separate directories
574 # ossep => pathname that uses os.sep to separate directories
574 # ossep => pathname that uses os.sep to separate directories
575 cwd = repo.getcwd()
575 cwd = repo.getcwd()
576 targets = {}
576 targets = {}
577 after = opts.get("after")
577 after = opts.get("after")
578 dryrun = opts.get("dry_run")
578 dryrun = opts.get("dry_run")
579 wctx = repo[None]
579 wctx = repo[None]
580
580
581 def walkpat(pat):
581 def walkpat(pat):
582 srcs = []
582 srcs = []
583 if after:
583 if after:
584 badstates = '?'
584 badstates = '?'
585 else:
585 else:
586 badstates = '?r'
586 badstates = '?r'
587 m = scmutil.match(repo[None], [pat], opts, globbed=True)
587 m = scmutil.match(repo[None], [pat], opts, globbed=True)
588 for abs in repo.walk(m):
588 for abs in repo.walk(m):
589 state = repo.dirstate[abs]
589 state = repo.dirstate[abs]
590 rel = m.rel(abs)
590 rel = m.rel(abs)
591 exact = m.exact(abs)
591 exact = m.exact(abs)
592 if state in badstates:
592 if state in badstates:
593 if exact and state == '?':
593 if exact and state == '?':
594 ui.warn(_('%s: not copying - file is not managed\n') % rel)
594 ui.warn(_('%s: not copying - file is not managed\n') % rel)
595 if exact and state == 'r':
595 if exact and state == 'r':
596 ui.warn(_('%s: not copying - file has been marked for'
596 ui.warn(_('%s: not copying - file has been marked for'
597 ' remove\n') % rel)
597 ' remove\n') % rel)
598 continue
598 continue
599 # abs: hgsep
599 # abs: hgsep
600 # rel: ossep
600 # rel: ossep
601 srcs.append((abs, rel, exact))
601 srcs.append((abs, rel, exact))
602 return srcs
602 return srcs
603
603
604 # abssrc: hgsep
604 # abssrc: hgsep
605 # relsrc: ossep
605 # relsrc: ossep
606 # otarget: ossep
606 # otarget: ossep
607 def copyfile(abssrc, relsrc, otarget, exact):
607 def copyfile(abssrc, relsrc, otarget, exact):
608 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
608 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
609 if '/' in abstarget:
609 if '/' in abstarget:
610 # We cannot normalize abstarget itself, this would prevent
610 # We cannot normalize abstarget itself, this would prevent
611 # case only renames, like a => A.
611 # case only renames, like a => A.
612 abspath, absname = abstarget.rsplit('/', 1)
612 abspath, absname = abstarget.rsplit('/', 1)
613 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
613 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
614 reltarget = repo.pathto(abstarget, cwd)
614 reltarget = repo.pathto(abstarget, cwd)
615 target = repo.wjoin(abstarget)
615 target = repo.wjoin(abstarget)
616 src = repo.wjoin(abssrc)
616 src = repo.wjoin(abssrc)
617 state = repo.dirstate[abstarget]
617 state = repo.dirstate[abstarget]
618
618
619 scmutil.checkportable(ui, abstarget)
619 scmutil.checkportable(ui, abstarget)
620
620
621 # check for collisions
621 # check for collisions
622 prevsrc = targets.get(abstarget)
622 prevsrc = targets.get(abstarget)
623 if prevsrc is not None:
623 if prevsrc is not None:
624 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
624 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
625 (reltarget, repo.pathto(abssrc, cwd),
625 (reltarget, repo.pathto(abssrc, cwd),
626 repo.pathto(prevsrc, cwd)))
626 repo.pathto(prevsrc, cwd)))
627 return
627 return
628
628
629 # check for overwrites
629 # check for overwrites
630 exists = os.path.lexists(target)
630 exists = os.path.lexists(target)
631 samefile = False
631 samefile = False
632 if exists and abssrc != abstarget:
632 if exists and abssrc != abstarget:
633 if (repo.dirstate.normalize(abssrc) ==
633 if (repo.dirstate.normalize(abssrc) ==
634 repo.dirstate.normalize(abstarget)):
634 repo.dirstate.normalize(abstarget)):
635 if not rename:
635 if not rename:
636 ui.warn(_("%s: can't copy - same file\n") % reltarget)
636 ui.warn(_("%s: can't copy - same file\n") % reltarget)
637 return
637 return
638 exists = False
638 exists = False
639 samefile = True
639 samefile = True
640
640
641 if not after and exists or after and state in 'mn':
641 if not after and exists or after and state in 'mn':
642 if not opts['force']:
642 if not opts['force']:
643 ui.warn(_('%s: not overwriting - file exists\n') %
643 ui.warn(_('%s: not overwriting - file exists\n') %
644 reltarget)
644 reltarget)
645 return
645 return
646
646
647 if after:
647 if after:
648 if not exists:
648 if not exists:
649 if rename:
649 if rename:
650 ui.warn(_('%s: not recording move - %s does not exist\n') %
650 ui.warn(_('%s: not recording move - %s does not exist\n') %
651 (relsrc, reltarget))
651 (relsrc, reltarget))
652 else:
652 else:
653 ui.warn(_('%s: not recording copy - %s does not exist\n') %
653 ui.warn(_('%s: not recording copy - %s does not exist\n') %
654 (relsrc, reltarget))
654 (relsrc, reltarget))
655 return
655 return
656 elif not dryrun:
656 elif not dryrun:
657 try:
657 try:
658 if exists:
658 if exists:
659 os.unlink(target)
659 os.unlink(target)
660 targetdir = os.path.dirname(target) or '.'
660 targetdir = os.path.dirname(target) or '.'
661 if not os.path.isdir(targetdir):
661 if not os.path.isdir(targetdir):
662 os.makedirs(targetdir)
662 os.makedirs(targetdir)
663 if samefile:
663 if samefile:
664 tmp = target + "~hgrename"
664 tmp = target + "~hgrename"
665 os.rename(src, tmp)
665 os.rename(src, tmp)
666 os.rename(tmp, target)
666 os.rename(tmp, target)
667 else:
667 else:
668 util.copyfile(src, target)
668 util.copyfile(src, target)
669 srcexists = True
669 srcexists = True
670 except IOError as inst:
670 except IOError as inst:
671 if inst.errno == errno.ENOENT:
671 if inst.errno == errno.ENOENT:
672 ui.warn(_('%s: deleted in working directory\n') % relsrc)
672 ui.warn(_('%s: deleted in working directory\n') % relsrc)
673 srcexists = False
673 srcexists = False
674 else:
674 else:
675 ui.warn(_('%s: cannot copy - %s\n') %
675 ui.warn(_('%s: cannot copy - %s\n') %
676 (relsrc, inst.strerror))
676 (relsrc, inst.strerror))
677 return True # report a failure
677 return True # report a failure
678
678
679 if ui.verbose or not exact:
679 if ui.verbose or not exact:
680 if rename:
680 if rename:
681 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
681 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
682 else:
682 else:
683 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
683 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
684
684
685 targets[abstarget] = abssrc
685 targets[abstarget] = abssrc
686
686
687 # fix up dirstate
687 # fix up dirstate
688 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
688 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
689 dryrun=dryrun, cwd=cwd)
689 dryrun=dryrun, cwd=cwd)
690 if rename and not dryrun:
690 if rename and not dryrun:
691 if not after and srcexists and not samefile:
691 if not after and srcexists and not samefile:
692 util.unlinkpath(repo.wjoin(abssrc))
692 util.unlinkpath(repo.wjoin(abssrc))
693 wctx.forget([abssrc])
693 wctx.forget([abssrc])
694
694
695 # pat: ossep
695 # pat: ossep
696 # dest ossep
696 # dest ossep
697 # srcs: list of (hgsep, hgsep, ossep, bool)
697 # srcs: list of (hgsep, hgsep, ossep, bool)
698 # return: function that takes hgsep and returns ossep
698 # return: function that takes hgsep and returns ossep
699 def targetpathfn(pat, dest, srcs):
699 def targetpathfn(pat, dest, srcs):
700 if os.path.isdir(pat):
700 if os.path.isdir(pat):
701 abspfx = pathutil.canonpath(repo.root, cwd, pat)
701 abspfx = pathutil.canonpath(repo.root, cwd, pat)
702 abspfx = util.localpath(abspfx)
702 abspfx = util.localpath(abspfx)
703 if destdirexists:
703 if destdirexists:
704 striplen = len(os.path.split(abspfx)[0])
704 striplen = len(os.path.split(abspfx)[0])
705 else:
705 else:
706 striplen = len(abspfx)
706 striplen = len(abspfx)
707 if striplen:
707 if striplen:
708 striplen += len(os.sep)
708 striplen += len(os.sep)
709 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
709 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
710 elif destdirexists:
710 elif destdirexists:
711 res = lambda p: os.path.join(dest,
711 res = lambda p: os.path.join(dest,
712 os.path.basename(util.localpath(p)))
712 os.path.basename(util.localpath(p)))
713 else:
713 else:
714 res = lambda p: dest
714 res = lambda p: dest
715 return res
715 return res
716
716
717 # pat: ossep
717 # pat: ossep
718 # dest ossep
718 # dest ossep
719 # srcs: list of (hgsep, hgsep, ossep, bool)
719 # srcs: list of (hgsep, hgsep, ossep, bool)
720 # return: function that takes hgsep and returns ossep
720 # return: function that takes hgsep and returns ossep
721 def targetpathafterfn(pat, dest, srcs):
721 def targetpathafterfn(pat, dest, srcs):
722 if matchmod.patkind(pat):
722 if matchmod.patkind(pat):
723 # a mercurial pattern
723 # a mercurial pattern
724 res = lambda p: os.path.join(dest,
724 res = lambda p: os.path.join(dest,
725 os.path.basename(util.localpath(p)))
725 os.path.basename(util.localpath(p)))
726 else:
726 else:
727 abspfx = pathutil.canonpath(repo.root, cwd, pat)
727 abspfx = pathutil.canonpath(repo.root, cwd, pat)
728 if len(abspfx) < len(srcs[0][0]):
728 if len(abspfx) < len(srcs[0][0]):
729 # A directory. Either the target path contains the last
729 # A directory. Either the target path contains the last
730 # component of the source path or it does not.
730 # component of the source path or it does not.
731 def evalpath(striplen):
731 def evalpath(striplen):
732 score = 0
732 score = 0
733 for s in srcs:
733 for s in srcs:
734 t = os.path.join(dest, util.localpath(s[0])[striplen:])
734 t = os.path.join(dest, util.localpath(s[0])[striplen:])
735 if os.path.lexists(t):
735 if os.path.lexists(t):
736 score += 1
736 score += 1
737 return score
737 return score
738
738
739 abspfx = util.localpath(abspfx)
739 abspfx = util.localpath(abspfx)
740 striplen = len(abspfx)
740 striplen = len(abspfx)
741 if striplen:
741 if striplen:
742 striplen += len(os.sep)
742 striplen += len(os.sep)
743 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
743 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
744 score = evalpath(striplen)
744 score = evalpath(striplen)
745 striplen1 = len(os.path.split(abspfx)[0])
745 striplen1 = len(os.path.split(abspfx)[0])
746 if striplen1:
746 if striplen1:
747 striplen1 += len(os.sep)
747 striplen1 += len(os.sep)
748 if evalpath(striplen1) > score:
748 if evalpath(striplen1) > score:
749 striplen = striplen1
749 striplen = striplen1
750 res = lambda p: os.path.join(dest,
750 res = lambda p: os.path.join(dest,
751 util.localpath(p)[striplen:])
751 util.localpath(p)[striplen:])
752 else:
752 else:
753 # a file
753 # a file
754 if destdirexists:
754 if destdirexists:
755 res = lambda p: os.path.join(dest,
755 res = lambda p: os.path.join(dest,
756 os.path.basename(util.localpath(p)))
756 os.path.basename(util.localpath(p)))
757 else:
757 else:
758 res = lambda p: dest
758 res = lambda p: dest
759 return res
759 return res
760
760
761 pats = scmutil.expandpats(pats)
761 pats = scmutil.expandpats(pats)
762 if not pats:
762 if not pats:
763 raise error.Abort(_('no source or destination specified'))
763 raise error.Abort(_('no source or destination specified'))
764 if len(pats) == 1:
764 if len(pats) == 1:
765 raise error.Abort(_('no destination specified'))
765 raise error.Abort(_('no destination specified'))
766 dest = pats.pop()
766 dest = pats.pop()
767 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
767 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
768 if not destdirexists:
768 if not destdirexists:
769 if len(pats) > 1 or matchmod.patkind(pats[0]):
769 if len(pats) > 1 or matchmod.patkind(pats[0]):
770 raise error.Abort(_('with multiple sources, destination must be an '
770 raise error.Abort(_('with multiple sources, destination must be an '
771 'existing directory'))
771 'existing directory'))
772 if util.endswithsep(dest):
772 if util.endswithsep(dest):
773 raise error.Abort(_('destination %s is not a directory') % dest)
773 raise error.Abort(_('destination %s is not a directory') % dest)
774
774
775 tfn = targetpathfn
775 tfn = targetpathfn
776 if after:
776 if after:
777 tfn = targetpathafterfn
777 tfn = targetpathafterfn
778 copylist = []
778 copylist = []
779 for pat in pats:
779 for pat in pats:
780 srcs = walkpat(pat)
780 srcs = walkpat(pat)
781 if not srcs:
781 if not srcs:
782 continue
782 continue
783 copylist.append((tfn(pat, dest, srcs), srcs))
783 copylist.append((tfn(pat, dest, srcs), srcs))
784 if not copylist:
784 if not copylist:
785 raise error.Abort(_('no files to copy'))
785 raise error.Abort(_('no files to copy'))
786
786
787 errors = 0
787 errors = 0
788 for targetpath, srcs in copylist:
788 for targetpath, srcs in copylist:
789 for abssrc, relsrc, exact in srcs:
789 for abssrc, relsrc, exact in srcs:
790 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
790 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
791 errors += 1
791 errors += 1
792
792
793 if errors:
793 if errors:
794 ui.warn(_('(consider using --after)\n'))
794 ui.warn(_('(consider using --after)\n'))
795
795
796 return errors != 0
796 return errors != 0
797
797
798 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
798 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
799 runargs=None, appendpid=False):
799 runargs=None, appendpid=False):
800 '''Run a command as a service.'''
800 '''Run a command as a service.'''
801
801
802 def writepid(pid):
802 def writepid(pid):
803 if opts['pid_file']:
803 if opts['pid_file']:
804 if appendpid:
804 if appendpid:
805 mode = 'a'
805 mode = 'a'
806 else:
806 else:
807 mode = 'w'
807 mode = 'w'
808 fp = open(opts['pid_file'], mode)
808 fp = open(opts['pid_file'], mode)
809 fp.write(str(pid) + '\n')
809 fp.write(str(pid) + '\n')
810 fp.close()
810 fp.close()
811
811
812 if opts['daemon'] and not opts['daemon_postexec']:
812 if opts['daemon'] and not opts['daemon_postexec']:
813 # Signal child process startup with file removal
813 # Signal child process startup with file removal
814 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
814 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
815 os.close(lockfd)
815 os.close(lockfd)
816 try:
816 try:
817 if not runargs:
817 if not runargs:
818 runargs = util.hgcmd() + sys.argv[1:]
818 runargs = util.hgcmd() + sys.argv[1:]
819 runargs.append('--daemon-postexec=unlink:%s' % lockpath)
819 runargs.append('--daemon-postexec=unlink:%s' % lockpath)
820 # Don't pass --cwd to the child process, because we've already
820 # Don't pass --cwd to the child process, because we've already
821 # changed directory.
821 # changed directory.
822 for i in xrange(1, len(runargs)):
822 for i in xrange(1, len(runargs)):
823 if runargs[i].startswith('--cwd='):
823 if runargs[i].startswith('--cwd='):
824 del runargs[i]
824 del runargs[i]
825 break
825 break
826 elif runargs[i].startswith('--cwd'):
826 elif runargs[i].startswith('--cwd'):
827 del runargs[i:i + 2]
827 del runargs[i:i + 2]
828 break
828 break
829 def condfn():
829 def condfn():
830 return not os.path.exists(lockpath)
830 return not os.path.exists(lockpath)
831 pid = util.rundetached(runargs, condfn)
831 pid = util.rundetached(runargs, condfn)
832 if pid < 0:
832 if pid < 0:
833 raise error.Abort(_('child process failed to start'))
833 raise error.Abort(_('child process failed to start'))
834 writepid(pid)
834 writepid(pid)
835 finally:
835 finally:
836 try:
836 try:
837 os.unlink(lockpath)
837 os.unlink(lockpath)
838 except OSError as e:
838 except OSError as e:
839 if e.errno != errno.ENOENT:
839 if e.errno != errno.ENOENT:
840 raise
840 raise
841 if parentfn:
841 if parentfn:
842 return parentfn(pid)
842 return parentfn(pid)
843 else:
843 else:
844 return
844 return
845
845
846 if initfn:
846 if initfn:
847 initfn()
847 initfn()
848
848
849 if not opts['daemon']:
849 if not opts['daemon']:
850 writepid(util.getpid())
850 writepid(util.getpid())
851
851
852 if opts['daemon_postexec']:
852 if opts['daemon_postexec']:
853 try:
853 try:
854 os.setsid()
854 os.setsid()
855 except AttributeError:
855 except AttributeError:
856 pass
856 pass
857 for inst in opts['daemon_postexec']:
857 for inst in opts['daemon_postexec']:
858 if inst.startswith('unlink:'):
858 if inst.startswith('unlink:'):
859 lockpath = inst[7:]
859 lockpath = inst[7:]
860 os.unlink(lockpath)
860 os.unlink(lockpath)
861 elif inst.startswith('chdir:'):
861 elif inst.startswith('chdir:'):
862 os.chdir(inst[6:])
862 os.chdir(inst[6:])
863 elif inst != 'none':
863 elif inst != 'none':
864 raise error.Abort(_('invalid value for --daemon-postexec: %s')
864 raise error.Abort(_('invalid value for --daemon-postexec: %s')
865 % inst)
865 % inst)
866 util.hidewindow()
866 util.hidewindow()
867 sys.stdout.flush()
867 sys.stdout.flush()
868 sys.stderr.flush()
868 sys.stderr.flush()
869
869
870 nullfd = os.open(os.devnull, os.O_RDWR)
870 nullfd = os.open(os.devnull, os.O_RDWR)
871 logfilefd = nullfd
871 logfilefd = nullfd
872 if logfile:
872 if logfile:
873 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
873 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
874 os.dup2(nullfd, 0)
874 os.dup2(nullfd, 0)
875 os.dup2(logfilefd, 1)
875 os.dup2(logfilefd, 1)
876 os.dup2(logfilefd, 2)
876 os.dup2(logfilefd, 2)
877 if nullfd not in (0, 1, 2):
877 if nullfd not in (0, 1, 2):
878 os.close(nullfd)
878 os.close(nullfd)
879 if logfile and logfilefd not in (0, 1, 2):
879 if logfile and logfilefd not in (0, 1, 2):
880 os.close(logfilefd)
880 os.close(logfilefd)
881
881
882 if runfn:
882 if runfn:
883 return runfn()
883 return runfn()
884
884
885 ## facility to let extension process additional data into an import patch
885 ## facility to let extension process additional data into an import patch
886 # list of identifier to be executed in order
886 # list of identifier to be executed in order
887 extrapreimport = [] # run before commit
887 extrapreimport = [] # run before commit
888 extrapostimport = [] # run after commit
888 extrapostimport = [] # run after commit
889 # mapping from identifier to actual import function
889 # mapping from identifier to actual import function
890 #
890 #
891 # 'preimport' are run before the commit is made and are provided the following
891 # 'preimport' are run before the commit is made and are provided the following
892 # arguments:
892 # arguments:
893 # - repo: the localrepository instance,
893 # - repo: the localrepository instance,
894 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
894 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
895 # - extra: the future extra dictionary of the changeset, please mutate it,
895 # - extra: the future extra dictionary of the changeset, please mutate it,
896 # - opts: the import options.
896 # - opts: the import options.
897 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
897 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
898 # mutation of in memory commit and more. Feel free to rework the code to get
898 # mutation of in memory commit and more. Feel free to rework the code to get
899 # there.
899 # there.
900 extrapreimportmap = {}
900 extrapreimportmap = {}
901 # 'postimport' are run after the commit is made and are provided the following
901 # 'postimport' are run after the commit is made and are provided the following
902 # argument:
902 # argument:
903 # - ctx: the changectx created by import.
903 # - ctx: the changectx created by import.
904 extrapostimportmap = {}
904 extrapostimportmap = {}
905
905
906 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
906 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
907 """Utility function used by commands.import to import a single patch
907 """Utility function used by commands.import to import a single patch
908
908
909 This function is explicitly defined here to help the evolve extension to
909 This function is explicitly defined here to help the evolve extension to
910 wrap this part of the import logic.
910 wrap this part of the import logic.
911
911
912 The API is currently a bit ugly because it a simple code translation from
912 The API is currently a bit ugly because it a simple code translation from
913 the import command. Feel free to make it better.
913 the import command. Feel free to make it better.
914
914
915 :hunk: a patch (as a binary string)
915 :hunk: a patch (as a binary string)
916 :parents: nodes that will be parent of the created commit
916 :parents: nodes that will be parent of the created commit
917 :opts: the full dict of option passed to the import command
917 :opts: the full dict of option passed to the import command
918 :msgs: list to save commit message to.
918 :msgs: list to save commit message to.
919 (used in case we need to save it when failing)
919 (used in case we need to save it when failing)
920 :updatefunc: a function that update a repo to a given node
920 :updatefunc: a function that update a repo to a given node
921 updatefunc(<repo>, <node>)
921 updatefunc(<repo>, <node>)
922 """
922 """
923 # avoid cycle context -> subrepo -> cmdutil
923 # avoid cycle context -> subrepo -> cmdutil
924 from . import context
924 from . import context
925 extractdata = patch.extract(ui, hunk)
925 extractdata = patch.extract(ui, hunk)
926 tmpname = extractdata.get('filename')
926 tmpname = extractdata.get('filename')
927 message = extractdata.get('message')
927 message = extractdata.get('message')
928 user = opts.get('user') or extractdata.get('user')
928 user = opts.get('user') or extractdata.get('user')
929 date = opts.get('date') or extractdata.get('date')
929 date = opts.get('date') or extractdata.get('date')
930 branch = extractdata.get('branch')
930 branch = extractdata.get('branch')
931 nodeid = extractdata.get('nodeid')
931 nodeid = extractdata.get('nodeid')
932 p1 = extractdata.get('p1')
932 p1 = extractdata.get('p1')
933 p2 = extractdata.get('p2')
933 p2 = extractdata.get('p2')
934
934
935 nocommit = opts.get('no_commit')
935 nocommit = opts.get('no_commit')
936 importbranch = opts.get('import_branch')
936 importbranch = opts.get('import_branch')
937 update = not opts.get('bypass')
937 update = not opts.get('bypass')
938 strip = opts["strip"]
938 strip = opts["strip"]
939 prefix = opts["prefix"]
939 prefix = opts["prefix"]
940 sim = float(opts.get('similarity') or 0)
940 sim = float(opts.get('similarity') or 0)
941 if not tmpname:
941 if not tmpname:
942 return (None, None, False)
942 return (None, None, False)
943
943
944 rejects = False
944 rejects = False
945
945
946 try:
946 try:
947 cmdline_message = logmessage(ui, opts)
947 cmdline_message = logmessage(ui, opts)
948 if cmdline_message:
948 if cmdline_message:
949 # pickup the cmdline msg
949 # pickup the cmdline msg
950 message = cmdline_message
950 message = cmdline_message
951 elif message:
951 elif message:
952 # pickup the patch msg
952 # pickup the patch msg
953 message = message.strip()
953 message = message.strip()
954 else:
954 else:
955 # launch the editor
955 # launch the editor
956 message = None
956 message = None
957 ui.debug('message:\n%s\n' % message)
957 ui.debug('message:\n%s\n' % message)
958
958
959 if len(parents) == 1:
959 if len(parents) == 1:
960 parents.append(repo[nullid])
960 parents.append(repo[nullid])
961 if opts.get('exact'):
961 if opts.get('exact'):
962 if not nodeid or not p1:
962 if not nodeid or not p1:
963 raise error.Abort(_('not a Mercurial patch'))
963 raise error.Abort(_('not a Mercurial patch'))
964 p1 = repo[p1]
964 p1 = repo[p1]
965 p2 = repo[p2 or nullid]
965 p2 = repo[p2 or nullid]
966 elif p2:
966 elif p2:
967 try:
967 try:
968 p1 = repo[p1]
968 p1 = repo[p1]
969 p2 = repo[p2]
969 p2 = repo[p2]
970 # Without any options, consider p2 only if the
970 # Without any options, consider p2 only if the
971 # patch is being applied on top of the recorded
971 # patch is being applied on top of the recorded
972 # first parent.
972 # first parent.
973 if p1 != parents[0]:
973 if p1 != parents[0]:
974 p1 = parents[0]
974 p1 = parents[0]
975 p2 = repo[nullid]
975 p2 = repo[nullid]
976 except error.RepoError:
976 except error.RepoError:
977 p1, p2 = parents
977 p1, p2 = parents
978 if p2.node() == nullid:
978 if p2.node() == nullid:
979 ui.warn(_("warning: import the patch as a normal revision\n"
979 ui.warn(_("warning: import the patch as a normal revision\n"
980 "(use --exact to import the patch as a merge)\n"))
980 "(use --exact to import the patch as a merge)\n"))
981 else:
981 else:
982 p1, p2 = parents
982 p1, p2 = parents
983
983
984 n = None
984 n = None
985 if update:
985 if update:
986 if p1 != parents[0]:
986 if p1 != parents[0]:
987 updatefunc(repo, p1.node())
987 updatefunc(repo, p1.node())
988 if p2 != parents[1]:
988 if p2 != parents[1]:
989 repo.setparents(p1.node(), p2.node())
989 repo.setparents(p1.node(), p2.node())
990
990
991 if opts.get('exact') or importbranch:
991 if opts.get('exact') or importbranch:
992 repo.dirstate.setbranch(branch or 'default')
992 repo.dirstate.setbranch(branch or 'default')
993
993
994 partial = opts.get('partial', False)
994 partial = opts.get('partial', False)
995 files = set()
995 files = set()
996 try:
996 try:
997 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
997 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
998 files=files, eolmode=None, similarity=sim / 100.0)
998 files=files, eolmode=None, similarity=sim / 100.0)
999 except patch.PatchError as e:
999 except patch.PatchError as e:
1000 if not partial:
1000 if not partial:
1001 raise error.Abort(str(e))
1001 raise error.Abort(str(e))
1002 if partial:
1002 if partial:
1003 rejects = True
1003 rejects = True
1004
1004
1005 files = list(files)
1005 files = list(files)
1006 if nocommit:
1006 if nocommit:
1007 if message:
1007 if message:
1008 msgs.append(message)
1008 msgs.append(message)
1009 else:
1009 else:
1010 if opts.get('exact') or p2:
1010 if opts.get('exact') or p2:
1011 # If you got here, you either use --force and know what
1011 # If you got here, you either use --force and know what
1012 # you are doing or used --exact or a merge patch while
1012 # you are doing or used --exact or a merge patch while
1013 # being updated to its first parent.
1013 # being updated to its first parent.
1014 m = None
1014 m = None
1015 else:
1015 else:
1016 m = scmutil.matchfiles(repo, files or [])
1016 m = scmutil.matchfiles(repo, files or [])
1017 editform = mergeeditform(repo[None], 'import.normal')
1017 editform = mergeeditform(repo[None], 'import.normal')
1018 if opts.get('exact'):
1018 if opts.get('exact'):
1019 editor = None
1019 editor = None
1020 else:
1020 else:
1021 editor = getcommiteditor(editform=editform, **opts)
1021 editor = getcommiteditor(editform=editform, **opts)
1022 allowemptyback = repo.ui.backupconfig('ui', 'allowemptycommit')
1022 allowemptyback = repo.ui.backupconfig('ui', 'allowemptycommit')
1023 extra = {}
1023 extra = {}
1024 for idfunc in extrapreimport:
1024 for idfunc in extrapreimport:
1025 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
1025 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
1026 try:
1026 try:
1027 if partial:
1027 if partial:
1028 repo.ui.setconfig('ui', 'allowemptycommit', True)
1028 repo.ui.setconfig('ui', 'allowemptycommit', True)
1029 n = repo.commit(message, user,
1029 n = repo.commit(message, user,
1030 date, match=m,
1030 date, match=m,
1031 editor=editor, extra=extra)
1031 editor=editor, extra=extra)
1032 for idfunc in extrapostimport:
1032 for idfunc in extrapostimport:
1033 extrapostimportmap[idfunc](repo[n])
1033 extrapostimportmap[idfunc](repo[n])
1034 finally:
1034 finally:
1035 repo.ui.restoreconfig(allowemptyback)
1035 repo.ui.restoreconfig(allowemptyback)
1036 else:
1036 else:
1037 if opts.get('exact') or importbranch:
1037 if opts.get('exact') or importbranch:
1038 branch = branch or 'default'
1038 branch = branch or 'default'
1039 else:
1039 else:
1040 branch = p1.branch()
1040 branch = p1.branch()
1041 store = patch.filestore()
1041 store = patch.filestore()
1042 try:
1042 try:
1043 files = set()
1043 files = set()
1044 try:
1044 try:
1045 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1045 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1046 files, eolmode=None)
1046 files, eolmode=None)
1047 except patch.PatchError as e:
1047 except patch.PatchError as e:
1048 raise error.Abort(str(e))
1048 raise error.Abort(str(e))
1049 if opts.get('exact'):
1049 if opts.get('exact'):
1050 editor = None
1050 editor = None
1051 else:
1051 else:
1052 editor = getcommiteditor(editform='import.bypass')
1052 editor = getcommiteditor(editform='import.bypass')
1053 memctx = context.makememctx(repo, (p1.node(), p2.node()),
1053 memctx = context.makememctx(repo, (p1.node(), p2.node()),
1054 message,
1054 message,
1055 user,
1055 user,
1056 date,
1056 date,
1057 branch, files, store,
1057 branch, files, store,
1058 editor=editor)
1058 editor=editor)
1059 n = memctx.commit()
1059 n = memctx.commit()
1060 finally:
1060 finally:
1061 store.close()
1061 store.close()
1062 if opts.get('exact') and nocommit:
1062 if opts.get('exact') and nocommit:
1063 # --exact with --no-commit is still useful in that it does merge
1063 # --exact with --no-commit is still useful in that it does merge
1064 # and branch bits
1064 # and branch bits
1065 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1065 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1066 elif opts.get('exact') and hex(n) != nodeid:
1066 elif opts.get('exact') and hex(n) != nodeid:
1067 raise error.Abort(_('patch is damaged or loses information'))
1067 raise error.Abort(_('patch is damaged or loses information'))
1068 msg = _('applied to working directory')
1068 msg = _('applied to working directory')
1069 if n:
1069 if n:
1070 # i18n: refers to a short changeset id
1070 # i18n: refers to a short changeset id
1071 msg = _('created %s') % short(n)
1071 msg = _('created %s') % short(n)
1072 return (msg, n, rejects)
1072 return (msg, n, rejects)
1073 finally:
1073 finally:
1074 os.unlink(tmpname)
1074 os.unlink(tmpname)
1075
1075
1076 # facility to let extensions include additional data in an exported patch
1076 # facility to let extensions include additional data in an exported patch
1077 # list of identifiers to be executed in order
1077 # list of identifiers to be executed in order
1078 extraexport = []
1078 extraexport = []
1079 # mapping from identifier to actual export function
1079 # mapping from identifier to actual export function
1080 # function as to return a string to be added to the header or None
1080 # function as to return a string to be added to the header or None
1081 # it is given two arguments (sequencenumber, changectx)
1081 # it is given two arguments (sequencenumber, changectx)
1082 extraexportmap = {}
1082 extraexportmap = {}
1083
1083
1084 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1084 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1085 opts=None, match=None):
1085 opts=None, match=None):
1086 '''export changesets as hg patches.'''
1086 '''export changesets as hg patches.'''
1087
1087
1088 total = len(revs)
1088 total = len(revs)
1089 revwidth = max([len(str(rev)) for rev in revs])
1089 revwidth = max([len(str(rev)) for rev in revs])
1090 filemode = {}
1090 filemode = {}
1091
1091
1092 def single(rev, seqno, fp):
1092 def single(rev, seqno, fp):
1093 ctx = repo[rev]
1093 ctx = repo[rev]
1094 node = ctx.node()
1094 node = ctx.node()
1095 parents = [p.node() for p in ctx.parents() if p]
1095 parents = [p.node() for p in ctx.parents() if p]
1096 branch = ctx.branch()
1096 branch = ctx.branch()
1097 if switch_parent:
1097 if switch_parent:
1098 parents.reverse()
1098 parents.reverse()
1099
1099
1100 if parents:
1100 if parents:
1101 prev = parents[0]
1101 prev = parents[0]
1102 else:
1102 else:
1103 prev = nullid
1103 prev = nullid
1104
1104
1105 shouldclose = False
1105 shouldclose = False
1106 if not fp and len(template) > 0:
1106 if not fp and len(template) > 0:
1107 desc_lines = ctx.description().rstrip().split('\n')
1107 desc_lines = ctx.description().rstrip().split('\n')
1108 desc = desc_lines[0] #Commit always has a first line.
1108 desc = desc_lines[0] #Commit always has a first line.
1109 fp = makefileobj(repo, template, node, desc=desc, total=total,
1109 fp = makefileobj(repo, template, node, desc=desc, total=total,
1110 seqno=seqno, revwidth=revwidth, mode='wb',
1110 seqno=seqno, revwidth=revwidth, mode='wb',
1111 modemap=filemode)
1111 modemap=filemode)
1112 shouldclose = True
1112 shouldclose = True
1113 if fp and not getattr(fp, 'name', '<unnamed>').startswith('<'):
1113 if fp and not getattr(fp, 'name', '<unnamed>').startswith('<'):
1114 repo.ui.note("%s\n" % fp.name)
1114 repo.ui.note("%s\n" % fp.name)
1115
1115
1116 if not fp:
1116 if not fp:
1117 write = repo.ui.write
1117 write = repo.ui.write
1118 else:
1118 else:
1119 def write(s, **kw):
1119 def write(s, **kw):
1120 fp.write(s)
1120 fp.write(s)
1121
1121
1122 write("# HG changeset patch\n")
1122 write("# HG changeset patch\n")
1123 write("# User %s\n" % ctx.user())
1123 write("# User %s\n" % ctx.user())
1124 write("# Date %d %d\n" % ctx.date())
1124 write("# Date %d %d\n" % ctx.date())
1125 write("# %s\n" % util.datestr(ctx.date()))
1125 write("# %s\n" % util.datestr(ctx.date()))
1126 if branch and branch != 'default':
1126 if branch and branch != 'default':
1127 write("# Branch %s\n" % branch)
1127 write("# Branch %s\n" % branch)
1128 write("# Node ID %s\n" % hex(node))
1128 write("# Node ID %s\n" % hex(node))
1129 write("# Parent %s\n" % hex(prev))
1129 write("# Parent %s\n" % hex(prev))
1130 if len(parents) > 1:
1130 if len(parents) > 1:
1131 write("# Parent %s\n" % hex(parents[1]))
1131 write("# Parent %s\n" % hex(parents[1]))
1132
1132
1133 for headerid in extraexport:
1133 for headerid in extraexport:
1134 header = extraexportmap[headerid](seqno, ctx)
1134 header = extraexportmap[headerid](seqno, ctx)
1135 if header is not None:
1135 if header is not None:
1136 write('# %s\n' % header)
1136 write('# %s\n' % header)
1137 write(ctx.description().rstrip())
1137 write(ctx.description().rstrip())
1138 write("\n\n")
1138 write("\n\n")
1139
1139
1140 for chunk, label in patch.diffui(repo, prev, node, match, opts=opts):
1140 for chunk, label in patch.diffui(repo, prev, node, match, opts=opts):
1141 write(chunk, label=label)
1141 write(chunk, label=label)
1142
1142
1143 if shouldclose:
1143 if shouldclose:
1144 fp.close()
1144 fp.close()
1145
1145
1146 for seqno, rev in enumerate(revs):
1146 for seqno, rev in enumerate(revs):
1147 single(rev, seqno + 1, fp)
1147 single(rev, seqno + 1, fp)
1148
1148
1149 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1149 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1150 changes=None, stat=False, fp=None, prefix='',
1150 changes=None, stat=False, fp=None, prefix='',
1151 root='', listsubrepos=False):
1151 root='', listsubrepos=False):
1152 '''show diff or diffstat.'''
1152 '''show diff or diffstat.'''
1153 if fp is None:
1153 if fp is None:
1154 write = ui.write
1154 write = ui.write
1155 else:
1155 else:
1156 def write(s, **kw):
1156 def write(s, **kw):
1157 fp.write(s)
1157 fp.write(s)
1158
1158
1159 if root:
1159 if root:
1160 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1160 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1161 else:
1161 else:
1162 relroot = ''
1162 relroot = ''
1163 if relroot != '':
1163 if relroot != '':
1164 # XXX relative roots currently don't work if the root is within a
1164 # XXX relative roots currently don't work if the root is within a
1165 # subrepo
1165 # subrepo
1166 uirelroot = match.uipath(relroot)
1166 uirelroot = match.uipath(relroot)
1167 relroot += '/'
1167 relroot += '/'
1168 for matchroot in match.files():
1168 for matchroot in match.files():
1169 if not matchroot.startswith(relroot):
1169 if not matchroot.startswith(relroot):
1170 ui.warn(_('warning: %s not inside relative root %s\n') % (
1170 ui.warn(_('warning: %s not inside relative root %s\n') % (
1171 match.uipath(matchroot), uirelroot))
1171 match.uipath(matchroot), uirelroot))
1172
1172
1173 if stat:
1173 if stat:
1174 diffopts = diffopts.copy(context=0)
1174 diffopts = diffopts.copy(context=0)
1175 width = 80
1175 width = 80
1176 if not ui.plain():
1176 if not ui.plain():
1177 width = ui.termwidth()
1177 width = ui.termwidth()
1178 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1178 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1179 prefix=prefix, relroot=relroot)
1179 prefix=prefix, relroot=relroot)
1180 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1180 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1181 width=width,
1181 width=width,
1182 git=diffopts.git):
1182 git=diffopts.git):
1183 write(chunk, label=label)
1183 write(chunk, label=label)
1184 else:
1184 else:
1185 for chunk, label in patch.diffui(repo, node1, node2, match,
1185 for chunk, label in patch.diffui(repo, node1, node2, match,
1186 changes, diffopts, prefix=prefix,
1186 changes, diffopts, prefix=prefix,
1187 relroot=relroot):
1187 relroot=relroot):
1188 write(chunk, label=label)
1188 write(chunk, label=label)
1189
1189
1190 if listsubrepos:
1190 if listsubrepos:
1191 ctx1 = repo[node1]
1191 ctx1 = repo[node1]
1192 ctx2 = repo[node2]
1192 ctx2 = repo[node2]
1193 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1193 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1194 tempnode2 = node2
1194 tempnode2 = node2
1195 try:
1195 try:
1196 if node2 is not None:
1196 if node2 is not None:
1197 tempnode2 = ctx2.substate[subpath][1]
1197 tempnode2 = ctx2.substate[subpath][1]
1198 except KeyError:
1198 except KeyError:
1199 # A subrepo that existed in node1 was deleted between node1 and
1199 # A subrepo that existed in node1 was deleted between node1 and
1200 # node2 (inclusive). Thus, ctx2's substate won't contain that
1200 # node2 (inclusive). Thus, ctx2's substate won't contain that
1201 # subpath. The best we can do is to ignore it.
1201 # subpath. The best we can do is to ignore it.
1202 tempnode2 = None
1202 tempnode2 = None
1203 submatch = matchmod.subdirmatcher(subpath, match)
1203 submatch = matchmod.subdirmatcher(subpath, match)
1204 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1204 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1205 stat=stat, fp=fp, prefix=prefix)
1205 stat=stat, fp=fp, prefix=prefix)
1206
1206
1207 class changeset_printer(object):
1207 class changeset_printer(object):
1208 '''show changeset information when templating not requested.'''
1208 '''show changeset information when templating not requested.'''
1209
1209
1210 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1210 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1211 self.ui = ui
1211 self.ui = ui
1212 self.repo = repo
1212 self.repo = repo
1213 self.buffered = buffered
1213 self.buffered = buffered
1214 self.matchfn = matchfn
1214 self.matchfn = matchfn
1215 self.diffopts = diffopts
1215 self.diffopts = diffopts
1216 self.header = {}
1216 self.header = {}
1217 self.hunk = {}
1217 self.hunk = {}
1218 self.lastheader = None
1218 self.lastheader = None
1219 self.footer = None
1219 self.footer = None
1220
1220
1221 def flush(self, ctx):
1221 def flush(self, ctx):
1222 rev = ctx.rev()
1222 rev = ctx.rev()
1223 if rev in self.header:
1223 if rev in self.header:
1224 h = self.header[rev]
1224 h = self.header[rev]
1225 if h != self.lastheader:
1225 if h != self.lastheader:
1226 self.lastheader = h
1226 self.lastheader = h
1227 self.ui.write(h)
1227 self.ui.write(h)
1228 del self.header[rev]
1228 del self.header[rev]
1229 if rev in self.hunk:
1229 if rev in self.hunk:
1230 self.ui.write(self.hunk[rev])
1230 self.ui.write(self.hunk[rev])
1231 del self.hunk[rev]
1231 del self.hunk[rev]
1232 return 1
1232 return 1
1233 return 0
1233 return 0
1234
1234
1235 def close(self):
1235 def close(self):
1236 if self.footer:
1236 if self.footer:
1237 self.ui.write(self.footer)
1237 self.ui.write(self.footer)
1238
1238
1239 def show(self, ctx, copies=None, matchfn=None, **props):
1239 def show(self, ctx, copies=None, matchfn=None, **props):
1240 if self.buffered:
1240 if self.buffered:
1241 self.ui.pushbuffer(labeled=True)
1241 self.ui.pushbuffer(labeled=True)
1242 self._show(ctx, copies, matchfn, props)
1242 self._show(ctx, copies, matchfn, props)
1243 self.hunk[ctx.rev()] = self.ui.popbuffer()
1243 self.hunk[ctx.rev()] = self.ui.popbuffer()
1244 else:
1244 else:
1245 self._show(ctx, copies, matchfn, props)
1245 self._show(ctx, copies, matchfn, props)
1246
1246
1247 def _show(self, ctx, copies, matchfn, props):
1247 def _show(self, ctx, copies, matchfn, props):
1248 '''show a single changeset or file revision'''
1248 '''show a single changeset or file revision'''
1249 changenode = ctx.node()
1249 changenode = ctx.node()
1250 rev = ctx.rev()
1250 rev = ctx.rev()
1251 if self.ui.debugflag:
1251 if self.ui.debugflag:
1252 hexfunc = hex
1252 hexfunc = hex
1253 else:
1253 else:
1254 hexfunc = short
1254 hexfunc = short
1255 # as of now, wctx.node() and wctx.rev() return None, but we want to
1255 # as of now, wctx.node() and wctx.rev() return None, but we want to
1256 # show the same values as {node} and {rev} templatekw
1256 # show the same values as {node} and {rev} templatekw
1257 revnode = (scmutil.intrev(rev), hexfunc(bin(ctx.hex())))
1257 revnode = (scmutil.intrev(rev), hexfunc(bin(ctx.hex())))
1258
1258
1259 if self.ui.quiet:
1259 if self.ui.quiet:
1260 self.ui.write("%d:%s\n" % revnode, label='log.node')
1260 self.ui.write("%d:%s\n" % revnode, label='log.node')
1261 return
1261 return
1262
1262
1263 date = util.datestr(ctx.date())
1263 date = util.datestr(ctx.date())
1264
1264
1265 # i18n: column positioning for "hg log"
1265 # i18n: column positioning for "hg log"
1266 self.ui.write(_("changeset: %d:%s\n") % revnode,
1266 self.ui.write(_("changeset: %d:%s\n") % revnode,
1267 label='log.changeset changeset.%s' % ctx.phasestr())
1267 label='log.changeset changeset.%s' % ctx.phasestr())
1268
1268
1269 # branches are shown first before any other names due to backwards
1269 # branches are shown first before any other names due to backwards
1270 # compatibility
1270 # compatibility
1271 branch = ctx.branch()
1271 branch = ctx.branch()
1272 # don't show the default branch name
1272 # don't show the default branch name
1273 if branch != 'default':
1273 if branch != 'default':
1274 # i18n: column positioning for "hg log"
1274 # i18n: column positioning for "hg log"
1275 self.ui.write(_("branch: %s\n") % branch,
1275 self.ui.write(_("branch: %s\n") % branch,
1276 label='log.branch')
1276 label='log.branch')
1277
1277
1278 for nsname, ns in self.repo.names.iteritems():
1278 for nsname, ns in self.repo.names.iteritems():
1279 # branches has special logic already handled above, so here we just
1279 # branches has special logic already handled above, so here we just
1280 # skip it
1280 # skip it
1281 if nsname == 'branches':
1281 if nsname == 'branches':
1282 continue
1282 continue
1283 # we will use the templatename as the color name since those two
1283 # we will use the templatename as the color name since those two
1284 # should be the same
1284 # should be the same
1285 for name in ns.names(self.repo, changenode):
1285 for name in ns.names(self.repo, changenode):
1286 self.ui.write(ns.logfmt % name,
1286 self.ui.write(ns.logfmt % name,
1287 label='log.%s' % ns.colorname)
1287 label='log.%s' % ns.colorname)
1288 if self.ui.debugflag:
1288 if self.ui.debugflag:
1289 # i18n: column positioning for "hg log"
1289 # i18n: column positioning for "hg log"
1290 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1290 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1291 label='log.phase')
1291 label='log.phase')
1292 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1292 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1293 label = 'log.parent changeset.%s' % pctx.phasestr()
1293 label = 'log.parent changeset.%s' % pctx.phasestr()
1294 # i18n: column positioning for "hg log"
1294 # i18n: column positioning for "hg log"
1295 self.ui.write(_("parent: %d:%s\n")
1295 self.ui.write(_("parent: %d:%s\n")
1296 % (pctx.rev(), hexfunc(pctx.node())),
1296 % (pctx.rev(), hexfunc(pctx.node())),
1297 label=label)
1297 label=label)
1298
1298
1299 if self.ui.debugflag and rev is not None:
1299 if self.ui.debugflag and rev is not None:
1300 mnode = ctx.manifestnode()
1300 mnode = ctx.manifestnode()
1301 # i18n: column positioning for "hg log"
1301 # i18n: column positioning for "hg log"
1302 self.ui.write(_("manifest: %d:%s\n") %
1302 self.ui.write(_("manifest: %d:%s\n") %
1303 (self.repo.manifest.rev(mnode), hex(mnode)),
1303 (self.repo.manifest.rev(mnode), hex(mnode)),
1304 label='ui.debug log.manifest')
1304 label='ui.debug log.manifest')
1305 # i18n: column positioning for "hg log"
1305 # i18n: column positioning for "hg log"
1306 self.ui.write(_("user: %s\n") % ctx.user(),
1306 self.ui.write(_("user: %s\n") % ctx.user(),
1307 label='log.user')
1307 label='log.user')
1308 # i18n: column positioning for "hg log"
1308 # i18n: column positioning for "hg log"
1309 self.ui.write(_("date: %s\n") % date,
1309 self.ui.write(_("date: %s\n") % date,
1310 label='log.date')
1310 label='log.date')
1311
1311
1312 if self.ui.debugflag:
1312 if self.ui.debugflag:
1313 files = ctx.p1().status(ctx)[:3]
1313 files = ctx.p1().status(ctx)[:3]
1314 for key, value in zip([# i18n: column positioning for "hg log"
1314 for key, value in zip([# i18n: column positioning for "hg log"
1315 _("files:"),
1315 _("files:"),
1316 # i18n: column positioning for "hg log"
1316 # i18n: column positioning for "hg log"
1317 _("files+:"),
1317 _("files+:"),
1318 # i18n: column positioning for "hg log"
1318 # i18n: column positioning for "hg log"
1319 _("files-:")], files):
1319 _("files-:")], files):
1320 if value:
1320 if value:
1321 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1321 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1322 label='ui.debug log.files')
1322 label='ui.debug log.files')
1323 elif ctx.files() and self.ui.verbose:
1323 elif ctx.files() and self.ui.verbose:
1324 # i18n: column positioning for "hg log"
1324 # i18n: column positioning for "hg log"
1325 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1325 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1326 label='ui.note log.files')
1326 label='ui.note log.files')
1327 if copies and self.ui.verbose:
1327 if copies and self.ui.verbose:
1328 copies = ['%s (%s)' % c for c in copies]
1328 copies = ['%s (%s)' % c for c in copies]
1329 # i18n: column positioning for "hg log"
1329 # i18n: column positioning for "hg log"
1330 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1330 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1331 label='ui.note log.copies')
1331 label='ui.note log.copies')
1332
1332
1333 extra = ctx.extra()
1333 extra = ctx.extra()
1334 if extra and self.ui.debugflag:
1334 if extra and self.ui.debugflag:
1335 for key, value in sorted(extra.items()):
1335 for key, value in sorted(extra.items()):
1336 # i18n: column positioning for "hg log"
1336 # i18n: column positioning for "hg log"
1337 self.ui.write(_("extra: %s=%s\n")
1337 self.ui.write(_("extra: %s=%s\n")
1338 % (key, value.encode('string_escape')),
1338 % (key, value.encode('string_escape')),
1339 label='ui.debug log.extra')
1339 label='ui.debug log.extra')
1340
1340
1341 description = ctx.description().strip()
1341 description = ctx.description().strip()
1342 if description:
1342 if description:
1343 if self.ui.verbose:
1343 if self.ui.verbose:
1344 self.ui.write(_("description:\n"),
1344 self.ui.write(_("description:\n"),
1345 label='ui.note log.description')
1345 label='ui.note log.description')
1346 self.ui.write(description,
1346 self.ui.write(description,
1347 label='ui.note log.description')
1347 label='ui.note log.description')
1348 self.ui.write("\n\n")
1348 self.ui.write("\n\n")
1349 else:
1349 else:
1350 # i18n: column positioning for "hg log"
1350 # i18n: column positioning for "hg log"
1351 self.ui.write(_("summary: %s\n") %
1351 self.ui.write(_("summary: %s\n") %
1352 description.splitlines()[0],
1352 description.splitlines()[0],
1353 label='log.summary')
1353 label='log.summary')
1354 self.ui.write("\n")
1354 self.ui.write("\n")
1355
1355
1356 self.showpatch(ctx, matchfn)
1356 self.showpatch(ctx, matchfn)
1357
1357
1358 def showpatch(self, ctx, matchfn):
1358 def showpatch(self, ctx, matchfn):
1359 if not matchfn:
1359 if not matchfn:
1360 matchfn = self.matchfn
1360 matchfn = self.matchfn
1361 if matchfn:
1361 if matchfn:
1362 stat = self.diffopts.get('stat')
1362 stat = self.diffopts.get('stat')
1363 diff = self.diffopts.get('patch')
1363 diff = self.diffopts.get('patch')
1364 diffopts = patch.diffallopts(self.ui, self.diffopts)
1364 diffopts = patch.diffallopts(self.ui, self.diffopts)
1365 node = ctx.node()
1365 node = ctx.node()
1366 prev = ctx.p1().node()
1366 prev = ctx.p1().node()
1367 if stat:
1367 if stat:
1368 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1368 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1369 match=matchfn, stat=True)
1369 match=matchfn, stat=True)
1370 if diff:
1370 if diff:
1371 if stat:
1371 if stat:
1372 self.ui.write("\n")
1372 self.ui.write("\n")
1373 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1373 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1374 match=matchfn, stat=False)
1374 match=matchfn, stat=False)
1375 self.ui.write("\n")
1375 self.ui.write("\n")
1376
1376
1377 class jsonchangeset(changeset_printer):
1377 class jsonchangeset(changeset_printer):
1378 '''format changeset information.'''
1378 '''format changeset information.'''
1379
1379
1380 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1380 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1381 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1381 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1382 self.cache = {}
1382 self.cache = {}
1383 self._first = True
1383 self._first = True
1384
1384
1385 def close(self):
1385 def close(self):
1386 if not self._first:
1386 if not self._first:
1387 self.ui.write("\n]\n")
1387 self.ui.write("\n]\n")
1388 else:
1388 else:
1389 self.ui.write("[]\n")
1389 self.ui.write("[]\n")
1390
1390
1391 def _show(self, ctx, copies, matchfn, props):
1391 def _show(self, ctx, copies, matchfn, props):
1392 '''show a single changeset or file revision'''
1392 '''show a single changeset or file revision'''
1393 rev = ctx.rev()
1393 rev = ctx.rev()
1394 if rev is None:
1394 if rev is None:
1395 jrev = jnode = 'null'
1395 jrev = jnode = 'null'
1396 else:
1396 else:
1397 jrev = str(rev)
1397 jrev = str(rev)
1398 jnode = '"%s"' % hex(ctx.node())
1398 jnode = '"%s"' % hex(ctx.node())
1399 j = encoding.jsonescape
1399 j = encoding.jsonescape
1400
1400
1401 if self._first:
1401 if self._first:
1402 self.ui.write("[\n {")
1402 self.ui.write("[\n {")
1403 self._first = False
1403 self._first = False
1404 else:
1404 else:
1405 self.ui.write(",\n {")
1405 self.ui.write(",\n {")
1406
1406
1407 if self.ui.quiet:
1407 if self.ui.quiet:
1408 self.ui.write(('\n "rev": %s') % jrev)
1408 self.ui.write(('\n "rev": %s') % jrev)
1409 self.ui.write((',\n "node": %s') % jnode)
1409 self.ui.write((',\n "node": %s') % jnode)
1410 self.ui.write('\n }')
1410 self.ui.write('\n }')
1411 return
1411 return
1412
1412
1413 self.ui.write(('\n "rev": %s') % jrev)
1413 self.ui.write(('\n "rev": %s') % jrev)
1414 self.ui.write((',\n "node": %s') % jnode)
1414 self.ui.write((',\n "node": %s') % jnode)
1415 self.ui.write((',\n "branch": "%s"') % j(ctx.branch()))
1415 self.ui.write((',\n "branch": "%s"') % j(ctx.branch()))
1416 self.ui.write((',\n "phase": "%s"') % ctx.phasestr())
1416 self.ui.write((',\n "phase": "%s"') % ctx.phasestr())
1417 self.ui.write((',\n "user": "%s"') % j(ctx.user()))
1417 self.ui.write((',\n "user": "%s"') % j(ctx.user()))
1418 self.ui.write((',\n "date": [%d, %d]') % ctx.date())
1418 self.ui.write((',\n "date": [%d, %d]') % ctx.date())
1419 self.ui.write((',\n "desc": "%s"') % j(ctx.description()))
1419 self.ui.write((',\n "desc": "%s"') % j(ctx.description()))
1420
1420
1421 self.ui.write((',\n "bookmarks": [%s]') %
1421 self.ui.write((',\n "bookmarks": [%s]') %
1422 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1422 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1423 self.ui.write((',\n "tags": [%s]') %
1423 self.ui.write((',\n "tags": [%s]') %
1424 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1424 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1425 self.ui.write((',\n "parents": [%s]') %
1425 self.ui.write((',\n "parents": [%s]') %
1426 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1426 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1427
1427
1428 if self.ui.debugflag:
1428 if self.ui.debugflag:
1429 if rev is None:
1429 if rev is None:
1430 jmanifestnode = 'null'
1430 jmanifestnode = 'null'
1431 else:
1431 else:
1432 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1432 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1433 self.ui.write((',\n "manifest": %s') % jmanifestnode)
1433 self.ui.write((',\n "manifest": %s') % jmanifestnode)
1434
1434
1435 self.ui.write((',\n "extra": {%s}') %
1435 self.ui.write((',\n "extra": {%s}') %
1436 ", ".join('"%s": "%s"' % (j(k), j(v))
1436 ", ".join('"%s": "%s"' % (j(k), j(v))
1437 for k, v in ctx.extra().items()))
1437 for k, v in ctx.extra().items()))
1438
1438
1439 files = ctx.p1().status(ctx)
1439 files = ctx.p1().status(ctx)
1440 self.ui.write((',\n "modified": [%s]') %
1440 self.ui.write((',\n "modified": [%s]') %
1441 ", ".join('"%s"' % j(f) for f in files[0]))
1441 ", ".join('"%s"' % j(f) for f in files[0]))
1442 self.ui.write((',\n "added": [%s]') %
1442 self.ui.write((',\n "added": [%s]') %
1443 ", ".join('"%s"' % j(f) for f in files[1]))
1443 ", ".join('"%s"' % j(f) for f in files[1]))
1444 self.ui.write((',\n "removed": [%s]') %
1444 self.ui.write((',\n "removed": [%s]') %
1445 ", ".join('"%s"' % j(f) for f in files[2]))
1445 ", ".join('"%s"' % j(f) for f in files[2]))
1446
1446
1447 elif self.ui.verbose:
1447 elif self.ui.verbose:
1448 self.ui.write((',\n "files": [%s]') %
1448 self.ui.write((',\n "files": [%s]') %
1449 ", ".join('"%s"' % j(f) for f in ctx.files()))
1449 ", ".join('"%s"' % j(f) for f in ctx.files()))
1450
1450
1451 if copies:
1451 if copies:
1452 self.ui.write((',\n "copies": {%s}') %
1452 self.ui.write((',\n "copies": {%s}') %
1453 ", ".join('"%s": "%s"' % (j(k), j(v))
1453 ", ".join('"%s": "%s"' % (j(k), j(v))
1454 for k, v in copies))
1454 for k, v in copies))
1455
1455
1456 matchfn = self.matchfn
1456 matchfn = self.matchfn
1457 if matchfn:
1457 if matchfn:
1458 stat = self.diffopts.get('stat')
1458 stat = self.diffopts.get('stat')
1459 diff = self.diffopts.get('patch')
1459 diff = self.diffopts.get('patch')
1460 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1460 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1461 node, prev = ctx.node(), ctx.p1().node()
1461 node, prev = ctx.node(), ctx.p1().node()
1462 if stat:
1462 if stat:
1463 self.ui.pushbuffer()
1463 self.ui.pushbuffer()
1464 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1464 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1465 match=matchfn, stat=True)
1465 match=matchfn, stat=True)
1466 self.ui.write((',\n "diffstat": "%s"')
1466 self.ui.write((',\n "diffstat": "%s"')
1467 % j(self.ui.popbuffer()))
1467 % j(self.ui.popbuffer()))
1468 if diff:
1468 if diff:
1469 self.ui.pushbuffer()
1469 self.ui.pushbuffer()
1470 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1470 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1471 match=matchfn, stat=False)
1471 match=matchfn, stat=False)
1472 self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer()))
1472 self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer()))
1473
1473
1474 self.ui.write("\n }")
1474 self.ui.write("\n }")
1475
1475
1476 class changeset_templater(changeset_printer):
1476 class changeset_templater(changeset_printer):
1477 '''format changeset information.'''
1477 '''format changeset information.'''
1478
1478
1479 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1479 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1480 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1480 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1481 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1481 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1482 filters = {'formatnode': formatnode}
1482 filters = {'formatnode': formatnode}
1483 defaulttempl = {
1483 defaulttempl = {
1484 'parent': '{rev}:{node|formatnode} ',
1484 'parent': '{rev}:{node|formatnode} ',
1485 'manifest': '{rev}:{node|formatnode}',
1485 'manifest': '{rev}:{node|formatnode}',
1486 'file_copy': '{name} ({source})',
1486 'file_copy': '{name} ({source})',
1487 'extra': '{key}={value|stringescape}'
1487 'extra': '{key}={value|stringescape}'
1488 }
1488 }
1489 # filecopy is preserved for compatibility reasons
1489 # filecopy is preserved for compatibility reasons
1490 defaulttempl['filecopy'] = defaulttempl['file_copy']
1490 defaulttempl['filecopy'] = defaulttempl['file_copy']
1491 assert not (tmpl and mapfile)
1491 assert not (tmpl and mapfile)
1492 if mapfile:
1492 if mapfile:
1493 self.t = templater.templater.frommapfile(mapfile, filters=filters,
1493 self.t = templater.templater.frommapfile(mapfile, filters=filters,
1494 cache=defaulttempl)
1494 cache=defaulttempl)
1495 else:
1495 else:
1496 self.t = formatter.maketemplater(ui, 'changeset', tmpl,
1496 self.t = formatter.maketemplater(ui, 'changeset', tmpl,
1497 filters=filters,
1497 filters=filters,
1498 cache=defaulttempl)
1498 cache=defaulttempl)
1499
1499
1500 self.cache = {}
1500 self.cache = {}
1501
1501
1502 # find correct templates for current mode
1502 # find correct templates for current mode
1503 tmplmodes = [
1503 tmplmodes = [
1504 (True, None),
1504 (True, None),
1505 (self.ui.verbose, 'verbose'),
1505 (self.ui.verbose, 'verbose'),
1506 (self.ui.quiet, 'quiet'),
1506 (self.ui.quiet, 'quiet'),
1507 (self.ui.debugflag, 'debug'),
1507 (self.ui.debugflag, 'debug'),
1508 ]
1508 ]
1509
1509
1510 self._parts = {'header': '', 'footer': '', 'changeset': 'changeset',
1510 self._parts = {'header': '', 'footer': '', 'changeset': 'changeset',
1511 'docheader': '', 'docfooter': ''}
1511 'docheader': '', 'docfooter': ''}
1512 for mode, postfix in tmplmodes:
1512 for mode, postfix in tmplmodes:
1513 for t in self._parts:
1513 for t in self._parts:
1514 cur = t
1514 cur = t
1515 if postfix:
1515 if postfix:
1516 cur += "_" + postfix
1516 cur += "_" + postfix
1517 if mode and cur in self.t:
1517 if mode and cur in self.t:
1518 self._parts[t] = cur
1518 self._parts[t] = cur
1519
1519
1520 if self._parts['docheader']:
1520 if self._parts['docheader']:
1521 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1521 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1522
1522
1523 def close(self):
1523 def close(self):
1524 if self._parts['docfooter']:
1524 if self._parts['docfooter']:
1525 if not self.footer:
1525 if not self.footer:
1526 self.footer = ""
1526 self.footer = ""
1527 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1527 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1528 return super(changeset_templater, self).close()
1528 return super(changeset_templater, self).close()
1529
1529
1530 def _show(self, ctx, copies, matchfn, props):
1530 def _show(self, ctx, copies, matchfn, props):
1531 '''show a single changeset or file revision'''
1531 '''show a single changeset or file revision'''
1532 props = props.copy()
1532 props = props.copy()
1533 props.update(templatekw.keywords)
1533 props.update(templatekw.keywords)
1534 props['templ'] = self.t
1534 props['templ'] = self.t
1535 props['ctx'] = ctx
1535 props['ctx'] = ctx
1536 props['repo'] = self.repo
1536 props['repo'] = self.repo
1537 props['ui'] = self.repo.ui
1537 props['ui'] = self.repo.ui
1538 props['revcache'] = {'copies': copies}
1538 props['revcache'] = {'copies': copies}
1539 props['cache'] = self.cache
1539 props['cache'] = self.cache
1540
1540
1541 # write header
1541 # write header
1542 if self._parts['header']:
1542 if self._parts['header']:
1543 h = templater.stringify(self.t(self._parts['header'], **props))
1543 h = templater.stringify(self.t(self._parts['header'], **props))
1544 if self.buffered:
1544 if self.buffered:
1545 self.header[ctx.rev()] = h
1545 self.header[ctx.rev()] = h
1546 else:
1546 else:
1547 if self.lastheader != h:
1547 if self.lastheader != h:
1548 self.lastheader = h
1548 self.lastheader = h
1549 self.ui.write(h)
1549 self.ui.write(h)
1550
1550
1551 # write changeset metadata, then patch if requested
1551 # write changeset metadata, then patch if requested
1552 key = self._parts['changeset']
1552 key = self._parts['changeset']
1553 self.ui.write(templater.stringify(self.t(key, **props)))
1553 self.ui.write(templater.stringify(self.t(key, **props)))
1554 self.showpatch(ctx, matchfn)
1554 self.showpatch(ctx, matchfn)
1555
1555
1556 if self._parts['footer']:
1556 if self._parts['footer']:
1557 if not self.footer:
1557 if not self.footer:
1558 self.footer = templater.stringify(
1558 self.footer = templater.stringify(
1559 self.t(self._parts['footer'], **props))
1559 self.t(self._parts['footer'], **props))
1560
1560
1561 def gettemplate(ui, tmpl, style):
1561 def gettemplate(ui, tmpl, style):
1562 """
1562 """
1563 Find the template matching the given template spec or style.
1563 Find the template matching the given template spec or style.
1564 """
1564 """
1565
1565
1566 # ui settings
1566 # ui settings
1567 if not tmpl and not style: # template are stronger than style
1567 if not tmpl and not style: # template are stronger than style
1568 tmpl = ui.config('ui', 'logtemplate')
1568 tmpl = ui.config('ui', 'logtemplate')
1569 if tmpl:
1569 if tmpl:
1570 return templater.unquotestring(tmpl), None
1570 return templater.unquotestring(tmpl), None
1571 else:
1571 else:
1572 style = util.expandpath(ui.config('ui', 'style', ''))
1572 style = util.expandpath(ui.config('ui', 'style', ''))
1573
1573
1574 if not tmpl and style:
1574 if not tmpl and style:
1575 mapfile = style
1575 mapfile = style
1576 if not os.path.split(mapfile)[0]:
1576 if not os.path.split(mapfile)[0]:
1577 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1577 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1578 or templater.templatepath(mapfile))
1578 or templater.templatepath(mapfile))
1579 if mapname:
1579 if mapname:
1580 mapfile = mapname
1580 mapfile = mapname
1581 return None, mapfile
1581 return None, mapfile
1582
1582
1583 if not tmpl:
1583 if not tmpl:
1584 return None, None
1584 return None, None
1585
1585
1586 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1586 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1587
1587
1588 def show_changeset(ui, repo, opts, buffered=False):
1588 def show_changeset(ui, repo, opts, buffered=False):
1589 """show one changeset using template or regular display.
1589 """show one changeset using template or regular display.
1590
1590
1591 Display format will be the first non-empty hit of:
1591 Display format will be the first non-empty hit of:
1592 1. option 'template'
1592 1. option 'template'
1593 2. option 'style'
1593 2. option 'style'
1594 3. [ui] setting 'logtemplate'
1594 3. [ui] setting 'logtemplate'
1595 4. [ui] setting 'style'
1595 4. [ui] setting 'style'
1596 If all of these values are either the unset or the empty string,
1596 If all of these values are either the unset or the empty string,
1597 regular display via changeset_printer() is done.
1597 regular display via changeset_printer() is done.
1598 """
1598 """
1599 # options
1599 # options
1600 matchfn = None
1600 matchfn = None
1601 if opts.get('patch') or opts.get('stat'):
1601 if opts.get('patch') or opts.get('stat'):
1602 matchfn = scmutil.matchall(repo)
1602 matchfn = scmutil.matchall(repo)
1603
1603
1604 if opts.get('template') == 'json':
1604 if opts.get('template') == 'json':
1605 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1605 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1606
1606
1607 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1607 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1608
1608
1609 if not tmpl and not mapfile:
1609 if not tmpl and not mapfile:
1610 return changeset_printer(ui, repo, matchfn, opts, buffered)
1610 return changeset_printer(ui, repo, matchfn, opts, buffered)
1611
1611
1612 return changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile, buffered)
1612 return changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile, buffered)
1613
1613
1614 def showmarker(ui, marker, index=None):
1614 def showmarker(fm, marker, index=None):
1615 """utility function to display obsolescence marker in a readable way
1615 """utility function to display obsolescence marker in a readable way
1616
1616
1617 To be used by debug function."""
1617 To be used by debug function."""
1618 if index is not None:
1618 if index is not None:
1619 ui.write("%i " % index)
1619 fm.write('index', '%i ', index)
1620 ui.write(hex(marker.precnode()))
1620 fm.write('precnode', '%s ', hex(marker.precnode()))
1621 for repl in marker.succnodes():
1621 succs = marker.succnodes()
1622 ui.write(' ')
1622 fm.condwrite(succs, 'succnodes', '%s ',
1623 ui.write(hex(repl))
1623 fm.formatlist(map(hex, succs), name='node'))
1624 ui.write(' %X ' % marker.flags())
1624 fm.write('flag', '%X ', marker.flags())
1625 parents = marker.parentnodes()
1625 parents = marker.parentnodes()
1626 if parents is not None:
1626 if parents is not None:
1627 ui.write('{%s} ' % ', '.join(hex(p) for p in parents))
1627 fm.write('parentnodes', '{%s} ',
1628 ui.write('(%s) ' % util.datestr(marker.date()))
1628 fm.formatlist(map(hex, parents), name='node', sep=', '))
1629 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1629 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
1630 sorted(marker.metadata().items())
1630 meta = marker.metadata().copy()
1631 if t[0] != 'date')))
1631 meta.pop('date', None)
1632 ui.write('\n')
1632 fm.write('metadata', '{%s}', fm.formatdict(meta, fmt='%r: %r', sep=', '))
1633 fm.plain('\n')
1633
1634
1634 def finddate(ui, repo, date):
1635 def finddate(ui, repo, date):
1635 """Find the tipmost changeset that matches the given date spec"""
1636 """Find the tipmost changeset that matches the given date spec"""
1636
1637
1637 df = util.matchdate(date)
1638 df = util.matchdate(date)
1638 m = scmutil.matchall(repo)
1639 m = scmutil.matchall(repo)
1639 results = {}
1640 results = {}
1640
1641
1641 def prep(ctx, fns):
1642 def prep(ctx, fns):
1642 d = ctx.date()
1643 d = ctx.date()
1643 if df(d[0]):
1644 if df(d[0]):
1644 results[ctx.rev()] = d
1645 results[ctx.rev()] = d
1645
1646
1646 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1647 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1647 rev = ctx.rev()
1648 rev = ctx.rev()
1648 if rev in results:
1649 if rev in results:
1649 ui.status(_("found revision %s from %s\n") %
1650 ui.status(_("found revision %s from %s\n") %
1650 (rev, util.datestr(results[rev])))
1651 (rev, util.datestr(results[rev])))
1651 return str(rev)
1652 return str(rev)
1652
1653
1653 raise error.Abort(_("revision matching date not found"))
1654 raise error.Abort(_("revision matching date not found"))
1654
1655
1655 def increasingwindows(windowsize=8, sizelimit=512):
1656 def increasingwindows(windowsize=8, sizelimit=512):
1656 while True:
1657 while True:
1657 yield windowsize
1658 yield windowsize
1658 if windowsize < sizelimit:
1659 if windowsize < sizelimit:
1659 windowsize *= 2
1660 windowsize *= 2
1660
1661
1661 class FileWalkError(Exception):
1662 class FileWalkError(Exception):
1662 pass
1663 pass
1663
1664
1664 def walkfilerevs(repo, match, follow, revs, fncache):
1665 def walkfilerevs(repo, match, follow, revs, fncache):
1665 '''Walks the file history for the matched files.
1666 '''Walks the file history for the matched files.
1666
1667
1667 Returns the changeset revs that are involved in the file history.
1668 Returns the changeset revs that are involved in the file history.
1668
1669
1669 Throws FileWalkError if the file history can't be walked using
1670 Throws FileWalkError if the file history can't be walked using
1670 filelogs alone.
1671 filelogs alone.
1671 '''
1672 '''
1672 wanted = set()
1673 wanted = set()
1673 copies = []
1674 copies = []
1674 minrev, maxrev = min(revs), max(revs)
1675 minrev, maxrev = min(revs), max(revs)
1675 def filerevgen(filelog, last):
1676 def filerevgen(filelog, last):
1676 """
1677 """
1677 Only files, no patterns. Check the history of each file.
1678 Only files, no patterns. Check the history of each file.
1678
1679
1679 Examines filelog entries within minrev, maxrev linkrev range
1680 Examines filelog entries within minrev, maxrev linkrev range
1680 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1681 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1681 tuples in backwards order
1682 tuples in backwards order
1682 """
1683 """
1683 cl_count = len(repo)
1684 cl_count = len(repo)
1684 revs = []
1685 revs = []
1685 for j in xrange(0, last + 1):
1686 for j in xrange(0, last + 1):
1686 linkrev = filelog.linkrev(j)
1687 linkrev = filelog.linkrev(j)
1687 if linkrev < minrev:
1688 if linkrev < minrev:
1688 continue
1689 continue
1689 # only yield rev for which we have the changelog, it can
1690 # only yield rev for which we have the changelog, it can
1690 # happen while doing "hg log" during a pull or commit
1691 # happen while doing "hg log" during a pull or commit
1691 if linkrev >= cl_count:
1692 if linkrev >= cl_count:
1692 break
1693 break
1693
1694
1694 parentlinkrevs = []
1695 parentlinkrevs = []
1695 for p in filelog.parentrevs(j):
1696 for p in filelog.parentrevs(j):
1696 if p != nullrev:
1697 if p != nullrev:
1697 parentlinkrevs.append(filelog.linkrev(p))
1698 parentlinkrevs.append(filelog.linkrev(p))
1698 n = filelog.node(j)
1699 n = filelog.node(j)
1699 revs.append((linkrev, parentlinkrevs,
1700 revs.append((linkrev, parentlinkrevs,
1700 follow and filelog.renamed(n)))
1701 follow and filelog.renamed(n)))
1701
1702
1702 return reversed(revs)
1703 return reversed(revs)
1703 def iterfiles():
1704 def iterfiles():
1704 pctx = repo['.']
1705 pctx = repo['.']
1705 for filename in match.files():
1706 for filename in match.files():
1706 if follow:
1707 if follow:
1707 if filename not in pctx:
1708 if filename not in pctx:
1708 raise error.Abort(_('cannot follow file not in parent '
1709 raise error.Abort(_('cannot follow file not in parent '
1709 'revision: "%s"') % filename)
1710 'revision: "%s"') % filename)
1710 yield filename, pctx[filename].filenode()
1711 yield filename, pctx[filename].filenode()
1711 else:
1712 else:
1712 yield filename, None
1713 yield filename, None
1713 for filename_node in copies:
1714 for filename_node in copies:
1714 yield filename_node
1715 yield filename_node
1715
1716
1716 for file_, node in iterfiles():
1717 for file_, node in iterfiles():
1717 filelog = repo.file(file_)
1718 filelog = repo.file(file_)
1718 if not len(filelog):
1719 if not len(filelog):
1719 if node is None:
1720 if node is None:
1720 # A zero count may be a directory or deleted file, so
1721 # A zero count may be a directory or deleted file, so
1721 # try to find matching entries on the slow path.
1722 # try to find matching entries on the slow path.
1722 if follow:
1723 if follow:
1723 raise error.Abort(
1724 raise error.Abort(
1724 _('cannot follow nonexistent file: "%s"') % file_)
1725 _('cannot follow nonexistent file: "%s"') % file_)
1725 raise FileWalkError("Cannot walk via filelog")
1726 raise FileWalkError("Cannot walk via filelog")
1726 else:
1727 else:
1727 continue
1728 continue
1728
1729
1729 if node is None:
1730 if node is None:
1730 last = len(filelog) - 1
1731 last = len(filelog) - 1
1731 else:
1732 else:
1732 last = filelog.rev(node)
1733 last = filelog.rev(node)
1733
1734
1734 # keep track of all ancestors of the file
1735 # keep track of all ancestors of the file
1735 ancestors = set([filelog.linkrev(last)])
1736 ancestors = set([filelog.linkrev(last)])
1736
1737
1737 # iterate from latest to oldest revision
1738 # iterate from latest to oldest revision
1738 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1739 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1739 if not follow:
1740 if not follow:
1740 if rev > maxrev:
1741 if rev > maxrev:
1741 continue
1742 continue
1742 else:
1743 else:
1743 # Note that last might not be the first interesting
1744 # Note that last might not be the first interesting
1744 # rev to us:
1745 # rev to us:
1745 # if the file has been changed after maxrev, we'll
1746 # if the file has been changed after maxrev, we'll
1746 # have linkrev(last) > maxrev, and we still need
1747 # have linkrev(last) > maxrev, and we still need
1747 # to explore the file graph
1748 # to explore the file graph
1748 if rev not in ancestors:
1749 if rev not in ancestors:
1749 continue
1750 continue
1750 # XXX insert 1327 fix here
1751 # XXX insert 1327 fix here
1751 if flparentlinkrevs:
1752 if flparentlinkrevs:
1752 ancestors.update(flparentlinkrevs)
1753 ancestors.update(flparentlinkrevs)
1753
1754
1754 fncache.setdefault(rev, []).append(file_)
1755 fncache.setdefault(rev, []).append(file_)
1755 wanted.add(rev)
1756 wanted.add(rev)
1756 if copied:
1757 if copied:
1757 copies.append(copied)
1758 copies.append(copied)
1758
1759
1759 return wanted
1760 return wanted
1760
1761
1761 class _followfilter(object):
1762 class _followfilter(object):
1762 def __init__(self, repo, onlyfirst=False):
1763 def __init__(self, repo, onlyfirst=False):
1763 self.repo = repo
1764 self.repo = repo
1764 self.startrev = nullrev
1765 self.startrev = nullrev
1765 self.roots = set()
1766 self.roots = set()
1766 self.onlyfirst = onlyfirst
1767 self.onlyfirst = onlyfirst
1767
1768
1768 def match(self, rev):
1769 def match(self, rev):
1769 def realparents(rev):
1770 def realparents(rev):
1770 if self.onlyfirst:
1771 if self.onlyfirst:
1771 return self.repo.changelog.parentrevs(rev)[0:1]
1772 return self.repo.changelog.parentrevs(rev)[0:1]
1772 else:
1773 else:
1773 return filter(lambda x: x != nullrev,
1774 return filter(lambda x: x != nullrev,
1774 self.repo.changelog.parentrevs(rev))
1775 self.repo.changelog.parentrevs(rev))
1775
1776
1776 if self.startrev == nullrev:
1777 if self.startrev == nullrev:
1777 self.startrev = rev
1778 self.startrev = rev
1778 return True
1779 return True
1779
1780
1780 if rev > self.startrev:
1781 if rev > self.startrev:
1781 # forward: all descendants
1782 # forward: all descendants
1782 if not self.roots:
1783 if not self.roots:
1783 self.roots.add(self.startrev)
1784 self.roots.add(self.startrev)
1784 for parent in realparents(rev):
1785 for parent in realparents(rev):
1785 if parent in self.roots:
1786 if parent in self.roots:
1786 self.roots.add(rev)
1787 self.roots.add(rev)
1787 return True
1788 return True
1788 else:
1789 else:
1789 # backwards: all parents
1790 # backwards: all parents
1790 if not self.roots:
1791 if not self.roots:
1791 self.roots.update(realparents(self.startrev))
1792 self.roots.update(realparents(self.startrev))
1792 if rev in self.roots:
1793 if rev in self.roots:
1793 self.roots.remove(rev)
1794 self.roots.remove(rev)
1794 self.roots.update(realparents(rev))
1795 self.roots.update(realparents(rev))
1795 return True
1796 return True
1796
1797
1797 return False
1798 return False
1798
1799
1799 def walkchangerevs(repo, match, opts, prepare):
1800 def walkchangerevs(repo, match, opts, prepare):
1800 '''Iterate over files and the revs in which they changed.
1801 '''Iterate over files and the revs in which they changed.
1801
1802
1802 Callers most commonly need to iterate backwards over the history
1803 Callers most commonly need to iterate backwards over the history
1803 in which they are interested. Doing so has awful (quadratic-looking)
1804 in which they are interested. Doing so has awful (quadratic-looking)
1804 performance, so we use iterators in a "windowed" way.
1805 performance, so we use iterators in a "windowed" way.
1805
1806
1806 We walk a window of revisions in the desired order. Within the
1807 We walk a window of revisions in the desired order. Within the
1807 window, we first walk forwards to gather data, then in the desired
1808 window, we first walk forwards to gather data, then in the desired
1808 order (usually backwards) to display it.
1809 order (usually backwards) to display it.
1809
1810
1810 This function returns an iterator yielding contexts. Before
1811 This function returns an iterator yielding contexts. Before
1811 yielding each context, the iterator will first call the prepare
1812 yielding each context, the iterator will first call the prepare
1812 function on each context in the window in forward order.'''
1813 function on each context in the window in forward order.'''
1813
1814
1814 follow = opts.get('follow') or opts.get('follow_first')
1815 follow = opts.get('follow') or opts.get('follow_first')
1815 revs = _logrevs(repo, opts)
1816 revs = _logrevs(repo, opts)
1816 if not revs:
1817 if not revs:
1817 return []
1818 return []
1818 wanted = set()
1819 wanted = set()
1819 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1820 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1820 opts.get('removed'))
1821 opts.get('removed'))
1821 fncache = {}
1822 fncache = {}
1822 change = repo.changectx
1823 change = repo.changectx
1823
1824
1824 # First step is to fill wanted, the set of revisions that we want to yield.
1825 # First step is to fill wanted, the set of revisions that we want to yield.
1825 # When it does not induce extra cost, we also fill fncache for revisions in
1826 # When it does not induce extra cost, we also fill fncache for revisions in
1826 # wanted: a cache of filenames that were changed (ctx.files()) and that
1827 # wanted: a cache of filenames that were changed (ctx.files()) and that
1827 # match the file filtering conditions.
1828 # match the file filtering conditions.
1828
1829
1829 if match.always():
1830 if match.always():
1830 # No files, no patterns. Display all revs.
1831 # No files, no patterns. Display all revs.
1831 wanted = revs
1832 wanted = revs
1832 elif not slowpath:
1833 elif not slowpath:
1833 # We only have to read through the filelog to find wanted revisions
1834 # We only have to read through the filelog to find wanted revisions
1834
1835
1835 try:
1836 try:
1836 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1837 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1837 except FileWalkError:
1838 except FileWalkError:
1838 slowpath = True
1839 slowpath = True
1839
1840
1840 # We decided to fall back to the slowpath because at least one
1841 # We decided to fall back to the slowpath because at least one
1841 # of the paths was not a file. Check to see if at least one of them
1842 # of the paths was not a file. Check to see if at least one of them
1842 # existed in history, otherwise simply return
1843 # existed in history, otherwise simply return
1843 for path in match.files():
1844 for path in match.files():
1844 if path == '.' or path in repo.store:
1845 if path == '.' or path in repo.store:
1845 break
1846 break
1846 else:
1847 else:
1847 return []
1848 return []
1848
1849
1849 if slowpath:
1850 if slowpath:
1850 # We have to read the changelog to match filenames against
1851 # We have to read the changelog to match filenames against
1851 # changed files
1852 # changed files
1852
1853
1853 if follow:
1854 if follow:
1854 raise error.Abort(_('can only follow copies/renames for explicit '
1855 raise error.Abort(_('can only follow copies/renames for explicit '
1855 'filenames'))
1856 'filenames'))
1856
1857
1857 # The slow path checks files modified in every changeset.
1858 # The slow path checks files modified in every changeset.
1858 # This is really slow on large repos, so compute the set lazily.
1859 # This is really slow on large repos, so compute the set lazily.
1859 class lazywantedset(object):
1860 class lazywantedset(object):
1860 def __init__(self):
1861 def __init__(self):
1861 self.set = set()
1862 self.set = set()
1862 self.revs = set(revs)
1863 self.revs = set(revs)
1863
1864
1864 # No need to worry about locality here because it will be accessed
1865 # No need to worry about locality here because it will be accessed
1865 # in the same order as the increasing window below.
1866 # in the same order as the increasing window below.
1866 def __contains__(self, value):
1867 def __contains__(self, value):
1867 if value in self.set:
1868 if value in self.set:
1868 return True
1869 return True
1869 elif not value in self.revs:
1870 elif not value in self.revs:
1870 return False
1871 return False
1871 else:
1872 else:
1872 self.revs.discard(value)
1873 self.revs.discard(value)
1873 ctx = change(value)
1874 ctx = change(value)
1874 matches = filter(match, ctx.files())
1875 matches = filter(match, ctx.files())
1875 if matches:
1876 if matches:
1876 fncache[value] = matches
1877 fncache[value] = matches
1877 self.set.add(value)
1878 self.set.add(value)
1878 return True
1879 return True
1879 return False
1880 return False
1880
1881
1881 def discard(self, value):
1882 def discard(self, value):
1882 self.revs.discard(value)
1883 self.revs.discard(value)
1883 self.set.discard(value)
1884 self.set.discard(value)
1884
1885
1885 wanted = lazywantedset()
1886 wanted = lazywantedset()
1886
1887
1887 # it might be worthwhile to do this in the iterator if the rev range
1888 # it might be worthwhile to do this in the iterator if the rev range
1888 # is descending and the prune args are all within that range
1889 # is descending and the prune args are all within that range
1889 for rev in opts.get('prune', ()):
1890 for rev in opts.get('prune', ()):
1890 rev = repo[rev].rev()
1891 rev = repo[rev].rev()
1891 ff = _followfilter(repo)
1892 ff = _followfilter(repo)
1892 stop = min(revs[0], revs[-1])
1893 stop = min(revs[0], revs[-1])
1893 for x in xrange(rev, stop - 1, -1):
1894 for x in xrange(rev, stop - 1, -1):
1894 if ff.match(x):
1895 if ff.match(x):
1895 wanted = wanted - [x]
1896 wanted = wanted - [x]
1896
1897
1897 # Now that wanted is correctly initialized, we can iterate over the
1898 # Now that wanted is correctly initialized, we can iterate over the
1898 # revision range, yielding only revisions in wanted.
1899 # revision range, yielding only revisions in wanted.
1899 def iterate():
1900 def iterate():
1900 if follow and match.always():
1901 if follow and match.always():
1901 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1902 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1902 def want(rev):
1903 def want(rev):
1903 return ff.match(rev) and rev in wanted
1904 return ff.match(rev) and rev in wanted
1904 else:
1905 else:
1905 def want(rev):
1906 def want(rev):
1906 return rev in wanted
1907 return rev in wanted
1907
1908
1908 it = iter(revs)
1909 it = iter(revs)
1909 stopiteration = False
1910 stopiteration = False
1910 for windowsize in increasingwindows():
1911 for windowsize in increasingwindows():
1911 nrevs = []
1912 nrevs = []
1912 for i in xrange(windowsize):
1913 for i in xrange(windowsize):
1913 rev = next(it, None)
1914 rev = next(it, None)
1914 if rev is None:
1915 if rev is None:
1915 stopiteration = True
1916 stopiteration = True
1916 break
1917 break
1917 elif want(rev):
1918 elif want(rev):
1918 nrevs.append(rev)
1919 nrevs.append(rev)
1919 for rev in sorted(nrevs):
1920 for rev in sorted(nrevs):
1920 fns = fncache.get(rev)
1921 fns = fncache.get(rev)
1921 ctx = change(rev)
1922 ctx = change(rev)
1922 if not fns:
1923 if not fns:
1923 def fns_generator():
1924 def fns_generator():
1924 for f in ctx.files():
1925 for f in ctx.files():
1925 if match(f):
1926 if match(f):
1926 yield f
1927 yield f
1927 fns = fns_generator()
1928 fns = fns_generator()
1928 prepare(ctx, fns)
1929 prepare(ctx, fns)
1929 for rev in nrevs:
1930 for rev in nrevs:
1930 yield change(rev)
1931 yield change(rev)
1931
1932
1932 if stopiteration:
1933 if stopiteration:
1933 break
1934 break
1934
1935
1935 return iterate()
1936 return iterate()
1936
1937
1937 def _makefollowlogfilematcher(repo, files, followfirst):
1938 def _makefollowlogfilematcher(repo, files, followfirst):
1938 # When displaying a revision with --patch --follow FILE, we have
1939 # When displaying a revision with --patch --follow FILE, we have
1939 # to know which file of the revision must be diffed. With
1940 # to know which file of the revision must be diffed. With
1940 # --follow, we want the names of the ancestors of FILE in the
1941 # --follow, we want the names of the ancestors of FILE in the
1941 # revision, stored in "fcache". "fcache" is populated by
1942 # revision, stored in "fcache". "fcache" is populated by
1942 # reproducing the graph traversal already done by --follow revset
1943 # reproducing the graph traversal already done by --follow revset
1943 # and relating linkrevs to file names (which is not "correct" but
1944 # and relating linkrevs to file names (which is not "correct" but
1944 # good enough).
1945 # good enough).
1945 fcache = {}
1946 fcache = {}
1946 fcacheready = [False]
1947 fcacheready = [False]
1947 pctx = repo['.']
1948 pctx = repo['.']
1948
1949
1949 def populate():
1950 def populate():
1950 for fn in files:
1951 for fn in files:
1951 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1952 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1952 for c in i:
1953 for c in i:
1953 fcache.setdefault(c.linkrev(), set()).add(c.path())
1954 fcache.setdefault(c.linkrev(), set()).add(c.path())
1954
1955
1955 def filematcher(rev):
1956 def filematcher(rev):
1956 if not fcacheready[0]:
1957 if not fcacheready[0]:
1957 # Lazy initialization
1958 # Lazy initialization
1958 fcacheready[0] = True
1959 fcacheready[0] = True
1959 populate()
1960 populate()
1960 return scmutil.matchfiles(repo, fcache.get(rev, []))
1961 return scmutil.matchfiles(repo, fcache.get(rev, []))
1961
1962
1962 return filematcher
1963 return filematcher
1963
1964
1964 def _makenofollowlogfilematcher(repo, pats, opts):
1965 def _makenofollowlogfilematcher(repo, pats, opts):
1965 '''hook for extensions to override the filematcher for non-follow cases'''
1966 '''hook for extensions to override the filematcher for non-follow cases'''
1966 return None
1967 return None
1967
1968
1968 def _makelogrevset(repo, pats, opts, revs):
1969 def _makelogrevset(repo, pats, opts, revs):
1969 """Return (expr, filematcher) where expr is a revset string built
1970 """Return (expr, filematcher) where expr is a revset string built
1970 from log options and file patterns or None. If --stat or --patch
1971 from log options and file patterns or None. If --stat or --patch
1971 are not passed filematcher is None. Otherwise it is a callable
1972 are not passed filematcher is None. Otherwise it is a callable
1972 taking a revision number and returning a match objects filtering
1973 taking a revision number and returning a match objects filtering
1973 the files to be detailed when displaying the revision.
1974 the files to be detailed when displaying the revision.
1974 """
1975 """
1975 opt2revset = {
1976 opt2revset = {
1976 'no_merges': ('not merge()', None),
1977 'no_merges': ('not merge()', None),
1977 'only_merges': ('merge()', None),
1978 'only_merges': ('merge()', None),
1978 '_ancestors': ('ancestors(%(val)s)', None),
1979 '_ancestors': ('ancestors(%(val)s)', None),
1979 '_fancestors': ('_firstancestors(%(val)s)', None),
1980 '_fancestors': ('_firstancestors(%(val)s)', None),
1980 '_descendants': ('descendants(%(val)s)', None),
1981 '_descendants': ('descendants(%(val)s)', None),
1981 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1982 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1982 '_matchfiles': ('_matchfiles(%(val)s)', None),
1983 '_matchfiles': ('_matchfiles(%(val)s)', None),
1983 'date': ('date(%(val)r)', None),
1984 'date': ('date(%(val)r)', None),
1984 'branch': ('branch(%(val)r)', ' or '),
1985 'branch': ('branch(%(val)r)', ' or '),
1985 '_patslog': ('filelog(%(val)r)', ' or '),
1986 '_patslog': ('filelog(%(val)r)', ' or '),
1986 '_patsfollow': ('follow(%(val)r)', ' or '),
1987 '_patsfollow': ('follow(%(val)r)', ' or '),
1987 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1988 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1988 'keyword': ('keyword(%(val)r)', ' or '),
1989 'keyword': ('keyword(%(val)r)', ' or '),
1989 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1990 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1990 'user': ('user(%(val)r)', ' or '),
1991 'user': ('user(%(val)r)', ' or '),
1991 }
1992 }
1992
1993
1993 opts = dict(opts)
1994 opts = dict(opts)
1994 # follow or not follow?
1995 # follow or not follow?
1995 follow = opts.get('follow') or opts.get('follow_first')
1996 follow = opts.get('follow') or opts.get('follow_first')
1996 if opts.get('follow_first'):
1997 if opts.get('follow_first'):
1997 followfirst = 1
1998 followfirst = 1
1998 else:
1999 else:
1999 followfirst = 0
2000 followfirst = 0
2000 # --follow with FILE behavior depends on revs...
2001 # --follow with FILE behavior depends on revs...
2001 it = iter(revs)
2002 it = iter(revs)
2002 startrev = next(it)
2003 startrev = next(it)
2003 followdescendants = startrev < next(it, startrev)
2004 followdescendants = startrev < next(it, startrev)
2004
2005
2005 # branch and only_branch are really aliases and must be handled at
2006 # branch and only_branch are really aliases and must be handled at
2006 # the same time
2007 # the same time
2007 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
2008 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
2008 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
2009 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
2009 # pats/include/exclude are passed to match.match() directly in
2010 # pats/include/exclude are passed to match.match() directly in
2010 # _matchfiles() revset but walkchangerevs() builds its matcher with
2011 # _matchfiles() revset but walkchangerevs() builds its matcher with
2011 # scmutil.match(). The difference is input pats are globbed on
2012 # scmutil.match(). The difference is input pats are globbed on
2012 # platforms without shell expansion (windows).
2013 # platforms without shell expansion (windows).
2013 wctx = repo[None]
2014 wctx = repo[None]
2014 match, pats = scmutil.matchandpats(wctx, pats, opts)
2015 match, pats = scmutil.matchandpats(wctx, pats, opts)
2015 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
2016 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
2016 opts.get('removed'))
2017 opts.get('removed'))
2017 if not slowpath:
2018 if not slowpath:
2018 for f in match.files():
2019 for f in match.files():
2019 if follow and f not in wctx:
2020 if follow and f not in wctx:
2020 # If the file exists, it may be a directory, so let it
2021 # If the file exists, it may be a directory, so let it
2021 # take the slow path.
2022 # take the slow path.
2022 if os.path.exists(repo.wjoin(f)):
2023 if os.path.exists(repo.wjoin(f)):
2023 slowpath = True
2024 slowpath = True
2024 continue
2025 continue
2025 else:
2026 else:
2026 raise error.Abort(_('cannot follow file not in parent '
2027 raise error.Abort(_('cannot follow file not in parent '
2027 'revision: "%s"') % f)
2028 'revision: "%s"') % f)
2028 filelog = repo.file(f)
2029 filelog = repo.file(f)
2029 if not filelog:
2030 if not filelog:
2030 # A zero count may be a directory or deleted file, so
2031 # A zero count may be a directory or deleted file, so
2031 # try to find matching entries on the slow path.
2032 # try to find matching entries on the slow path.
2032 if follow:
2033 if follow:
2033 raise error.Abort(
2034 raise error.Abort(
2034 _('cannot follow nonexistent file: "%s"') % f)
2035 _('cannot follow nonexistent file: "%s"') % f)
2035 slowpath = True
2036 slowpath = True
2036
2037
2037 # We decided to fall back to the slowpath because at least one
2038 # We decided to fall back to the slowpath because at least one
2038 # of the paths was not a file. Check to see if at least one of them
2039 # of the paths was not a file. Check to see if at least one of them
2039 # existed in history - in that case, we'll continue down the
2040 # existed in history - in that case, we'll continue down the
2040 # slowpath; otherwise, we can turn off the slowpath
2041 # slowpath; otherwise, we can turn off the slowpath
2041 if slowpath:
2042 if slowpath:
2042 for path in match.files():
2043 for path in match.files():
2043 if path == '.' or path in repo.store:
2044 if path == '.' or path in repo.store:
2044 break
2045 break
2045 else:
2046 else:
2046 slowpath = False
2047 slowpath = False
2047
2048
2048 fpats = ('_patsfollow', '_patsfollowfirst')
2049 fpats = ('_patsfollow', '_patsfollowfirst')
2049 fnopats = (('_ancestors', '_fancestors'),
2050 fnopats = (('_ancestors', '_fancestors'),
2050 ('_descendants', '_fdescendants'))
2051 ('_descendants', '_fdescendants'))
2051 if slowpath:
2052 if slowpath:
2052 # See walkchangerevs() slow path.
2053 # See walkchangerevs() slow path.
2053 #
2054 #
2054 # pats/include/exclude cannot be represented as separate
2055 # pats/include/exclude cannot be represented as separate
2055 # revset expressions as their filtering logic applies at file
2056 # revset expressions as their filtering logic applies at file
2056 # level. For instance "-I a -X a" matches a revision touching
2057 # level. For instance "-I a -X a" matches a revision touching
2057 # "a" and "b" while "file(a) and not file(b)" does
2058 # "a" and "b" while "file(a) and not file(b)" does
2058 # not. Besides, filesets are evaluated against the working
2059 # not. Besides, filesets are evaluated against the working
2059 # directory.
2060 # directory.
2060 matchargs = ['r:', 'd:relpath']
2061 matchargs = ['r:', 'd:relpath']
2061 for p in pats:
2062 for p in pats:
2062 matchargs.append('p:' + p)
2063 matchargs.append('p:' + p)
2063 for p in opts.get('include', []):
2064 for p in opts.get('include', []):
2064 matchargs.append('i:' + p)
2065 matchargs.append('i:' + p)
2065 for p in opts.get('exclude', []):
2066 for p in opts.get('exclude', []):
2066 matchargs.append('x:' + p)
2067 matchargs.append('x:' + p)
2067 matchargs = ','.join(('%r' % p) for p in matchargs)
2068 matchargs = ','.join(('%r' % p) for p in matchargs)
2068 opts['_matchfiles'] = matchargs
2069 opts['_matchfiles'] = matchargs
2069 if follow:
2070 if follow:
2070 opts[fnopats[0][followfirst]] = '.'
2071 opts[fnopats[0][followfirst]] = '.'
2071 else:
2072 else:
2072 if follow:
2073 if follow:
2073 if pats:
2074 if pats:
2074 # follow() revset interprets its file argument as a
2075 # follow() revset interprets its file argument as a
2075 # manifest entry, so use match.files(), not pats.
2076 # manifest entry, so use match.files(), not pats.
2076 opts[fpats[followfirst]] = list(match.files())
2077 opts[fpats[followfirst]] = list(match.files())
2077 else:
2078 else:
2078 op = fnopats[followdescendants][followfirst]
2079 op = fnopats[followdescendants][followfirst]
2079 opts[op] = 'rev(%d)' % startrev
2080 opts[op] = 'rev(%d)' % startrev
2080 else:
2081 else:
2081 opts['_patslog'] = list(pats)
2082 opts['_patslog'] = list(pats)
2082
2083
2083 filematcher = None
2084 filematcher = None
2084 if opts.get('patch') or opts.get('stat'):
2085 if opts.get('patch') or opts.get('stat'):
2085 # When following files, track renames via a special matcher.
2086 # When following files, track renames via a special matcher.
2086 # If we're forced to take the slowpath it means we're following
2087 # If we're forced to take the slowpath it means we're following
2087 # at least one pattern/directory, so don't bother with rename tracking.
2088 # at least one pattern/directory, so don't bother with rename tracking.
2088 if follow and not match.always() and not slowpath:
2089 if follow and not match.always() and not slowpath:
2089 # _makefollowlogfilematcher expects its files argument to be
2090 # _makefollowlogfilematcher expects its files argument to be
2090 # relative to the repo root, so use match.files(), not pats.
2091 # relative to the repo root, so use match.files(), not pats.
2091 filematcher = _makefollowlogfilematcher(repo, match.files(),
2092 filematcher = _makefollowlogfilematcher(repo, match.files(),
2092 followfirst)
2093 followfirst)
2093 else:
2094 else:
2094 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2095 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2095 if filematcher is None:
2096 if filematcher is None:
2096 filematcher = lambda rev: match
2097 filematcher = lambda rev: match
2097
2098
2098 expr = []
2099 expr = []
2099 for op, val in sorted(opts.iteritems()):
2100 for op, val in sorted(opts.iteritems()):
2100 if not val:
2101 if not val:
2101 continue
2102 continue
2102 if op not in opt2revset:
2103 if op not in opt2revset:
2103 continue
2104 continue
2104 revop, andor = opt2revset[op]
2105 revop, andor = opt2revset[op]
2105 if '%(val)' not in revop:
2106 if '%(val)' not in revop:
2106 expr.append(revop)
2107 expr.append(revop)
2107 else:
2108 else:
2108 if not isinstance(val, list):
2109 if not isinstance(val, list):
2109 e = revop % {'val': val}
2110 e = revop % {'val': val}
2110 else:
2111 else:
2111 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2112 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2112 expr.append(e)
2113 expr.append(e)
2113
2114
2114 if expr:
2115 if expr:
2115 expr = '(' + ' and '.join(expr) + ')'
2116 expr = '(' + ' and '.join(expr) + ')'
2116 else:
2117 else:
2117 expr = None
2118 expr = None
2118 return expr, filematcher
2119 return expr, filematcher
2119
2120
2120 def _logrevs(repo, opts):
2121 def _logrevs(repo, opts):
2121 # Default --rev value depends on --follow but --follow behavior
2122 # Default --rev value depends on --follow but --follow behavior
2122 # depends on revisions resolved from --rev...
2123 # depends on revisions resolved from --rev...
2123 follow = opts.get('follow') or opts.get('follow_first')
2124 follow = opts.get('follow') or opts.get('follow_first')
2124 if opts.get('rev'):
2125 if opts.get('rev'):
2125 revs = scmutil.revrange(repo, opts['rev'])
2126 revs = scmutil.revrange(repo, opts['rev'])
2126 elif follow and repo.dirstate.p1() == nullid:
2127 elif follow and repo.dirstate.p1() == nullid:
2127 revs = revset.baseset()
2128 revs = revset.baseset()
2128 elif follow:
2129 elif follow:
2129 revs = repo.revs('reverse(:.)')
2130 revs = repo.revs('reverse(:.)')
2130 else:
2131 else:
2131 revs = revset.spanset(repo)
2132 revs = revset.spanset(repo)
2132 revs.reverse()
2133 revs.reverse()
2133 return revs
2134 return revs
2134
2135
2135 def getgraphlogrevs(repo, pats, opts):
2136 def getgraphlogrevs(repo, pats, opts):
2136 """Return (revs, expr, filematcher) where revs is an iterable of
2137 """Return (revs, expr, filematcher) where revs is an iterable of
2137 revision numbers, expr is a revset string built from log options
2138 revision numbers, expr is a revset string built from log options
2138 and file patterns or None, and used to filter 'revs'. If --stat or
2139 and file patterns or None, and used to filter 'revs'. If --stat or
2139 --patch are not passed filematcher is None. Otherwise it is a
2140 --patch are not passed filematcher is None. Otherwise it is a
2140 callable taking a revision number and returning a match objects
2141 callable taking a revision number and returning a match objects
2141 filtering the files to be detailed when displaying the revision.
2142 filtering the files to be detailed when displaying the revision.
2142 """
2143 """
2143 limit = loglimit(opts)
2144 limit = loglimit(opts)
2144 revs = _logrevs(repo, opts)
2145 revs = _logrevs(repo, opts)
2145 if not revs:
2146 if not revs:
2146 return revset.baseset(), None, None
2147 return revset.baseset(), None, None
2147 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2148 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2148 if opts.get('rev'):
2149 if opts.get('rev'):
2149 # User-specified revs might be unsorted, but don't sort before
2150 # User-specified revs might be unsorted, but don't sort before
2150 # _makelogrevset because it might depend on the order of revs
2151 # _makelogrevset because it might depend on the order of revs
2151 if not (revs.isdescending() or revs.istopo()):
2152 if not (revs.isdescending() or revs.istopo()):
2152 revs.sort(reverse=True)
2153 revs.sort(reverse=True)
2153 if expr:
2154 if expr:
2154 # Revset matchers often operate faster on revisions in changelog
2155 # Revset matchers often operate faster on revisions in changelog
2155 # order, because most filters deal with the changelog.
2156 # order, because most filters deal with the changelog.
2156 revs.reverse()
2157 revs.reverse()
2157 matcher = revset.match(repo.ui, expr)
2158 matcher = revset.match(repo.ui, expr)
2158 # Revset matches can reorder revisions. "A or B" typically returns
2159 # Revset matches can reorder revisions. "A or B" typically returns
2159 # returns the revision matching A then the revision matching B. Sort
2160 # returns the revision matching A then the revision matching B. Sort
2160 # again to fix that.
2161 # again to fix that.
2161 revs = matcher(repo, revs)
2162 revs = matcher(repo, revs)
2162 revs.sort(reverse=True)
2163 revs.sort(reverse=True)
2163 if limit is not None:
2164 if limit is not None:
2164 limitedrevs = []
2165 limitedrevs = []
2165 for idx, rev in enumerate(revs):
2166 for idx, rev in enumerate(revs):
2166 if idx >= limit:
2167 if idx >= limit:
2167 break
2168 break
2168 limitedrevs.append(rev)
2169 limitedrevs.append(rev)
2169 revs = revset.baseset(limitedrevs)
2170 revs = revset.baseset(limitedrevs)
2170
2171
2171 return revs, expr, filematcher
2172 return revs, expr, filematcher
2172
2173
2173 def getlogrevs(repo, pats, opts):
2174 def getlogrevs(repo, pats, opts):
2174 """Return (revs, expr, filematcher) where revs is an iterable of
2175 """Return (revs, expr, filematcher) where revs is an iterable of
2175 revision numbers, expr is a revset string built from log options
2176 revision numbers, expr is a revset string built from log options
2176 and file patterns or None, and used to filter 'revs'. If --stat or
2177 and file patterns or None, and used to filter 'revs'. If --stat or
2177 --patch are not passed filematcher is None. Otherwise it is a
2178 --patch are not passed filematcher is None. Otherwise it is a
2178 callable taking a revision number and returning a match objects
2179 callable taking a revision number and returning a match objects
2179 filtering the files to be detailed when displaying the revision.
2180 filtering the files to be detailed when displaying the revision.
2180 """
2181 """
2181 limit = loglimit(opts)
2182 limit = loglimit(opts)
2182 revs = _logrevs(repo, opts)
2183 revs = _logrevs(repo, opts)
2183 if not revs:
2184 if not revs:
2184 return revset.baseset([]), None, None
2185 return revset.baseset([]), None, None
2185 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2186 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2186 if expr:
2187 if expr:
2187 # Revset matchers often operate faster on revisions in changelog
2188 # Revset matchers often operate faster on revisions in changelog
2188 # order, because most filters deal with the changelog.
2189 # order, because most filters deal with the changelog.
2189 if not opts.get('rev'):
2190 if not opts.get('rev'):
2190 revs.reverse()
2191 revs.reverse()
2191 matcher = revset.match(repo.ui, expr)
2192 matcher = revset.match(repo.ui, expr)
2192 # Revset matches can reorder revisions. "A or B" typically returns
2193 # Revset matches can reorder revisions. "A or B" typically returns
2193 # returns the revision matching A then the revision matching B. Sort
2194 # returns the revision matching A then the revision matching B. Sort
2194 # again to fix that.
2195 # again to fix that.
2195 fixopts = ['branch', 'only_branch', 'keyword', 'user']
2196 fixopts = ['branch', 'only_branch', 'keyword', 'user']
2196 oldrevs = revs
2197 oldrevs = revs
2197 revs = matcher(repo, revs)
2198 revs = matcher(repo, revs)
2198 if not opts.get('rev'):
2199 if not opts.get('rev'):
2199 revs.sort(reverse=True)
2200 revs.sort(reverse=True)
2200 elif len(pats) > 1 or any(len(opts.get(op, [])) > 1 for op in fixopts):
2201 elif len(pats) > 1 or any(len(opts.get(op, [])) > 1 for op in fixopts):
2201 # XXX "A or B" is known to change the order; fix it by filtering
2202 # XXX "A or B" is known to change the order; fix it by filtering
2202 # matched set again (issue5100)
2203 # matched set again (issue5100)
2203 revs = oldrevs & revs
2204 revs = oldrevs & revs
2204 if limit is not None:
2205 if limit is not None:
2205 limitedrevs = []
2206 limitedrevs = []
2206 for idx, r in enumerate(revs):
2207 for idx, r in enumerate(revs):
2207 if limit <= idx:
2208 if limit <= idx:
2208 break
2209 break
2209 limitedrevs.append(r)
2210 limitedrevs.append(r)
2210 revs = revset.baseset(limitedrevs)
2211 revs = revset.baseset(limitedrevs)
2211
2212
2212 return revs, expr, filematcher
2213 return revs, expr, filematcher
2213
2214
2214 def _graphnodeformatter(ui, displayer):
2215 def _graphnodeformatter(ui, displayer):
2215 spec = ui.config('ui', 'graphnodetemplate')
2216 spec = ui.config('ui', 'graphnodetemplate')
2216 if not spec:
2217 if not spec:
2217 return templatekw.showgraphnode # fast path for "{graphnode}"
2218 return templatekw.showgraphnode # fast path for "{graphnode}"
2218
2219
2219 templ = formatter.gettemplater(ui, 'graphnode', spec)
2220 templ = formatter.gettemplater(ui, 'graphnode', spec)
2220 cache = {}
2221 cache = {}
2221 if isinstance(displayer, changeset_templater):
2222 if isinstance(displayer, changeset_templater):
2222 cache = displayer.cache # reuse cache of slow templates
2223 cache = displayer.cache # reuse cache of slow templates
2223 props = templatekw.keywords.copy()
2224 props = templatekw.keywords.copy()
2224 props['templ'] = templ
2225 props['templ'] = templ
2225 props['cache'] = cache
2226 props['cache'] = cache
2226 def formatnode(repo, ctx):
2227 def formatnode(repo, ctx):
2227 props['ctx'] = ctx
2228 props['ctx'] = ctx
2228 props['repo'] = repo
2229 props['repo'] = repo
2229 props['ui'] = repo.ui
2230 props['ui'] = repo.ui
2230 props['revcache'] = {}
2231 props['revcache'] = {}
2231 return templater.stringify(templ('graphnode', **props))
2232 return templater.stringify(templ('graphnode', **props))
2232 return formatnode
2233 return formatnode
2233
2234
2234 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2235 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2235 filematcher=None):
2236 filematcher=None):
2236 formatnode = _graphnodeformatter(ui, displayer)
2237 formatnode = _graphnodeformatter(ui, displayer)
2237 state = graphmod.asciistate()
2238 state = graphmod.asciistate()
2238 styles = state['styles']
2239 styles = state['styles']
2239
2240
2240 # only set graph styling if HGPLAIN is not set.
2241 # only set graph styling if HGPLAIN is not set.
2241 if ui.plain('graph'):
2242 if ui.plain('graph'):
2242 # set all edge styles to |, the default pre-3.8 behaviour
2243 # set all edge styles to |, the default pre-3.8 behaviour
2243 styles.update(dict.fromkeys(styles, '|'))
2244 styles.update(dict.fromkeys(styles, '|'))
2244 else:
2245 else:
2245 edgetypes = {
2246 edgetypes = {
2246 'parent': graphmod.PARENT,
2247 'parent': graphmod.PARENT,
2247 'grandparent': graphmod.GRANDPARENT,
2248 'grandparent': graphmod.GRANDPARENT,
2248 'missing': graphmod.MISSINGPARENT
2249 'missing': graphmod.MISSINGPARENT
2249 }
2250 }
2250 for name, key in edgetypes.items():
2251 for name, key in edgetypes.items():
2251 # experimental config: experimental.graphstyle.*
2252 # experimental config: experimental.graphstyle.*
2252 styles[key] = ui.config('experimental', 'graphstyle.%s' % name,
2253 styles[key] = ui.config('experimental', 'graphstyle.%s' % name,
2253 styles[key])
2254 styles[key])
2254 if not styles[key]:
2255 if not styles[key]:
2255 styles[key] = None
2256 styles[key] = None
2256
2257
2257 # experimental config: experimental.graphshorten
2258 # experimental config: experimental.graphshorten
2258 state['graphshorten'] = ui.configbool('experimental', 'graphshorten')
2259 state['graphshorten'] = ui.configbool('experimental', 'graphshorten')
2259
2260
2260 for rev, type, ctx, parents in dag:
2261 for rev, type, ctx, parents in dag:
2261 char = formatnode(repo, ctx)
2262 char = formatnode(repo, ctx)
2262 copies = None
2263 copies = None
2263 if getrenamed and ctx.rev():
2264 if getrenamed and ctx.rev():
2264 copies = []
2265 copies = []
2265 for fn in ctx.files():
2266 for fn in ctx.files():
2266 rename = getrenamed(fn, ctx.rev())
2267 rename = getrenamed(fn, ctx.rev())
2267 if rename:
2268 if rename:
2268 copies.append((fn, rename[0]))
2269 copies.append((fn, rename[0]))
2269 revmatchfn = None
2270 revmatchfn = None
2270 if filematcher is not None:
2271 if filematcher is not None:
2271 revmatchfn = filematcher(ctx.rev())
2272 revmatchfn = filematcher(ctx.rev())
2272 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2273 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2273 lines = displayer.hunk.pop(rev).split('\n')
2274 lines = displayer.hunk.pop(rev).split('\n')
2274 if not lines[-1]:
2275 if not lines[-1]:
2275 del lines[-1]
2276 del lines[-1]
2276 displayer.flush(ctx)
2277 displayer.flush(ctx)
2277 edges = edgefn(type, char, lines, state, rev, parents)
2278 edges = edgefn(type, char, lines, state, rev, parents)
2278 for type, char, lines, coldata in edges:
2279 for type, char, lines, coldata in edges:
2279 graphmod.ascii(ui, state, type, char, lines, coldata)
2280 graphmod.ascii(ui, state, type, char, lines, coldata)
2280 displayer.close()
2281 displayer.close()
2281
2282
2282 def graphlog(ui, repo, *pats, **opts):
2283 def graphlog(ui, repo, *pats, **opts):
2283 # Parameters are identical to log command ones
2284 # Parameters are identical to log command ones
2284 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2285 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2285 revdag = graphmod.dagwalker(repo, revs)
2286 revdag = graphmod.dagwalker(repo, revs)
2286
2287
2287 getrenamed = None
2288 getrenamed = None
2288 if opts.get('copies'):
2289 if opts.get('copies'):
2289 endrev = None
2290 endrev = None
2290 if opts.get('rev'):
2291 if opts.get('rev'):
2291 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2292 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2292 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2293 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2293 displayer = show_changeset(ui, repo, opts, buffered=True)
2294 displayer = show_changeset(ui, repo, opts, buffered=True)
2294 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2295 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2295 filematcher)
2296 filematcher)
2296
2297
2297 def checkunsupportedgraphflags(pats, opts):
2298 def checkunsupportedgraphflags(pats, opts):
2298 for op in ["newest_first"]:
2299 for op in ["newest_first"]:
2299 if op in opts and opts[op]:
2300 if op in opts and opts[op]:
2300 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2301 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2301 % op.replace("_", "-"))
2302 % op.replace("_", "-"))
2302
2303
2303 def graphrevs(repo, nodes, opts):
2304 def graphrevs(repo, nodes, opts):
2304 limit = loglimit(opts)
2305 limit = loglimit(opts)
2305 nodes.reverse()
2306 nodes.reverse()
2306 if limit is not None:
2307 if limit is not None:
2307 nodes = nodes[:limit]
2308 nodes = nodes[:limit]
2308 return graphmod.nodes(repo, nodes)
2309 return graphmod.nodes(repo, nodes)
2309
2310
2310 def add(ui, repo, match, prefix, explicitonly, **opts):
2311 def add(ui, repo, match, prefix, explicitonly, **opts):
2311 join = lambda f: os.path.join(prefix, f)
2312 join = lambda f: os.path.join(prefix, f)
2312 bad = []
2313 bad = []
2313
2314
2314 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2315 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2315 names = []
2316 names = []
2316 wctx = repo[None]
2317 wctx = repo[None]
2317 cca = None
2318 cca = None
2318 abort, warn = scmutil.checkportabilityalert(ui)
2319 abort, warn = scmutil.checkportabilityalert(ui)
2319 if abort or warn:
2320 if abort or warn:
2320 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2321 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2321
2322
2322 badmatch = matchmod.badmatch(match, badfn)
2323 badmatch = matchmod.badmatch(match, badfn)
2323 dirstate = repo.dirstate
2324 dirstate = repo.dirstate
2324 # We don't want to just call wctx.walk here, since it would return a lot of
2325 # We don't want to just call wctx.walk here, since it would return a lot of
2325 # clean files, which we aren't interested in and takes time.
2326 # clean files, which we aren't interested in and takes time.
2326 for f in sorted(dirstate.walk(badmatch, sorted(wctx.substate),
2327 for f in sorted(dirstate.walk(badmatch, sorted(wctx.substate),
2327 True, False, full=False)):
2328 True, False, full=False)):
2328 exact = match.exact(f)
2329 exact = match.exact(f)
2329 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2330 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2330 if cca:
2331 if cca:
2331 cca(f)
2332 cca(f)
2332 names.append(f)
2333 names.append(f)
2333 if ui.verbose or not exact:
2334 if ui.verbose or not exact:
2334 ui.status(_('adding %s\n') % match.rel(f))
2335 ui.status(_('adding %s\n') % match.rel(f))
2335
2336
2336 for subpath in sorted(wctx.substate):
2337 for subpath in sorted(wctx.substate):
2337 sub = wctx.sub(subpath)
2338 sub = wctx.sub(subpath)
2338 try:
2339 try:
2339 submatch = matchmod.subdirmatcher(subpath, match)
2340 submatch = matchmod.subdirmatcher(subpath, match)
2340 if opts.get('subrepos'):
2341 if opts.get('subrepos'):
2341 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2342 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2342 else:
2343 else:
2343 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2344 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2344 except error.LookupError:
2345 except error.LookupError:
2345 ui.status(_("skipping missing subrepository: %s\n")
2346 ui.status(_("skipping missing subrepository: %s\n")
2346 % join(subpath))
2347 % join(subpath))
2347
2348
2348 if not opts.get('dry_run'):
2349 if not opts.get('dry_run'):
2349 rejected = wctx.add(names, prefix)
2350 rejected = wctx.add(names, prefix)
2350 bad.extend(f for f in rejected if f in match.files())
2351 bad.extend(f for f in rejected if f in match.files())
2351 return bad
2352 return bad
2352
2353
2353 def forget(ui, repo, match, prefix, explicitonly):
2354 def forget(ui, repo, match, prefix, explicitonly):
2354 join = lambda f: os.path.join(prefix, f)
2355 join = lambda f: os.path.join(prefix, f)
2355 bad = []
2356 bad = []
2356 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2357 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2357 wctx = repo[None]
2358 wctx = repo[None]
2358 forgot = []
2359 forgot = []
2359
2360
2360 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2361 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2361 forget = sorted(s[0] + s[1] + s[3] + s[6])
2362 forget = sorted(s[0] + s[1] + s[3] + s[6])
2362 if explicitonly:
2363 if explicitonly:
2363 forget = [f for f in forget if match.exact(f)]
2364 forget = [f for f in forget if match.exact(f)]
2364
2365
2365 for subpath in sorted(wctx.substate):
2366 for subpath in sorted(wctx.substate):
2366 sub = wctx.sub(subpath)
2367 sub = wctx.sub(subpath)
2367 try:
2368 try:
2368 submatch = matchmod.subdirmatcher(subpath, match)
2369 submatch = matchmod.subdirmatcher(subpath, match)
2369 subbad, subforgot = sub.forget(submatch, prefix)
2370 subbad, subforgot = sub.forget(submatch, prefix)
2370 bad.extend([subpath + '/' + f for f in subbad])
2371 bad.extend([subpath + '/' + f for f in subbad])
2371 forgot.extend([subpath + '/' + f for f in subforgot])
2372 forgot.extend([subpath + '/' + f for f in subforgot])
2372 except error.LookupError:
2373 except error.LookupError:
2373 ui.status(_("skipping missing subrepository: %s\n")
2374 ui.status(_("skipping missing subrepository: %s\n")
2374 % join(subpath))
2375 % join(subpath))
2375
2376
2376 if not explicitonly:
2377 if not explicitonly:
2377 for f in match.files():
2378 for f in match.files():
2378 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2379 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2379 if f not in forgot:
2380 if f not in forgot:
2380 if repo.wvfs.exists(f):
2381 if repo.wvfs.exists(f):
2381 # Don't complain if the exact case match wasn't given.
2382 # Don't complain if the exact case match wasn't given.
2382 # But don't do this until after checking 'forgot', so
2383 # But don't do this until after checking 'forgot', so
2383 # that subrepo files aren't normalized, and this op is
2384 # that subrepo files aren't normalized, and this op is
2384 # purely from data cached by the status walk above.
2385 # purely from data cached by the status walk above.
2385 if repo.dirstate.normalize(f) in repo.dirstate:
2386 if repo.dirstate.normalize(f) in repo.dirstate:
2386 continue
2387 continue
2387 ui.warn(_('not removing %s: '
2388 ui.warn(_('not removing %s: '
2388 'file is already untracked\n')
2389 'file is already untracked\n')
2389 % match.rel(f))
2390 % match.rel(f))
2390 bad.append(f)
2391 bad.append(f)
2391
2392
2392 for f in forget:
2393 for f in forget:
2393 if ui.verbose or not match.exact(f):
2394 if ui.verbose or not match.exact(f):
2394 ui.status(_('removing %s\n') % match.rel(f))
2395 ui.status(_('removing %s\n') % match.rel(f))
2395
2396
2396 rejected = wctx.forget(forget, prefix)
2397 rejected = wctx.forget(forget, prefix)
2397 bad.extend(f for f in rejected if f in match.files())
2398 bad.extend(f for f in rejected if f in match.files())
2398 forgot.extend(f for f in forget if f not in rejected)
2399 forgot.extend(f for f in forget if f not in rejected)
2399 return bad, forgot
2400 return bad, forgot
2400
2401
2401 def files(ui, ctx, m, fm, fmt, subrepos):
2402 def files(ui, ctx, m, fm, fmt, subrepos):
2402 rev = ctx.rev()
2403 rev = ctx.rev()
2403 ret = 1
2404 ret = 1
2404 ds = ctx.repo().dirstate
2405 ds = ctx.repo().dirstate
2405
2406
2406 for f in ctx.matches(m):
2407 for f in ctx.matches(m):
2407 if rev is None and ds[f] == 'r':
2408 if rev is None and ds[f] == 'r':
2408 continue
2409 continue
2409 fm.startitem()
2410 fm.startitem()
2410 if ui.verbose:
2411 if ui.verbose:
2411 fc = ctx[f]
2412 fc = ctx[f]
2412 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2413 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2413 fm.data(abspath=f)
2414 fm.data(abspath=f)
2414 fm.write('path', fmt, m.rel(f))
2415 fm.write('path', fmt, m.rel(f))
2415 ret = 0
2416 ret = 0
2416
2417
2417 for subpath in sorted(ctx.substate):
2418 for subpath in sorted(ctx.substate):
2418 if subrepos or m.matchessubrepo(subpath):
2419 if subrepos or m.matchessubrepo(subpath):
2419 sub = ctx.sub(subpath)
2420 sub = ctx.sub(subpath)
2420 try:
2421 try:
2421 submatch = matchmod.subdirmatcher(subpath, m)
2422 submatch = matchmod.subdirmatcher(subpath, m)
2422 recurse = m.exact(subpath) or subrepos
2423 recurse = m.exact(subpath) or subrepos
2423 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2424 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2424 ret = 0
2425 ret = 0
2425 except error.LookupError:
2426 except error.LookupError:
2426 ui.status(_("skipping missing subrepository: %s\n")
2427 ui.status(_("skipping missing subrepository: %s\n")
2427 % m.abs(subpath))
2428 % m.abs(subpath))
2428
2429
2429 return ret
2430 return ret
2430
2431
2431 def remove(ui, repo, m, prefix, after, force, subrepos, warnings=None):
2432 def remove(ui, repo, m, prefix, after, force, subrepos, warnings=None):
2432 join = lambda f: os.path.join(prefix, f)
2433 join = lambda f: os.path.join(prefix, f)
2433 ret = 0
2434 ret = 0
2434 s = repo.status(match=m, clean=True)
2435 s = repo.status(match=m, clean=True)
2435 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2436 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2436
2437
2437 wctx = repo[None]
2438 wctx = repo[None]
2438
2439
2439 if warnings is None:
2440 if warnings is None:
2440 warnings = []
2441 warnings = []
2441 warn = True
2442 warn = True
2442 else:
2443 else:
2443 warn = False
2444 warn = False
2444
2445
2445 subs = sorted(wctx.substate)
2446 subs = sorted(wctx.substate)
2446 total = len(subs)
2447 total = len(subs)
2447 count = 0
2448 count = 0
2448 for subpath in subs:
2449 for subpath in subs:
2449 count += 1
2450 count += 1
2450 if subrepos or m.matchessubrepo(subpath):
2451 if subrepos or m.matchessubrepo(subpath):
2451 ui.progress(_('searching'), count, total=total, unit=_('subrepos'))
2452 ui.progress(_('searching'), count, total=total, unit=_('subrepos'))
2452
2453
2453 sub = wctx.sub(subpath)
2454 sub = wctx.sub(subpath)
2454 try:
2455 try:
2455 submatch = matchmod.subdirmatcher(subpath, m)
2456 submatch = matchmod.subdirmatcher(subpath, m)
2456 if sub.removefiles(submatch, prefix, after, force, subrepos,
2457 if sub.removefiles(submatch, prefix, after, force, subrepos,
2457 warnings):
2458 warnings):
2458 ret = 1
2459 ret = 1
2459 except error.LookupError:
2460 except error.LookupError:
2460 warnings.append(_("skipping missing subrepository: %s\n")
2461 warnings.append(_("skipping missing subrepository: %s\n")
2461 % join(subpath))
2462 % join(subpath))
2462 ui.progress(_('searching'), None)
2463 ui.progress(_('searching'), None)
2463
2464
2464 # warn about failure to delete explicit files/dirs
2465 # warn about failure to delete explicit files/dirs
2465 deleteddirs = util.dirs(deleted)
2466 deleteddirs = util.dirs(deleted)
2466 files = m.files()
2467 files = m.files()
2467 total = len(files)
2468 total = len(files)
2468 count = 0
2469 count = 0
2469 for f in files:
2470 for f in files:
2470 def insubrepo():
2471 def insubrepo():
2471 for subpath in wctx.substate:
2472 for subpath in wctx.substate:
2472 if f.startswith(subpath + '/'):
2473 if f.startswith(subpath + '/'):
2473 return True
2474 return True
2474 return False
2475 return False
2475
2476
2476 count += 1
2477 count += 1
2477 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2478 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2478 isdir = f in deleteddirs or wctx.hasdir(f)
2479 isdir = f in deleteddirs or wctx.hasdir(f)
2479 if (f in repo.dirstate or isdir or f == '.'
2480 if (f in repo.dirstate or isdir or f == '.'
2480 or insubrepo() or f in subs):
2481 or insubrepo() or f in subs):
2481 continue
2482 continue
2482
2483
2483 if repo.wvfs.exists(f):
2484 if repo.wvfs.exists(f):
2484 if repo.wvfs.isdir(f):
2485 if repo.wvfs.isdir(f):
2485 warnings.append(_('not removing %s: no tracked files\n')
2486 warnings.append(_('not removing %s: no tracked files\n')
2486 % m.rel(f))
2487 % m.rel(f))
2487 else:
2488 else:
2488 warnings.append(_('not removing %s: file is untracked\n')
2489 warnings.append(_('not removing %s: file is untracked\n')
2489 % m.rel(f))
2490 % m.rel(f))
2490 # missing files will generate a warning elsewhere
2491 # missing files will generate a warning elsewhere
2491 ret = 1
2492 ret = 1
2492 ui.progress(_('deleting'), None)
2493 ui.progress(_('deleting'), None)
2493
2494
2494 if force:
2495 if force:
2495 list = modified + deleted + clean + added
2496 list = modified + deleted + clean + added
2496 elif after:
2497 elif after:
2497 list = deleted
2498 list = deleted
2498 remaining = modified + added + clean
2499 remaining = modified + added + clean
2499 total = len(remaining)
2500 total = len(remaining)
2500 count = 0
2501 count = 0
2501 for f in remaining:
2502 for f in remaining:
2502 count += 1
2503 count += 1
2503 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2504 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2504 warnings.append(_('not removing %s: file still exists\n')
2505 warnings.append(_('not removing %s: file still exists\n')
2505 % m.rel(f))
2506 % m.rel(f))
2506 ret = 1
2507 ret = 1
2507 ui.progress(_('skipping'), None)
2508 ui.progress(_('skipping'), None)
2508 else:
2509 else:
2509 list = deleted + clean
2510 list = deleted + clean
2510 total = len(modified) + len(added)
2511 total = len(modified) + len(added)
2511 count = 0
2512 count = 0
2512 for f in modified:
2513 for f in modified:
2513 count += 1
2514 count += 1
2514 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2515 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2515 warnings.append(_('not removing %s: file is modified (use -f'
2516 warnings.append(_('not removing %s: file is modified (use -f'
2516 ' to force removal)\n') % m.rel(f))
2517 ' to force removal)\n') % m.rel(f))
2517 ret = 1
2518 ret = 1
2518 for f in added:
2519 for f in added:
2519 count += 1
2520 count += 1
2520 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2521 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2521 warnings.append(_('not removing %s: file has been marked for add'
2522 warnings.append(_('not removing %s: file has been marked for add'
2522 ' (use forget to undo)\n') % m.rel(f))
2523 ' (use forget to undo)\n') % m.rel(f))
2523 ret = 1
2524 ret = 1
2524 ui.progress(_('skipping'), None)
2525 ui.progress(_('skipping'), None)
2525
2526
2526 list = sorted(list)
2527 list = sorted(list)
2527 total = len(list)
2528 total = len(list)
2528 count = 0
2529 count = 0
2529 for f in list:
2530 for f in list:
2530 count += 1
2531 count += 1
2531 if ui.verbose or not m.exact(f):
2532 if ui.verbose or not m.exact(f):
2532 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2533 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2533 ui.status(_('removing %s\n') % m.rel(f))
2534 ui.status(_('removing %s\n') % m.rel(f))
2534 ui.progress(_('deleting'), None)
2535 ui.progress(_('deleting'), None)
2535
2536
2536 with repo.wlock():
2537 with repo.wlock():
2537 if not after:
2538 if not after:
2538 for f in list:
2539 for f in list:
2539 if f in added:
2540 if f in added:
2540 continue # we never unlink added files on remove
2541 continue # we never unlink added files on remove
2541 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
2542 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
2542 repo[None].forget(list)
2543 repo[None].forget(list)
2543
2544
2544 if warn:
2545 if warn:
2545 for warning in warnings:
2546 for warning in warnings:
2546 ui.warn(warning)
2547 ui.warn(warning)
2547
2548
2548 return ret
2549 return ret
2549
2550
2550 def cat(ui, repo, ctx, matcher, prefix, **opts):
2551 def cat(ui, repo, ctx, matcher, prefix, **opts):
2551 err = 1
2552 err = 1
2552
2553
2553 def write(path):
2554 def write(path):
2554 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2555 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2555 pathname=os.path.join(prefix, path))
2556 pathname=os.path.join(prefix, path))
2556 data = ctx[path].data()
2557 data = ctx[path].data()
2557 if opts.get('decode'):
2558 if opts.get('decode'):
2558 data = repo.wwritedata(path, data)
2559 data = repo.wwritedata(path, data)
2559 fp.write(data)
2560 fp.write(data)
2560 fp.close()
2561 fp.close()
2561
2562
2562 # Automation often uses hg cat on single files, so special case it
2563 # Automation often uses hg cat on single files, so special case it
2563 # for performance to avoid the cost of parsing the manifest.
2564 # for performance to avoid the cost of parsing the manifest.
2564 if len(matcher.files()) == 1 and not matcher.anypats():
2565 if len(matcher.files()) == 1 and not matcher.anypats():
2565 file = matcher.files()[0]
2566 file = matcher.files()[0]
2566 mf = repo.manifest
2567 mf = repo.manifest
2567 mfnode = ctx.manifestnode()
2568 mfnode = ctx.manifestnode()
2568 if mfnode and mf.find(mfnode, file)[0]:
2569 if mfnode and mf.find(mfnode, file)[0]:
2569 write(file)
2570 write(file)
2570 return 0
2571 return 0
2571
2572
2572 for abs in ctx.walk(matcher):
2573 for abs in ctx.walk(matcher):
2573 write(abs)
2574 write(abs)
2574 err = 0
2575 err = 0
2575
2576
2576 for subpath in sorted(ctx.substate):
2577 for subpath in sorted(ctx.substate):
2577 sub = ctx.sub(subpath)
2578 sub = ctx.sub(subpath)
2578 try:
2579 try:
2579 submatch = matchmod.subdirmatcher(subpath, matcher)
2580 submatch = matchmod.subdirmatcher(subpath, matcher)
2580
2581
2581 if not sub.cat(submatch, os.path.join(prefix, sub._path),
2582 if not sub.cat(submatch, os.path.join(prefix, sub._path),
2582 **opts):
2583 **opts):
2583 err = 0
2584 err = 0
2584 except error.RepoLookupError:
2585 except error.RepoLookupError:
2585 ui.status(_("skipping missing subrepository: %s\n")
2586 ui.status(_("skipping missing subrepository: %s\n")
2586 % os.path.join(prefix, subpath))
2587 % os.path.join(prefix, subpath))
2587
2588
2588 return err
2589 return err
2589
2590
2590 def commit(ui, repo, commitfunc, pats, opts):
2591 def commit(ui, repo, commitfunc, pats, opts):
2591 '''commit the specified files or all outstanding changes'''
2592 '''commit the specified files or all outstanding changes'''
2592 date = opts.get('date')
2593 date = opts.get('date')
2593 if date:
2594 if date:
2594 opts['date'] = util.parsedate(date)
2595 opts['date'] = util.parsedate(date)
2595 message = logmessage(ui, opts)
2596 message = logmessage(ui, opts)
2596 matcher = scmutil.match(repo[None], pats, opts)
2597 matcher = scmutil.match(repo[None], pats, opts)
2597
2598
2598 # extract addremove carefully -- this function can be called from a command
2599 # extract addremove carefully -- this function can be called from a command
2599 # that doesn't support addremove
2600 # that doesn't support addremove
2600 if opts.get('addremove'):
2601 if opts.get('addremove'):
2601 if scmutil.addremove(repo, matcher, "", opts) != 0:
2602 if scmutil.addremove(repo, matcher, "", opts) != 0:
2602 raise error.Abort(
2603 raise error.Abort(
2603 _("failed to mark all new/missing files as added/removed"))
2604 _("failed to mark all new/missing files as added/removed"))
2604
2605
2605 return commitfunc(ui, repo, message, matcher, opts)
2606 return commitfunc(ui, repo, message, matcher, opts)
2606
2607
2607 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2608 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2608 # avoid cycle context -> subrepo -> cmdutil
2609 # avoid cycle context -> subrepo -> cmdutil
2609 from . import context
2610 from . import context
2610
2611
2611 # amend will reuse the existing user if not specified, but the obsolete
2612 # amend will reuse the existing user if not specified, but the obsolete
2612 # marker creation requires that the current user's name is specified.
2613 # marker creation requires that the current user's name is specified.
2613 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2614 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2614 ui.username() # raise exception if username not set
2615 ui.username() # raise exception if username not set
2615
2616
2616 ui.note(_('amending changeset %s\n') % old)
2617 ui.note(_('amending changeset %s\n') % old)
2617 base = old.p1()
2618 base = old.p1()
2618 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2619 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2619
2620
2620 wlock = lock = newid = None
2621 wlock = lock = newid = None
2621 try:
2622 try:
2622 wlock = repo.wlock()
2623 wlock = repo.wlock()
2623 lock = repo.lock()
2624 lock = repo.lock()
2624 with repo.transaction('amend') as tr:
2625 with repo.transaction('amend') as tr:
2625 # See if we got a message from -m or -l, if not, open the editor
2626 # See if we got a message from -m or -l, if not, open the editor
2626 # with the message of the changeset to amend
2627 # with the message of the changeset to amend
2627 message = logmessage(ui, opts)
2628 message = logmessage(ui, opts)
2628 # ensure logfile does not conflict with later enforcement of the
2629 # ensure logfile does not conflict with later enforcement of the
2629 # message. potential logfile content has been processed by
2630 # message. potential logfile content has been processed by
2630 # `logmessage` anyway.
2631 # `logmessage` anyway.
2631 opts.pop('logfile')
2632 opts.pop('logfile')
2632 # First, do a regular commit to record all changes in the working
2633 # First, do a regular commit to record all changes in the working
2633 # directory (if there are any)
2634 # directory (if there are any)
2634 ui.callhooks = False
2635 ui.callhooks = False
2635 activebookmark = repo._bookmarks.active
2636 activebookmark = repo._bookmarks.active
2636 try:
2637 try:
2637 repo._bookmarks.active = None
2638 repo._bookmarks.active = None
2638 opts['message'] = 'temporary amend commit for %s' % old
2639 opts['message'] = 'temporary amend commit for %s' % old
2639 node = commit(ui, repo, commitfunc, pats, opts)
2640 node = commit(ui, repo, commitfunc, pats, opts)
2640 finally:
2641 finally:
2641 repo._bookmarks.active = activebookmark
2642 repo._bookmarks.active = activebookmark
2642 repo._bookmarks.recordchange(tr)
2643 repo._bookmarks.recordchange(tr)
2643 ui.callhooks = True
2644 ui.callhooks = True
2644 ctx = repo[node]
2645 ctx = repo[node]
2645
2646
2646 # Participating changesets:
2647 # Participating changesets:
2647 #
2648 #
2648 # node/ctx o - new (intermediate) commit that contains changes
2649 # node/ctx o - new (intermediate) commit that contains changes
2649 # | from working dir to go into amending commit
2650 # | from working dir to go into amending commit
2650 # | (or a workingctx if there were no changes)
2651 # | (or a workingctx if there were no changes)
2651 # |
2652 # |
2652 # old o - changeset to amend
2653 # old o - changeset to amend
2653 # |
2654 # |
2654 # base o - parent of amending changeset
2655 # base o - parent of amending changeset
2655
2656
2656 # Update extra dict from amended commit (e.g. to preserve graft
2657 # Update extra dict from amended commit (e.g. to preserve graft
2657 # source)
2658 # source)
2658 extra.update(old.extra())
2659 extra.update(old.extra())
2659
2660
2660 # Also update it from the intermediate commit or from the wctx
2661 # Also update it from the intermediate commit or from the wctx
2661 extra.update(ctx.extra())
2662 extra.update(ctx.extra())
2662
2663
2663 if len(old.parents()) > 1:
2664 if len(old.parents()) > 1:
2664 # ctx.files() isn't reliable for merges, so fall back to the
2665 # ctx.files() isn't reliable for merges, so fall back to the
2665 # slower repo.status() method
2666 # slower repo.status() method
2666 files = set([fn for st in repo.status(base, old)[:3]
2667 files = set([fn for st in repo.status(base, old)[:3]
2667 for fn in st])
2668 for fn in st])
2668 else:
2669 else:
2669 files = set(old.files())
2670 files = set(old.files())
2670
2671
2671 # Second, we use either the commit we just did, or if there were no
2672 # Second, we use either the commit we just did, or if there were no
2672 # changes the parent of the working directory as the version of the
2673 # changes the parent of the working directory as the version of the
2673 # files in the final amend commit
2674 # files in the final amend commit
2674 if node:
2675 if node:
2675 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2676 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2676
2677
2677 user = ctx.user()
2678 user = ctx.user()
2678 date = ctx.date()
2679 date = ctx.date()
2679 # Recompute copies (avoid recording a -> b -> a)
2680 # Recompute copies (avoid recording a -> b -> a)
2680 copied = copies.pathcopies(base, ctx)
2681 copied = copies.pathcopies(base, ctx)
2681 if old.p2:
2682 if old.p2:
2682 copied.update(copies.pathcopies(old.p2(), ctx))
2683 copied.update(copies.pathcopies(old.p2(), ctx))
2683
2684
2684 # Prune files which were reverted by the updates: if old
2685 # Prune files which were reverted by the updates: if old
2685 # introduced file X and our intermediate commit, node,
2686 # introduced file X and our intermediate commit, node,
2686 # renamed that file, then those two files are the same and
2687 # renamed that file, then those two files are the same and
2687 # we can discard X from our list of files. Likewise if X
2688 # we can discard X from our list of files. Likewise if X
2688 # was deleted, it's no longer relevant
2689 # was deleted, it's no longer relevant
2689 files.update(ctx.files())
2690 files.update(ctx.files())
2690
2691
2691 def samefile(f):
2692 def samefile(f):
2692 if f in ctx.manifest():
2693 if f in ctx.manifest():
2693 a = ctx.filectx(f)
2694 a = ctx.filectx(f)
2694 if f in base.manifest():
2695 if f in base.manifest():
2695 b = base.filectx(f)
2696 b = base.filectx(f)
2696 return (not a.cmp(b)
2697 return (not a.cmp(b)
2697 and a.flags() == b.flags())
2698 and a.flags() == b.flags())
2698 else:
2699 else:
2699 return False
2700 return False
2700 else:
2701 else:
2701 return f not in base.manifest()
2702 return f not in base.manifest()
2702 files = [f for f in files if not samefile(f)]
2703 files = [f for f in files if not samefile(f)]
2703
2704
2704 def filectxfn(repo, ctx_, path):
2705 def filectxfn(repo, ctx_, path):
2705 try:
2706 try:
2706 fctx = ctx[path]
2707 fctx = ctx[path]
2707 flags = fctx.flags()
2708 flags = fctx.flags()
2708 mctx = context.memfilectx(repo,
2709 mctx = context.memfilectx(repo,
2709 fctx.path(), fctx.data(),
2710 fctx.path(), fctx.data(),
2710 islink='l' in flags,
2711 islink='l' in flags,
2711 isexec='x' in flags,
2712 isexec='x' in flags,
2712 copied=copied.get(path))
2713 copied=copied.get(path))
2713 return mctx
2714 return mctx
2714 except KeyError:
2715 except KeyError:
2715 return None
2716 return None
2716 else:
2717 else:
2717 ui.note(_('copying changeset %s to %s\n') % (old, base))
2718 ui.note(_('copying changeset %s to %s\n') % (old, base))
2718
2719
2719 # Use version of files as in the old cset
2720 # Use version of files as in the old cset
2720 def filectxfn(repo, ctx_, path):
2721 def filectxfn(repo, ctx_, path):
2721 try:
2722 try:
2722 return old.filectx(path)
2723 return old.filectx(path)
2723 except KeyError:
2724 except KeyError:
2724 return None
2725 return None
2725
2726
2726 user = opts.get('user') or old.user()
2727 user = opts.get('user') or old.user()
2727 date = opts.get('date') or old.date()
2728 date = opts.get('date') or old.date()
2728 editform = mergeeditform(old, 'commit.amend')
2729 editform = mergeeditform(old, 'commit.amend')
2729 editor = getcommiteditor(editform=editform, **opts)
2730 editor = getcommiteditor(editform=editform, **opts)
2730 if not message:
2731 if not message:
2731 editor = getcommiteditor(edit=True, editform=editform)
2732 editor = getcommiteditor(edit=True, editform=editform)
2732 message = old.description()
2733 message = old.description()
2733
2734
2734 pureextra = extra.copy()
2735 pureextra = extra.copy()
2735 extra['amend_source'] = old.hex()
2736 extra['amend_source'] = old.hex()
2736
2737
2737 new = context.memctx(repo,
2738 new = context.memctx(repo,
2738 parents=[base.node(), old.p2().node()],
2739 parents=[base.node(), old.p2().node()],
2739 text=message,
2740 text=message,
2740 files=files,
2741 files=files,
2741 filectxfn=filectxfn,
2742 filectxfn=filectxfn,
2742 user=user,
2743 user=user,
2743 date=date,
2744 date=date,
2744 extra=extra,
2745 extra=extra,
2745 editor=editor)
2746 editor=editor)
2746
2747
2747 newdesc = changelog.stripdesc(new.description())
2748 newdesc = changelog.stripdesc(new.description())
2748 if ((not node)
2749 if ((not node)
2749 and newdesc == old.description()
2750 and newdesc == old.description()
2750 and user == old.user()
2751 and user == old.user()
2751 and date == old.date()
2752 and date == old.date()
2752 and pureextra == old.extra()):
2753 and pureextra == old.extra()):
2753 # nothing changed. continuing here would create a new node
2754 # nothing changed. continuing here would create a new node
2754 # anyway because of the amend_source noise.
2755 # anyway because of the amend_source noise.
2755 #
2756 #
2756 # This not what we expect from amend.
2757 # This not what we expect from amend.
2757 return old.node()
2758 return old.node()
2758
2759
2759 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2760 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2760 try:
2761 try:
2761 if opts.get('secret'):
2762 if opts.get('secret'):
2762 commitphase = 'secret'
2763 commitphase = 'secret'
2763 else:
2764 else:
2764 commitphase = old.phase()
2765 commitphase = old.phase()
2765 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2766 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2766 newid = repo.commitctx(new)
2767 newid = repo.commitctx(new)
2767 finally:
2768 finally:
2768 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2769 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2769 if newid != old.node():
2770 if newid != old.node():
2770 # Reroute the working copy parent to the new changeset
2771 # Reroute the working copy parent to the new changeset
2771 repo.setparents(newid, nullid)
2772 repo.setparents(newid, nullid)
2772
2773
2773 # Move bookmarks from old parent to amend commit
2774 # Move bookmarks from old parent to amend commit
2774 bms = repo.nodebookmarks(old.node())
2775 bms = repo.nodebookmarks(old.node())
2775 if bms:
2776 if bms:
2776 marks = repo._bookmarks
2777 marks = repo._bookmarks
2777 for bm in bms:
2778 for bm in bms:
2778 ui.debug('moving bookmarks %r from %s to %s\n' %
2779 ui.debug('moving bookmarks %r from %s to %s\n' %
2779 (marks, old.hex(), hex(newid)))
2780 (marks, old.hex(), hex(newid)))
2780 marks[bm] = newid
2781 marks[bm] = newid
2781 marks.recordchange(tr)
2782 marks.recordchange(tr)
2782 #commit the whole amend process
2783 #commit the whole amend process
2783 if createmarkers:
2784 if createmarkers:
2784 # mark the new changeset as successor of the rewritten one
2785 # mark the new changeset as successor of the rewritten one
2785 new = repo[newid]
2786 new = repo[newid]
2786 obs = [(old, (new,))]
2787 obs = [(old, (new,))]
2787 if node:
2788 if node:
2788 obs.append((ctx, ()))
2789 obs.append((ctx, ()))
2789
2790
2790 obsolete.createmarkers(repo, obs)
2791 obsolete.createmarkers(repo, obs)
2791 if not createmarkers and newid != old.node():
2792 if not createmarkers and newid != old.node():
2792 # Strip the intermediate commit (if there was one) and the amended
2793 # Strip the intermediate commit (if there was one) and the amended
2793 # commit
2794 # commit
2794 if node:
2795 if node:
2795 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2796 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2796 ui.note(_('stripping amended changeset %s\n') % old)
2797 ui.note(_('stripping amended changeset %s\n') % old)
2797 repair.strip(ui, repo, old.node(), topic='amend-backup')
2798 repair.strip(ui, repo, old.node(), topic='amend-backup')
2798 finally:
2799 finally:
2799 lockmod.release(lock, wlock)
2800 lockmod.release(lock, wlock)
2800 return newid
2801 return newid
2801
2802
2802 def commiteditor(repo, ctx, subs, editform=''):
2803 def commiteditor(repo, ctx, subs, editform=''):
2803 if ctx.description():
2804 if ctx.description():
2804 return ctx.description()
2805 return ctx.description()
2805 return commitforceeditor(repo, ctx, subs, editform=editform,
2806 return commitforceeditor(repo, ctx, subs, editform=editform,
2806 unchangedmessagedetection=True)
2807 unchangedmessagedetection=True)
2807
2808
2808 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2809 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2809 editform='', unchangedmessagedetection=False):
2810 editform='', unchangedmessagedetection=False):
2810 if not extramsg:
2811 if not extramsg:
2811 extramsg = _("Leave message empty to abort commit.")
2812 extramsg = _("Leave message empty to abort commit.")
2812
2813
2813 forms = [e for e in editform.split('.') if e]
2814 forms = [e for e in editform.split('.') if e]
2814 forms.insert(0, 'changeset')
2815 forms.insert(0, 'changeset')
2815 templatetext = None
2816 templatetext = None
2816 while forms:
2817 while forms:
2817 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2818 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2818 if tmpl:
2819 if tmpl:
2819 templatetext = committext = buildcommittemplate(
2820 templatetext = committext = buildcommittemplate(
2820 repo, ctx, subs, extramsg, tmpl)
2821 repo, ctx, subs, extramsg, tmpl)
2821 break
2822 break
2822 forms.pop()
2823 forms.pop()
2823 else:
2824 else:
2824 committext = buildcommittext(repo, ctx, subs, extramsg)
2825 committext = buildcommittext(repo, ctx, subs, extramsg)
2825
2826
2826 # run editor in the repository root
2827 # run editor in the repository root
2827 olddir = os.getcwd()
2828 olddir = os.getcwd()
2828 os.chdir(repo.root)
2829 os.chdir(repo.root)
2829
2830
2830 # make in-memory changes visible to external process
2831 # make in-memory changes visible to external process
2831 tr = repo.currenttransaction()
2832 tr = repo.currenttransaction()
2832 repo.dirstate.write(tr)
2833 repo.dirstate.write(tr)
2833 pending = tr and tr.writepending() and repo.root
2834 pending = tr and tr.writepending() and repo.root
2834
2835
2835 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
2836 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
2836 editform=editform, pending=pending)
2837 editform=editform, pending=pending)
2837 text = re.sub("(?m)^HG:.*(\n|$)", "", editortext)
2838 text = re.sub("(?m)^HG:.*(\n|$)", "", editortext)
2838 os.chdir(olddir)
2839 os.chdir(olddir)
2839
2840
2840 if finishdesc:
2841 if finishdesc:
2841 text = finishdesc(text)
2842 text = finishdesc(text)
2842 if not text.strip():
2843 if not text.strip():
2843 raise error.Abort(_("empty commit message"))
2844 raise error.Abort(_("empty commit message"))
2844 if unchangedmessagedetection and editortext == templatetext:
2845 if unchangedmessagedetection and editortext == templatetext:
2845 raise error.Abort(_("commit message unchanged"))
2846 raise error.Abort(_("commit message unchanged"))
2846
2847
2847 return text
2848 return text
2848
2849
2849 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2850 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2850 ui = repo.ui
2851 ui = repo.ui
2851 tmpl, mapfile = gettemplate(ui, tmpl, None)
2852 tmpl, mapfile = gettemplate(ui, tmpl, None)
2852
2853
2853 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2854 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2854
2855
2855 for k, v in repo.ui.configitems('committemplate'):
2856 for k, v in repo.ui.configitems('committemplate'):
2856 if k != 'changeset':
2857 if k != 'changeset':
2857 t.t.cache[k] = v
2858 t.t.cache[k] = v
2858
2859
2859 if not extramsg:
2860 if not extramsg:
2860 extramsg = '' # ensure that extramsg is string
2861 extramsg = '' # ensure that extramsg is string
2861
2862
2862 ui.pushbuffer()
2863 ui.pushbuffer()
2863 t.show(ctx, extramsg=extramsg)
2864 t.show(ctx, extramsg=extramsg)
2864 return ui.popbuffer()
2865 return ui.popbuffer()
2865
2866
2866 def hgprefix(msg):
2867 def hgprefix(msg):
2867 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
2868 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
2868
2869
2869 def buildcommittext(repo, ctx, subs, extramsg):
2870 def buildcommittext(repo, ctx, subs, extramsg):
2870 edittext = []
2871 edittext = []
2871 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2872 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2872 if ctx.description():
2873 if ctx.description():
2873 edittext.append(ctx.description())
2874 edittext.append(ctx.description())
2874 edittext.append("")
2875 edittext.append("")
2875 edittext.append("") # Empty line between message and comments.
2876 edittext.append("") # Empty line between message and comments.
2876 edittext.append(hgprefix(_("Enter commit message."
2877 edittext.append(hgprefix(_("Enter commit message."
2877 " Lines beginning with 'HG:' are removed.")))
2878 " Lines beginning with 'HG:' are removed.")))
2878 edittext.append(hgprefix(extramsg))
2879 edittext.append(hgprefix(extramsg))
2879 edittext.append("HG: --")
2880 edittext.append("HG: --")
2880 edittext.append(hgprefix(_("user: %s") % ctx.user()))
2881 edittext.append(hgprefix(_("user: %s") % ctx.user()))
2881 if ctx.p2():
2882 if ctx.p2():
2882 edittext.append(hgprefix(_("branch merge")))
2883 edittext.append(hgprefix(_("branch merge")))
2883 if ctx.branch():
2884 if ctx.branch():
2884 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
2885 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
2885 if bookmarks.isactivewdirparent(repo):
2886 if bookmarks.isactivewdirparent(repo):
2886 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
2887 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
2887 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
2888 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
2888 edittext.extend([hgprefix(_("added %s") % f) for f in added])
2889 edittext.extend([hgprefix(_("added %s") % f) for f in added])
2889 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
2890 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
2890 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
2891 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
2891 if not added and not modified and not removed:
2892 if not added and not modified and not removed:
2892 edittext.append(hgprefix(_("no files changed")))
2893 edittext.append(hgprefix(_("no files changed")))
2893 edittext.append("")
2894 edittext.append("")
2894
2895
2895 return "\n".join(edittext)
2896 return "\n".join(edittext)
2896
2897
2897 def commitstatus(repo, node, branch, bheads=None, opts=None):
2898 def commitstatus(repo, node, branch, bheads=None, opts=None):
2898 if opts is None:
2899 if opts is None:
2899 opts = {}
2900 opts = {}
2900 ctx = repo[node]
2901 ctx = repo[node]
2901 parents = ctx.parents()
2902 parents = ctx.parents()
2902
2903
2903 if (not opts.get('amend') and bheads and node not in bheads and not
2904 if (not opts.get('amend') and bheads and node not in bheads and not
2904 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2905 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2905 repo.ui.status(_('created new head\n'))
2906 repo.ui.status(_('created new head\n'))
2906 # The message is not printed for initial roots. For the other
2907 # The message is not printed for initial roots. For the other
2907 # changesets, it is printed in the following situations:
2908 # changesets, it is printed in the following situations:
2908 #
2909 #
2909 # Par column: for the 2 parents with ...
2910 # Par column: for the 2 parents with ...
2910 # N: null or no parent
2911 # N: null or no parent
2911 # B: parent is on another named branch
2912 # B: parent is on another named branch
2912 # C: parent is a regular non head changeset
2913 # C: parent is a regular non head changeset
2913 # H: parent was a branch head of the current branch
2914 # H: parent was a branch head of the current branch
2914 # Msg column: whether we print "created new head" message
2915 # Msg column: whether we print "created new head" message
2915 # In the following, it is assumed that there already exists some
2916 # In the following, it is assumed that there already exists some
2916 # initial branch heads of the current branch, otherwise nothing is
2917 # initial branch heads of the current branch, otherwise nothing is
2917 # printed anyway.
2918 # printed anyway.
2918 #
2919 #
2919 # Par Msg Comment
2920 # Par Msg Comment
2920 # N N y additional topo root
2921 # N N y additional topo root
2921 #
2922 #
2922 # B N y additional branch root
2923 # B N y additional branch root
2923 # C N y additional topo head
2924 # C N y additional topo head
2924 # H N n usual case
2925 # H N n usual case
2925 #
2926 #
2926 # B B y weird additional branch root
2927 # B B y weird additional branch root
2927 # C B y branch merge
2928 # C B y branch merge
2928 # H B n merge with named branch
2929 # H B n merge with named branch
2929 #
2930 #
2930 # C C y additional head from merge
2931 # C C y additional head from merge
2931 # C H n merge with a head
2932 # C H n merge with a head
2932 #
2933 #
2933 # H H n head merge: head count decreases
2934 # H H n head merge: head count decreases
2934
2935
2935 if not opts.get('close_branch'):
2936 if not opts.get('close_branch'):
2936 for r in parents:
2937 for r in parents:
2937 if r.closesbranch() and r.branch() == branch:
2938 if r.closesbranch() and r.branch() == branch:
2938 repo.ui.status(_('reopening closed branch head %d\n') % r)
2939 repo.ui.status(_('reopening closed branch head %d\n') % r)
2939
2940
2940 if repo.ui.debugflag:
2941 if repo.ui.debugflag:
2941 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2942 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2942 elif repo.ui.verbose:
2943 elif repo.ui.verbose:
2943 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2944 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2944
2945
2945 def postcommitstatus(repo, pats, opts):
2946 def postcommitstatus(repo, pats, opts):
2946 return repo.status(match=scmutil.match(repo[None], pats, opts))
2947 return repo.status(match=scmutil.match(repo[None], pats, opts))
2947
2948
2948 def revert(ui, repo, ctx, parents, *pats, **opts):
2949 def revert(ui, repo, ctx, parents, *pats, **opts):
2949 parent, p2 = parents
2950 parent, p2 = parents
2950 node = ctx.node()
2951 node = ctx.node()
2951
2952
2952 mf = ctx.manifest()
2953 mf = ctx.manifest()
2953 if node == p2:
2954 if node == p2:
2954 parent = p2
2955 parent = p2
2955
2956
2956 # need all matching names in dirstate and manifest of target rev,
2957 # need all matching names in dirstate and manifest of target rev,
2957 # so have to walk both. do not print errors if files exist in one
2958 # so have to walk both. do not print errors if files exist in one
2958 # but not other. in both cases, filesets should be evaluated against
2959 # but not other. in both cases, filesets should be evaluated against
2959 # workingctx to get consistent result (issue4497). this means 'set:**'
2960 # workingctx to get consistent result (issue4497). this means 'set:**'
2960 # cannot be used to select missing files from target rev.
2961 # cannot be used to select missing files from target rev.
2961
2962
2962 # `names` is a mapping for all elements in working copy and target revision
2963 # `names` is a mapping for all elements in working copy and target revision
2963 # The mapping is in the form:
2964 # The mapping is in the form:
2964 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2965 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2965 names = {}
2966 names = {}
2966
2967
2967 with repo.wlock():
2968 with repo.wlock():
2968 ## filling of the `names` mapping
2969 ## filling of the `names` mapping
2969 # walk dirstate to fill `names`
2970 # walk dirstate to fill `names`
2970
2971
2971 interactive = opts.get('interactive', False)
2972 interactive = opts.get('interactive', False)
2972 wctx = repo[None]
2973 wctx = repo[None]
2973 m = scmutil.match(wctx, pats, opts)
2974 m = scmutil.match(wctx, pats, opts)
2974
2975
2975 # we'll need this later
2976 # we'll need this later
2976 targetsubs = sorted(s for s in wctx.substate if m(s))
2977 targetsubs = sorted(s for s in wctx.substate if m(s))
2977
2978
2978 if not m.always():
2979 if not m.always():
2979 for abs in repo.walk(matchmod.badmatch(m, lambda x, y: False)):
2980 for abs in repo.walk(matchmod.badmatch(m, lambda x, y: False)):
2980 names[abs] = m.rel(abs), m.exact(abs)
2981 names[abs] = m.rel(abs), m.exact(abs)
2981
2982
2982 # walk target manifest to fill `names`
2983 # walk target manifest to fill `names`
2983
2984
2984 def badfn(path, msg):
2985 def badfn(path, msg):
2985 if path in names:
2986 if path in names:
2986 return
2987 return
2987 if path in ctx.substate:
2988 if path in ctx.substate:
2988 return
2989 return
2989 path_ = path + '/'
2990 path_ = path + '/'
2990 for f in names:
2991 for f in names:
2991 if f.startswith(path_):
2992 if f.startswith(path_):
2992 return
2993 return
2993 ui.warn("%s: %s\n" % (m.rel(path), msg))
2994 ui.warn("%s: %s\n" % (m.rel(path), msg))
2994
2995
2995 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
2996 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
2996 if abs not in names:
2997 if abs not in names:
2997 names[abs] = m.rel(abs), m.exact(abs)
2998 names[abs] = m.rel(abs), m.exact(abs)
2998
2999
2999 # Find status of all file in `names`.
3000 # Find status of all file in `names`.
3000 m = scmutil.matchfiles(repo, names)
3001 m = scmutil.matchfiles(repo, names)
3001
3002
3002 changes = repo.status(node1=node, match=m,
3003 changes = repo.status(node1=node, match=m,
3003 unknown=True, ignored=True, clean=True)
3004 unknown=True, ignored=True, clean=True)
3004 else:
3005 else:
3005 changes = repo.status(node1=node, match=m)
3006 changes = repo.status(node1=node, match=m)
3006 for kind in changes:
3007 for kind in changes:
3007 for abs in kind:
3008 for abs in kind:
3008 names[abs] = m.rel(abs), m.exact(abs)
3009 names[abs] = m.rel(abs), m.exact(abs)
3009
3010
3010 m = scmutil.matchfiles(repo, names)
3011 m = scmutil.matchfiles(repo, names)
3011
3012
3012 modified = set(changes.modified)
3013 modified = set(changes.modified)
3013 added = set(changes.added)
3014 added = set(changes.added)
3014 removed = set(changes.removed)
3015 removed = set(changes.removed)
3015 _deleted = set(changes.deleted)
3016 _deleted = set(changes.deleted)
3016 unknown = set(changes.unknown)
3017 unknown = set(changes.unknown)
3017 unknown.update(changes.ignored)
3018 unknown.update(changes.ignored)
3018 clean = set(changes.clean)
3019 clean = set(changes.clean)
3019 modadded = set()
3020 modadded = set()
3020
3021
3021 # split between files known in target manifest and the others
3022 # split between files known in target manifest and the others
3022 smf = set(mf)
3023 smf = set(mf)
3023
3024
3024 # determine the exact nature of the deleted changesets
3025 # determine the exact nature of the deleted changesets
3025 deladded = _deleted - smf
3026 deladded = _deleted - smf
3026 deleted = _deleted - deladded
3027 deleted = _deleted - deladded
3027
3028
3028 # We need to account for the state of the file in the dirstate,
3029 # We need to account for the state of the file in the dirstate,
3029 # even when we revert against something else than parent. This will
3030 # even when we revert against something else than parent. This will
3030 # slightly alter the behavior of revert (doing back up or not, delete
3031 # slightly alter the behavior of revert (doing back up or not, delete
3031 # or just forget etc).
3032 # or just forget etc).
3032 if parent == node:
3033 if parent == node:
3033 dsmodified = modified
3034 dsmodified = modified
3034 dsadded = added
3035 dsadded = added
3035 dsremoved = removed
3036 dsremoved = removed
3036 # store all local modifications, useful later for rename detection
3037 # store all local modifications, useful later for rename detection
3037 localchanges = dsmodified | dsadded
3038 localchanges = dsmodified | dsadded
3038 modified, added, removed = set(), set(), set()
3039 modified, added, removed = set(), set(), set()
3039 else:
3040 else:
3040 changes = repo.status(node1=parent, match=m)
3041 changes = repo.status(node1=parent, match=m)
3041 dsmodified = set(changes.modified)
3042 dsmodified = set(changes.modified)
3042 dsadded = set(changes.added)
3043 dsadded = set(changes.added)
3043 dsremoved = set(changes.removed)
3044 dsremoved = set(changes.removed)
3044 # store all local modifications, useful later for rename detection
3045 # store all local modifications, useful later for rename detection
3045 localchanges = dsmodified | dsadded
3046 localchanges = dsmodified | dsadded
3046
3047
3047 # only take into account for removes between wc and target
3048 # only take into account for removes between wc and target
3048 clean |= dsremoved - removed
3049 clean |= dsremoved - removed
3049 dsremoved &= removed
3050 dsremoved &= removed
3050 # distinct between dirstate remove and other
3051 # distinct between dirstate remove and other
3051 removed -= dsremoved
3052 removed -= dsremoved
3052
3053
3053 modadded = added & dsmodified
3054 modadded = added & dsmodified
3054 added -= modadded
3055 added -= modadded
3055
3056
3056 # tell newly modified apart.
3057 # tell newly modified apart.
3057 dsmodified &= modified
3058 dsmodified &= modified
3058 dsmodified |= modified & dsadded # dirstate added may need backup
3059 dsmodified |= modified & dsadded # dirstate added may need backup
3059 modified -= dsmodified
3060 modified -= dsmodified
3060
3061
3061 # We need to wait for some post-processing to update this set
3062 # We need to wait for some post-processing to update this set
3062 # before making the distinction. The dirstate will be used for
3063 # before making the distinction. The dirstate will be used for
3063 # that purpose.
3064 # that purpose.
3064 dsadded = added
3065 dsadded = added
3065
3066
3066 # in case of merge, files that are actually added can be reported as
3067 # in case of merge, files that are actually added can be reported as
3067 # modified, we need to post process the result
3068 # modified, we need to post process the result
3068 if p2 != nullid:
3069 if p2 != nullid:
3069 mergeadd = dsmodified - smf
3070 mergeadd = dsmodified - smf
3070 dsadded |= mergeadd
3071 dsadded |= mergeadd
3071 dsmodified -= mergeadd
3072 dsmodified -= mergeadd
3072
3073
3073 # if f is a rename, update `names` to also revert the source
3074 # if f is a rename, update `names` to also revert the source
3074 cwd = repo.getcwd()
3075 cwd = repo.getcwd()
3075 for f in localchanges:
3076 for f in localchanges:
3076 src = repo.dirstate.copied(f)
3077 src = repo.dirstate.copied(f)
3077 # XXX should we check for rename down to target node?
3078 # XXX should we check for rename down to target node?
3078 if src and src not in names and repo.dirstate[src] == 'r':
3079 if src and src not in names and repo.dirstate[src] == 'r':
3079 dsremoved.add(src)
3080 dsremoved.add(src)
3080 names[src] = (repo.pathto(src, cwd), True)
3081 names[src] = (repo.pathto(src, cwd), True)
3081
3082
3082 # distinguish between file to forget and the other
3083 # distinguish between file to forget and the other
3083 added = set()
3084 added = set()
3084 for abs in dsadded:
3085 for abs in dsadded:
3085 if repo.dirstate[abs] != 'a':
3086 if repo.dirstate[abs] != 'a':
3086 added.add(abs)
3087 added.add(abs)
3087 dsadded -= added
3088 dsadded -= added
3088
3089
3089 for abs in deladded:
3090 for abs in deladded:
3090 if repo.dirstate[abs] == 'a':
3091 if repo.dirstate[abs] == 'a':
3091 dsadded.add(abs)
3092 dsadded.add(abs)
3092 deladded -= dsadded
3093 deladded -= dsadded
3093
3094
3094 # For files marked as removed, we check if an unknown file is present at
3095 # For files marked as removed, we check if an unknown file is present at
3095 # the same path. If a such file exists it may need to be backed up.
3096 # the same path. If a such file exists it may need to be backed up.
3096 # Making the distinction at this stage helps have simpler backup
3097 # Making the distinction at this stage helps have simpler backup
3097 # logic.
3098 # logic.
3098 removunk = set()
3099 removunk = set()
3099 for abs in removed:
3100 for abs in removed:
3100 target = repo.wjoin(abs)
3101 target = repo.wjoin(abs)
3101 if os.path.lexists(target):
3102 if os.path.lexists(target):
3102 removunk.add(abs)
3103 removunk.add(abs)
3103 removed -= removunk
3104 removed -= removunk
3104
3105
3105 dsremovunk = set()
3106 dsremovunk = set()
3106 for abs in dsremoved:
3107 for abs in dsremoved:
3107 target = repo.wjoin(abs)
3108 target = repo.wjoin(abs)
3108 if os.path.lexists(target):
3109 if os.path.lexists(target):
3109 dsremovunk.add(abs)
3110 dsremovunk.add(abs)
3110 dsremoved -= dsremovunk
3111 dsremoved -= dsremovunk
3111
3112
3112 # action to be actually performed by revert
3113 # action to be actually performed by revert
3113 # (<list of file>, message>) tuple
3114 # (<list of file>, message>) tuple
3114 actions = {'revert': ([], _('reverting %s\n')),
3115 actions = {'revert': ([], _('reverting %s\n')),
3115 'add': ([], _('adding %s\n')),
3116 'add': ([], _('adding %s\n')),
3116 'remove': ([], _('removing %s\n')),
3117 'remove': ([], _('removing %s\n')),
3117 'drop': ([], _('removing %s\n')),
3118 'drop': ([], _('removing %s\n')),
3118 'forget': ([], _('forgetting %s\n')),
3119 'forget': ([], _('forgetting %s\n')),
3119 'undelete': ([], _('undeleting %s\n')),
3120 'undelete': ([], _('undeleting %s\n')),
3120 'noop': (None, _('no changes needed to %s\n')),
3121 'noop': (None, _('no changes needed to %s\n')),
3121 'unknown': (None, _('file not managed: %s\n')),
3122 'unknown': (None, _('file not managed: %s\n')),
3122 }
3123 }
3123
3124
3124 # "constant" that convey the backup strategy.
3125 # "constant" that convey the backup strategy.
3125 # All set to `discard` if `no-backup` is set do avoid checking
3126 # All set to `discard` if `no-backup` is set do avoid checking
3126 # no_backup lower in the code.
3127 # no_backup lower in the code.
3127 # These values are ordered for comparison purposes
3128 # These values are ordered for comparison purposes
3128 backupinteractive = 3 # do backup if interactively modified
3129 backupinteractive = 3 # do backup if interactively modified
3129 backup = 2 # unconditionally do backup
3130 backup = 2 # unconditionally do backup
3130 check = 1 # check if the existing file differs from target
3131 check = 1 # check if the existing file differs from target
3131 discard = 0 # never do backup
3132 discard = 0 # never do backup
3132 if opts.get('no_backup'):
3133 if opts.get('no_backup'):
3133 backupinteractive = backup = check = discard
3134 backupinteractive = backup = check = discard
3134 if interactive:
3135 if interactive:
3135 dsmodifiedbackup = backupinteractive
3136 dsmodifiedbackup = backupinteractive
3136 else:
3137 else:
3137 dsmodifiedbackup = backup
3138 dsmodifiedbackup = backup
3138 tobackup = set()
3139 tobackup = set()
3139
3140
3140 backupanddel = actions['remove']
3141 backupanddel = actions['remove']
3141 if not opts.get('no_backup'):
3142 if not opts.get('no_backup'):
3142 backupanddel = actions['drop']
3143 backupanddel = actions['drop']
3143
3144
3144 disptable = (
3145 disptable = (
3145 # dispatch table:
3146 # dispatch table:
3146 # file state
3147 # file state
3147 # action
3148 # action
3148 # make backup
3149 # make backup
3149
3150
3150 ## Sets that results that will change file on disk
3151 ## Sets that results that will change file on disk
3151 # Modified compared to target, no local change
3152 # Modified compared to target, no local change
3152 (modified, actions['revert'], discard),
3153 (modified, actions['revert'], discard),
3153 # Modified compared to target, but local file is deleted
3154 # Modified compared to target, but local file is deleted
3154 (deleted, actions['revert'], discard),
3155 (deleted, actions['revert'], discard),
3155 # Modified compared to target, local change
3156 # Modified compared to target, local change
3156 (dsmodified, actions['revert'], dsmodifiedbackup),
3157 (dsmodified, actions['revert'], dsmodifiedbackup),
3157 # Added since target
3158 # Added since target
3158 (added, actions['remove'], discard),
3159 (added, actions['remove'], discard),
3159 # Added in working directory
3160 # Added in working directory
3160 (dsadded, actions['forget'], discard),
3161 (dsadded, actions['forget'], discard),
3161 # Added since target, have local modification
3162 # Added since target, have local modification
3162 (modadded, backupanddel, backup),
3163 (modadded, backupanddel, backup),
3163 # Added since target but file is missing in working directory
3164 # Added since target but file is missing in working directory
3164 (deladded, actions['drop'], discard),
3165 (deladded, actions['drop'], discard),
3165 # Removed since target, before working copy parent
3166 # Removed since target, before working copy parent
3166 (removed, actions['add'], discard),
3167 (removed, actions['add'], discard),
3167 # Same as `removed` but an unknown file exists at the same path
3168 # Same as `removed` but an unknown file exists at the same path
3168 (removunk, actions['add'], check),
3169 (removunk, actions['add'], check),
3169 # Removed since targe, marked as such in working copy parent
3170 # Removed since targe, marked as such in working copy parent
3170 (dsremoved, actions['undelete'], discard),
3171 (dsremoved, actions['undelete'], discard),
3171 # Same as `dsremoved` but an unknown file exists at the same path
3172 # Same as `dsremoved` but an unknown file exists at the same path
3172 (dsremovunk, actions['undelete'], check),
3173 (dsremovunk, actions['undelete'], check),
3173 ## the following sets does not result in any file changes
3174 ## the following sets does not result in any file changes
3174 # File with no modification
3175 # File with no modification
3175 (clean, actions['noop'], discard),
3176 (clean, actions['noop'], discard),
3176 # Existing file, not tracked anywhere
3177 # Existing file, not tracked anywhere
3177 (unknown, actions['unknown'], discard),
3178 (unknown, actions['unknown'], discard),
3178 )
3179 )
3179
3180
3180 for abs, (rel, exact) in sorted(names.items()):
3181 for abs, (rel, exact) in sorted(names.items()):
3181 # target file to be touch on disk (relative to cwd)
3182 # target file to be touch on disk (relative to cwd)
3182 target = repo.wjoin(abs)
3183 target = repo.wjoin(abs)
3183 # search the entry in the dispatch table.
3184 # search the entry in the dispatch table.
3184 # if the file is in any of these sets, it was touched in the working
3185 # if the file is in any of these sets, it was touched in the working
3185 # directory parent and we are sure it needs to be reverted.
3186 # directory parent and we are sure it needs to be reverted.
3186 for table, (xlist, msg), dobackup in disptable:
3187 for table, (xlist, msg), dobackup in disptable:
3187 if abs not in table:
3188 if abs not in table:
3188 continue
3189 continue
3189 if xlist is not None:
3190 if xlist is not None:
3190 xlist.append(abs)
3191 xlist.append(abs)
3191 if dobackup:
3192 if dobackup:
3192 # If in interactive mode, don't automatically create
3193 # If in interactive mode, don't automatically create
3193 # .orig files (issue4793)
3194 # .orig files (issue4793)
3194 if dobackup == backupinteractive:
3195 if dobackup == backupinteractive:
3195 tobackup.add(abs)
3196 tobackup.add(abs)
3196 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
3197 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
3197 bakname = scmutil.origpath(ui, repo, rel)
3198 bakname = scmutil.origpath(ui, repo, rel)
3198 ui.note(_('saving current version of %s as %s\n') %
3199 ui.note(_('saving current version of %s as %s\n') %
3199 (rel, bakname))
3200 (rel, bakname))
3200 if not opts.get('dry_run'):
3201 if not opts.get('dry_run'):
3201 if interactive:
3202 if interactive:
3202 util.copyfile(target, bakname)
3203 util.copyfile(target, bakname)
3203 else:
3204 else:
3204 util.rename(target, bakname)
3205 util.rename(target, bakname)
3205 if ui.verbose or not exact:
3206 if ui.verbose or not exact:
3206 if not isinstance(msg, basestring):
3207 if not isinstance(msg, basestring):
3207 msg = msg(abs)
3208 msg = msg(abs)
3208 ui.status(msg % rel)
3209 ui.status(msg % rel)
3209 elif exact:
3210 elif exact:
3210 ui.warn(msg % rel)
3211 ui.warn(msg % rel)
3211 break
3212 break
3212
3213
3213 if not opts.get('dry_run'):
3214 if not opts.get('dry_run'):
3214 needdata = ('revert', 'add', 'undelete')
3215 needdata = ('revert', 'add', 'undelete')
3215 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3216 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3216 _performrevert(repo, parents, ctx, actions, interactive, tobackup)
3217 _performrevert(repo, parents, ctx, actions, interactive, tobackup)
3217
3218
3218 if targetsubs:
3219 if targetsubs:
3219 # Revert the subrepos on the revert list
3220 # Revert the subrepos on the revert list
3220 for sub in targetsubs:
3221 for sub in targetsubs:
3221 try:
3222 try:
3222 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3223 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3223 except KeyError:
3224 except KeyError:
3224 raise error.Abort("subrepository '%s' does not exist in %s!"
3225 raise error.Abort("subrepository '%s' does not exist in %s!"
3225 % (sub, short(ctx.node())))
3226 % (sub, short(ctx.node())))
3226
3227
3227 def _revertprefetch(repo, ctx, *files):
3228 def _revertprefetch(repo, ctx, *files):
3228 """Let extension changing the storage layer prefetch content"""
3229 """Let extension changing the storage layer prefetch content"""
3229 pass
3230 pass
3230
3231
3231 def _performrevert(repo, parents, ctx, actions, interactive=False,
3232 def _performrevert(repo, parents, ctx, actions, interactive=False,
3232 tobackup=None):
3233 tobackup=None):
3233 """function that actually perform all the actions computed for revert
3234 """function that actually perform all the actions computed for revert
3234
3235
3235 This is an independent function to let extension to plug in and react to
3236 This is an independent function to let extension to plug in and react to
3236 the imminent revert.
3237 the imminent revert.
3237
3238
3238 Make sure you have the working directory locked when calling this function.
3239 Make sure you have the working directory locked when calling this function.
3239 """
3240 """
3240 parent, p2 = parents
3241 parent, p2 = parents
3241 node = ctx.node()
3242 node = ctx.node()
3242 excluded_files = []
3243 excluded_files = []
3243 matcher_opts = {"exclude": excluded_files}
3244 matcher_opts = {"exclude": excluded_files}
3244
3245
3245 def checkout(f):
3246 def checkout(f):
3246 fc = ctx[f]
3247 fc = ctx[f]
3247 repo.wwrite(f, fc.data(), fc.flags())
3248 repo.wwrite(f, fc.data(), fc.flags())
3248
3249
3249 audit_path = pathutil.pathauditor(repo.root)
3250 audit_path = pathutil.pathauditor(repo.root)
3250 for f in actions['forget'][0]:
3251 for f in actions['forget'][0]:
3251 if interactive:
3252 if interactive:
3252 choice = \
3253 choice = \
3253 repo.ui.promptchoice(
3254 repo.ui.promptchoice(
3254 _("forget added file %s (yn)?$$ &Yes $$ &No")
3255 _("forget added file %s (yn)?$$ &Yes $$ &No")
3255 % f)
3256 % f)
3256 if choice == 0:
3257 if choice == 0:
3257 repo.dirstate.drop(f)
3258 repo.dirstate.drop(f)
3258 else:
3259 else:
3259 excluded_files.append(repo.wjoin(f))
3260 excluded_files.append(repo.wjoin(f))
3260 else:
3261 else:
3261 repo.dirstate.drop(f)
3262 repo.dirstate.drop(f)
3262 for f in actions['remove'][0]:
3263 for f in actions['remove'][0]:
3263 audit_path(f)
3264 audit_path(f)
3264 try:
3265 try:
3265 util.unlinkpath(repo.wjoin(f))
3266 util.unlinkpath(repo.wjoin(f))
3266 except OSError:
3267 except OSError:
3267 pass
3268 pass
3268 repo.dirstate.remove(f)
3269 repo.dirstate.remove(f)
3269 for f in actions['drop'][0]:
3270 for f in actions['drop'][0]:
3270 audit_path(f)
3271 audit_path(f)
3271 repo.dirstate.remove(f)
3272 repo.dirstate.remove(f)
3272
3273
3273 normal = None
3274 normal = None
3274 if node == parent:
3275 if node == parent:
3275 # We're reverting to our parent. If possible, we'd like status
3276 # We're reverting to our parent. If possible, we'd like status
3276 # to report the file as clean. We have to use normallookup for
3277 # to report the file as clean. We have to use normallookup for
3277 # merges to avoid losing information about merged/dirty files.
3278 # merges to avoid losing information about merged/dirty files.
3278 if p2 != nullid:
3279 if p2 != nullid:
3279 normal = repo.dirstate.normallookup
3280 normal = repo.dirstate.normallookup
3280 else:
3281 else:
3281 normal = repo.dirstate.normal
3282 normal = repo.dirstate.normal
3282
3283
3283 newlyaddedandmodifiedfiles = set()
3284 newlyaddedandmodifiedfiles = set()
3284 if interactive:
3285 if interactive:
3285 # Prompt the user for changes to revert
3286 # Prompt the user for changes to revert
3286 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3287 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3287 m = scmutil.match(ctx, torevert, matcher_opts)
3288 m = scmutil.match(ctx, torevert, matcher_opts)
3288 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3289 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3289 diffopts.nodates = True
3290 diffopts.nodates = True
3290 diffopts.git = True
3291 diffopts.git = True
3291 reversehunks = repo.ui.configbool('experimental',
3292 reversehunks = repo.ui.configbool('experimental',
3292 'revertalternateinteractivemode',
3293 'revertalternateinteractivemode',
3293 True)
3294 True)
3294 if reversehunks:
3295 if reversehunks:
3295 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3296 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3296 else:
3297 else:
3297 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3298 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3298 originalchunks = patch.parsepatch(diff)
3299 originalchunks = patch.parsepatch(diff)
3299 operation = 'discard' if node == parent else 'revert'
3300 operation = 'discard' if node == parent else 'revert'
3300
3301
3301 try:
3302 try:
3302
3303
3303 chunks, opts = recordfilter(repo.ui, originalchunks,
3304 chunks, opts = recordfilter(repo.ui, originalchunks,
3304 operation=operation)
3305 operation=operation)
3305 if reversehunks:
3306 if reversehunks:
3306 chunks = patch.reversehunks(chunks)
3307 chunks = patch.reversehunks(chunks)
3307
3308
3308 except patch.PatchError as err:
3309 except patch.PatchError as err:
3309 raise error.Abort(_('error parsing patch: %s') % err)
3310 raise error.Abort(_('error parsing patch: %s') % err)
3310
3311
3311 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3312 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3312 if tobackup is None:
3313 if tobackup is None:
3313 tobackup = set()
3314 tobackup = set()
3314 # Apply changes
3315 # Apply changes
3315 fp = stringio()
3316 fp = stringio()
3316 for c in chunks:
3317 for c in chunks:
3317 # Create a backup file only if this hunk should be backed up
3318 # Create a backup file only if this hunk should be backed up
3318 if ishunk(c) and c.header.filename() in tobackup:
3319 if ishunk(c) and c.header.filename() in tobackup:
3319 abs = c.header.filename()
3320 abs = c.header.filename()
3320 target = repo.wjoin(abs)
3321 target = repo.wjoin(abs)
3321 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3322 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3322 util.copyfile(target, bakname)
3323 util.copyfile(target, bakname)
3323 tobackup.remove(abs)
3324 tobackup.remove(abs)
3324 c.write(fp)
3325 c.write(fp)
3325 dopatch = fp.tell()
3326 dopatch = fp.tell()
3326 fp.seek(0)
3327 fp.seek(0)
3327 if dopatch:
3328 if dopatch:
3328 try:
3329 try:
3329 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3330 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3330 except patch.PatchError as err:
3331 except patch.PatchError as err:
3331 raise error.Abort(str(err))
3332 raise error.Abort(str(err))
3332 del fp
3333 del fp
3333 else:
3334 else:
3334 for f in actions['revert'][0]:
3335 for f in actions['revert'][0]:
3335 checkout(f)
3336 checkout(f)
3336 if normal:
3337 if normal:
3337 normal(f)
3338 normal(f)
3338
3339
3339 for f in actions['add'][0]:
3340 for f in actions['add'][0]:
3340 # Don't checkout modified files, they are already created by the diff
3341 # Don't checkout modified files, they are already created by the diff
3341 if f not in newlyaddedandmodifiedfiles:
3342 if f not in newlyaddedandmodifiedfiles:
3342 checkout(f)
3343 checkout(f)
3343 repo.dirstate.add(f)
3344 repo.dirstate.add(f)
3344
3345
3345 normal = repo.dirstate.normallookup
3346 normal = repo.dirstate.normallookup
3346 if node == parent and p2 == nullid:
3347 if node == parent and p2 == nullid:
3347 normal = repo.dirstate.normal
3348 normal = repo.dirstate.normal
3348 for f in actions['undelete'][0]:
3349 for f in actions['undelete'][0]:
3349 checkout(f)
3350 checkout(f)
3350 normal(f)
3351 normal(f)
3351
3352
3352 copied = copies.pathcopies(repo[parent], ctx)
3353 copied = copies.pathcopies(repo[parent], ctx)
3353
3354
3354 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3355 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3355 if f in copied:
3356 if f in copied:
3356 repo.dirstate.copy(copied[f], f)
3357 repo.dirstate.copy(copied[f], f)
3357
3358
3358 def command(table):
3359 def command(table):
3359 """Returns a function object to be used as a decorator for making commands.
3360 """Returns a function object to be used as a decorator for making commands.
3360
3361
3361 This function receives a command table as its argument. The table should
3362 This function receives a command table as its argument. The table should
3362 be a dict.
3363 be a dict.
3363
3364
3364 The returned function can be used as a decorator for adding commands
3365 The returned function can be used as a decorator for adding commands
3365 to that command table. This function accepts multiple arguments to define
3366 to that command table. This function accepts multiple arguments to define
3366 a command.
3367 a command.
3367
3368
3368 The first argument is the command name.
3369 The first argument is the command name.
3369
3370
3370 The options argument is an iterable of tuples defining command arguments.
3371 The options argument is an iterable of tuples defining command arguments.
3371 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
3372 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
3372
3373
3373 The synopsis argument defines a short, one line summary of how to use the
3374 The synopsis argument defines a short, one line summary of how to use the
3374 command. This shows up in the help output.
3375 command. This shows up in the help output.
3375
3376
3376 The norepo argument defines whether the command does not require a
3377 The norepo argument defines whether the command does not require a
3377 local repository. Most commands operate against a repository, thus the
3378 local repository. Most commands operate against a repository, thus the
3378 default is False.
3379 default is False.
3379
3380
3380 The optionalrepo argument defines whether the command optionally requires
3381 The optionalrepo argument defines whether the command optionally requires
3381 a local repository.
3382 a local repository.
3382
3383
3383 The inferrepo argument defines whether to try to find a repository from the
3384 The inferrepo argument defines whether to try to find a repository from the
3384 command line arguments. If True, arguments will be examined for potential
3385 command line arguments. If True, arguments will be examined for potential
3385 repository locations. See ``findrepo()``. If a repository is found, it
3386 repository locations. See ``findrepo()``. If a repository is found, it
3386 will be used.
3387 will be used.
3387 """
3388 """
3388 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
3389 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
3389 inferrepo=False):
3390 inferrepo=False):
3390 def decorator(func):
3391 def decorator(func):
3391 func.norepo = norepo
3392 func.norepo = norepo
3392 func.optionalrepo = optionalrepo
3393 func.optionalrepo = optionalrepo
3393 func.inferrepo = inferrepo
3394 func.inferrepo = inferrepo
3394 if synopsis:
3395 if synopsis:
3395 table[name] = func, list(options), synopsis
3396 table[name] = func, list(options), synopsis
3396 else:
3397 else:
3397 table[name] = func, list(options)
3398 table[name] = func, list(options)
3398 return func
3399 return func
3399 return decorator
3400 return decorator
3400
3401
3401 return cmd
3402 return cmd
3402
3403
3403 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3404 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3404 # commands.outgoing. "missing" is "missing" of the result of
3405 # commands.outgoing. "missing" is "missing" of the result of
3405 # "findcommonoutgoing()"
3406 # "findcommonoutgoing()"
3406 outgoinghooks = util.hooks()
3407 outgoinghooks = util.hooks()
3407
3408
3408 # a list of (ui, repo) functions called by commands.summary
3409 # a list of (ui, repo) functions called by commands.summary
3409 summaryhooks = util.hooks()
3410 summaryhooks = util.hooks()
3410
3411
3411 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3412 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3412 #
3413 #
3413 # functions should return tuple of booleans below, if 'changes' is None:
3414 # functions should return tuple of booleans below, if 'changes' is None:
3414 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3415 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3415 #
3416 #
3416 # otherwise, 'changes' is a tuple of tuples below:
3417 # otherwise, 'changes' is a tuple of tuples below:
3417 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3418 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3418 # - (desturl, destbranch, destpeer, outgoing)
3419 # - (desturl, destbranch, destpeer, outgoing)
3419 summaryremotehooks = util.hooks()
3420 summaryremotehooks = util.hooks()
3420
3421
3421 # A list of state files kept by multistep operations like graft.
3422 # A list of state files kept by multistep operations like graft.
3422 # Since graft cannot be aborted, it is considered 'clearable' by update.
3423 # Since graft cannot be aborted, it is considered 'clearable' by update.
3423 # note: bisect is intentionally excluded
3424 # note: bisect is intentionally excluded
3424 # (state file, clearable, allowcommit, error, hint)
3425 # (state file, clearable, allowcommit, error, hint)
3425 unfinishedstates = [
3426 unfinishedstates = [
3426 ('graftstate', True, False, _('graft in progress'),
3427 ('graftstate', True, False, _('graft in progress'),
3427 _("use 'hg graft --continue' or 'hg update' to abort")),
3428 _("use 'hg graft --continue' or 'hg update' to abort")),
3428 ('updatestate', True, False, _('last update was interrupted'),
3429 ('updatestate', True, False, _('last update was interrupted'),
3429 _("use 'hg update' to get a consistent checkout"))
3430 _("use 'hg update' to get a consistent checkout"))
3430 ]
3431 ]
3431
3432
3432 def checkunfinished(repo, commit=False):
3433 def checkunfinished(repo, commit=False):
3433 '''Look for an unfinished multistep operation, like graft, and abort
3434 '''Look for an unfinished multistep operation, like graft, and abort
3434 if found. It's probably good to check this right before
3435 if found. It's probably good to check this right before
3435 bailifchanged().
3436 bailifchanged().
3436 '''
3437 '''
3437 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3438 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3438 if commit and allowcommit:
3439 if commit and allowcommit:
3439 continue
3440 continue
3440 if repo.vfs.exists(f):
3441 if repo.vfs.exists(f):
3441 raise error.Abort(msg, hint=hint)
3442 raise error.Abort(msg, hint=hint)
3442
3443
3443 def clearunfinished(repo):
3444 def clearunfinished(repo):
3444 '''Check for unfinished operations (as above), and clear the ones
3445 '''Check for unfinished operations (as above), and clear the ones
3445 that are clearable.
3446 that are clearable.
3446 '''
3447 '''
3447 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3448 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3448 if not clearable and repo.vfs.exists(f):
3449 if not clearable and repo.vfs.exists(f):
3449 raise error.Abort(msg, hint=hint)
3450 raise error.Abort(msg, hint=hint)
3450 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3451 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3451 if clearable and repo.vfs.exists(f):
3452 if clearable and repo.vfs.exists(f):
3452 util.unlink(repo.join(f))
3453 util.unlink(repo.join(f))
3453
3454
3454 afterresolvedstates = [
3455 afterresolvedstates = [
3455 ('graftstate',
3456 ('graftstate',
3456 _('hg graft --continue')),
3457 _('hg graft --continue')),
3457 ]
3458 ]
3458
3459
3459 def howtocontinue(repo):
3460 def howtocontinue(repo):
3460 '''Check for an unfinished operation and return the command to finish
3461 '''Check for an unfinished operation and return the command to finish
3461 it.
3462 it.
3462
3463
3463 afterresolvedstates tupples define a .hg/{file} and the corresponding
3464 afterresolvedstates tupples define a .hg/{file} and the corresponding
3464 command needed to finish it.
3465 command needed to finish it.
3465
3466
3466 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3467 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3467 a boolean.
3468 a boolean.
3468 '''
3469 '''
3469 contmsg = _("continue: %s")
3470 contmsg = _("continue: %s")
3470 for f, msg in afterresolvedstates:
3471 for f, msg in afterresolvedstates:
3471 if repo.vfs.exists(f):
3472 if repo.vfs.exists(f):
3472 return contmsg % msg, True
3473 return contmsg % msg, True
3473 workingctx = repo[None]
3474 workingctx = repo[None]
3474 dirty = any(repo.status()) or any(workingctx.sub(s).dirty()
3475 dirty = any(repo.status()) or any(workingctx.sub(s).dirty()
3475 for s in workingctx.substate)
3476 for s in workingctx.substate)
3476 if dirty:
3477 if dirty:
3477 return contmsg % _("hg commit"), False
3478 return contmsg % _("hg commit"), False
3478 return None, None
3479 return None, None
3479
3480
3480 def checkafterresolved(repo):
3481 def checkafterresolved(repo):
3481 '''Inform the user about the next action after completing hg resolve
3482 '''Inform the user about the next action after completing hg resolve
3482
3483
3483 If there's a matching afterresolvedstates, howtocontinue will yield
3484 If there's a matching afterresolvedstates, howtocontinue will yield
3484 repo.ui.warn as the reporter.
3485 repo.ui.warn as the reporter.
3485
3486
3486 Otherwise, it will yield repo.ui.note.
3487 Otherwise, it will yield repo.ui.note.
3487 '''
3488 '''
3488 msg, warning = howtocontinue(repo)
3489 msg, warning = howtocontinue(repo)
3489 if msg is not None:
3490 if msg is not None:
3490 if warning:
3491 if warning:
3491 repo.ui.warn("%s\n" % msg)
3492 repo.ui.warn("%s\n" % msg)
3492 else:
3493 else:
3493 repo.ui.note("%s\n" % msg)
3494 repo.ui.note("%s\n" % msg)
3494
3495
3495 def wrongtooltocontinue(repo, task):
3496 def wrongtooltocontinue(repo, task):
3496 '''Raise an abort suggesting how to properly continue if there is an
3497 '''Raise an abort suggesting how to properly continue if there is an
3497 active task.
3498 active task.
3498
3499
3499 Uses howtocontinue() to find the active task.
3500 Uses howtocontinue() to find the active task.
3500
3501
3501 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3502 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3502 a hint.
3503 a hint.
3503 '''
3504 '''
3504 after = howtocontinue(repo)
3505 after = howtocontinue(repo)
3505 hint = None
3506 hint = None
3506 if after[1]:
3507 if after[1]:
3507 hint = after[0]
3508 hint = after[0]
3508 raise error.Abort(_('no %s in progress') % task, hint=hint)
3509 raise error.Abort(_('no %s in progress') % task, hint=hint)
3509
3510
3510 class dirstateguard(object):
3511 class dirstateguard(object):
3511 '''Restore dirstate at unexpected failure.
3512 '''Restore dirstate at unexpected failure.
3512
3513
3513 At the construction, this class does:
3514 At the construction, this class does:
3514
3515
3515 - write current ``repo.dirstate`` out, and
3516 - write current ``repo.dirstate`` out, and
3516 - save ``.hg/dirstate`` into the backup file
3517 - save ``.hg/dirstate`` into the backup file
3517
3518
3518 This restores ``.hg/dirstate`` from backup file, if ``release()``
3519 This restores ``.hg/dirstate`` from backup file, if ``release()``
3519 is invoked before ``close()``.
3520 is invoked before ``close()``.
3520
3521
3521 This just removes the backup file at ``close()`` before ``release()``.
3522 This just removes the backup file at ``close()`` before ``release()``.
3522 '''
3523 '''
3523
3524
3524 def __init__(self, repo, name):
3525 def __init__(self, repo, name):
3525 self._repo = repo
3526 self._repo = repo
3526 self._suffix = '.backup.%s.%d' % (name, id(self))
3527 self._suffix = '.backup.%s.%d' % (name, id(self))
3527 repo.dirstate.savebackup(repo.currenttransaction(), self._suffix)
3528 repo.dirstate.savebackup(repo.currenttransaction(), self._suffix)
3528 self._active = True
3529 self._active = True
3529 self._closed = False
3530 self._closed = False
3530
3531
3531 def __del__(self):
3532 def __del__(self):
3532 if self._active: # still active
3533 if self._active: # still active
3533 # this may occur, even if this class is used correctly:
3534 # this may occur, even if this class is used correctly:
3534 # for example, releasing other resources like transaction
3535 # for example, releasing other resources like transaction
3535 # may raise exception before ``dirstateguard.release`` in
3536 # may raise exception before ``dirstateguard.release`` in
3536 # ``release(tr, ....)``.
3537 # ``release(tr, ....)``.
3537 self._abort()
3538 self._abort()
3538
3539
3539 def close(self):
3540 def close(self):
3540 if not self._active: # already inactivated
3541 if not self._active: # already inactivated
3541 msg = (_("can't close already inactivated backup: dirstate%s")
3542 msg = (_("can't close already inactivated backup: dirstate%s")
3542 % self._suffix)
3543 % self._suffix)
3543 raise error.Abort(msg)
3544 raise error.Abort(msg)
3544
3545
3545 self._repo.dirstate.clearbackup(self._repo.currenttransaction(),
3546 self._repo.dirstate.clearbackup(self._repo.currenttransaction(),
3546 self._suffix)
3547 self._suffix)
3547 self._active = False
3548 self._active = False
3548 self._closed = True
3549 self._closed = True
3549
3550
3550 def _abort(self):
3551 def _abort(self):
3551 self._repo.dirstate.restorebackup(self._repo.currenttransaction(),
3552 self._repo.dirstate.restorebackup(self._repo.currenttransaction(),
3552 self._suffix)
3553 self._suffix)
3553 self._active = False
3554 self._active = False
3554
3555
3555 def release(self):
3556 def release(self):
3556 if not self._closed:
3557 if not self._closed:
3557 if not self._active: # already inactivated
3558 if not self._active: # already inactivated
3558 msg = (_("can't release already inactivated backup:"
3559 msg = (_("can't release already inactivated backup:"
3559 " dirstate%s")
3560 " dirstate%s")
3560 % self._suffix)
3561 % self._suffix)
3561 raise error.Abort(msg)
3562 raise error.Abort(msg)
3562 self._abort()
3563 self._abort()
@@ -1,7279 +1,7282 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-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 __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import difflib
10 import difflib
11 import errno
11 import errno
12 import operator
12 import operator
13 import os
13 import os
14 import random
14 import random
15 import re
15 import re
16 import shlex
16 import shlex
17 import socket
17 import socket
18 import sys
18 import sys
19 import tempfile
19 import tempfile
20 import time
20 import time
21
21
22 from .i18n import _
22 from .i18n import _
23 from .node import (
23 from .node import (
24 bin,
24 bin,
25 hex,
25 hex,
26 nullhex,
26 nullhex,
27 nullid,
27 nullid,
28 nullrev,
28 nullrev,
29 short,
29 short,
30 )
30 )
31 from . import (
31 from . import (
32 archival,
32 archival,
33 bookmarks,
33 bookmarks,
34 bundle2,
34 bundle2,
35 changegroup,
35 changegroup,
36 cmdutil,
36 cmdutil,
37 commandserver,
37 commandserver,
38 context,
38 context,
39 copies,
39 copies,
40 dagparser,
40 dagparser,
41 dagutil,
41 dagutil,
42 destutil,
42 destutil,
43 discovery,
43 discovery,
44 encoding,
44 encoding,
45 error,
45 error,
46 exchange,
46 exchange,
47 extensions,
47 extensions,
48 fileset,
48 fileset,
49 formatter,
49 formatter,
50 graphmod,
50 graphmod,
51 hbisect,
51 hbisect,
52 help,
52 help,
53 hg,
53 hg,
54 hgweb,
54 hgweb,
55 localrepo,
55 localrepo,
56 lock as lockmod,
56 lock as lockmod,
57 merge as mergemod,
57 merge as mergemod,
58 minirst,
58 minirst,
59 obsolete,
59 obsolete,
60 patch,
60 patch,
61 phases,
61 phases,
62 policy,
62 policy,
63 pvec,
63 pvec,
64 repair,
64 repair,
65 revlog,
65 revlog,
66 revset,
66 revset,
67 scmutil,
67 scmutil,
68 setdiscovery,
68 setdiscovery,
69 simplemerge,
69 simplemerge,
70 sshserver,
70 sshserver,
71 streamclone,
71 streamclone,
72 templatekw,
72 templatekw,
73 templater,
73 templater,
74 treediscovery,
74 treediscovery,
75 ui as uimod,
75 ui as uimod,
76 util,
76 util,
77 )
77 )
78
78
79 release = lockmod.release
79 release = lockmod.release
80
80
81 table = {}
81 table = {}
82
82
83 command = cmdutil.command(table)
83 command = cmdutil.command(table)
84
84
85 # label constants
85 # label constants
86 # until 3.5, bookmarks.current was the advertised name, not
86 # until 3.5, bookmarks.current was the advertised name, not
87 # bookmarks.active, so we must use both to avoid breaking old
87 # bookmarks.active, so we must use both to avoid breaking old
88 # custom styles
88 # custom styles
89 activebookmarklabel = 'bookmarks.active bookmarks.current'
89 activebookmarklabel = 'bookmarks.active bookmarks.current'
90
90
91 # common command options
91 # common command options
92
92
93 globalopts = [
93 globalopts = [
94 ('R', 'repository', '',
94 ('R', 'repository', '',
95 _('repository root directory or name of overlay bundle file'),
95 _('repository root directory or name of overlay bundle file'),
96 _('REPO')),
96 _('REPO')),
97 ('', 'cwd', '',
97 ('', 'cwd', '',
98 _('change working directory'), _('DIR')),
98 _('change working directory'), _('DIR')),
99 ('y', 'noninteractive', None,
99 ('y', 'noninteractive', None,
100 _('do not prompt, automatically pick the first choice for all prompts')),
100 _('do not prompt, automatically pick the first choice for all prompts')),
101 ('q', 'quiet', None, _('suppress output')),
101 ('q', 'quiet', None, _('suppress output')),
102 ('v', 'verbose', None, _('enable additional output')),
102 ('v', 'verbose', None, _('enable additional output')),
103 ('', 'config', [],
103 ('', 'config', [],
104 _('set/override config option (use \'section.name=value\')'),
104 _('set/override config option (use \'section.name=value\')'),
105 _('CONFIG')),
105 _('CONFIG')),
106 ('', 'debug', None, _('enable debugging output')),
106 ('', 'debug', None, _('enable debugging output')),
107 ('', 'debugger', None, _('start debugger')),
107 ('', 'debugger', None, _('start debugger')),
108 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
108 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
109 _('ENCODE')),
109 _('ENCODE')),
110 ('', 'encodingmode', encoding.encodingmode,
110 ('', 'encodingmode', encoding.encodingmode,
111 _('set the charset encoding mode'), _('MODE')),
111 _('set the charset encoding mode'), _('MODE')),
112 ('', 'traceback', None, _('always print a traceback on exception')),
112 ('', 'traceback', None, _('always print a traceback on exception')),
113 ('', 'time', None, _('time how long the command takes')),
113 ('', 'time', None, _('time how long the command takes')),
114 ('', 'profile', None, _('print command execution profile')),
114 ('', 'profile', None, _('print command execution profile')),
115 ('', 'version', None, _('output version information and exit')),
115 ('', 'version', None, _('output version information and exit')),
116 ('h', 'help', None, _('display help and exit')),
116 ('h', 'help', None, _('display help and exit')),
117 ('', 'hidden', False, _('consider hidden changesets')),
117 ('', 'hidden', False, _('consider hidden changesets')),
118 ]
118 ]
119
119
120 dryrunopts = [('n', 'dry-run', None,
120 dryrunopts = [('n', 'dry-run', None,
121 _('do not perform actions, just print output'))]
121 _('do not perform actions, just print output'))]
122
122
123 remoteopts = [
123 remoteopts = [
124 ('e', 'ssh', '',
124 ('e', 'ssh', '',
125 _('specify ssh command to use'), _('CMD')),
125 _('specify ssh command to use'), _('CMD')),
126 ('', 'remotecmd', '',
126 ('', 'remotecmd', '',
127 _('specify hg command to run on the remote side'), _('CMD')),
127 _('specify hg command to run on the remote side'), _('CMD')),
128 ('', 'insecure', None,
128 ('', 'insecure', None,
129 _('do not verify server certificate (ignoring web.cacerts config)')),
129 _('do not verify server certificate (ignoring web.cacerts config)')),
130 ]
130 ]
131
131
132 walkopts = [
132 walkopts = [
133 ('I', 'include', [],
133 ('I', 'include', [],
134 _('include names matching the given patterns'), _('PATTERN')),
134 _('include names matching the given patterns'), _('PATTERN')),
135 ('X', 'exclude', [],
135 ('X', 'exclude', [],
136 _('exclude names matching the given patterns'), _('PATTERN')),
136 _('exclude names matching the given patterns'), _('PATTERN')),
137 ]
137 ]
138
138
139 commitopts = [
139 commitopts = [
140 ('m', 'message', '',
140 ('m', 'message', '',
141 _('use text as commit message'), _('TEXT')),
141 _('use text as commit message'), _('TEXT')),
142 ('l', 'logfile', '',
142 ('l', 'logfile', '',
143 _('read commit message from file'), _('FILE')),
143 _('read commit message from file'), _('FILE')),
144 ]
144 ]
145
145
146 commitopts2 = [
146 commitopts2 = [
147 ('d', 'date', '',
147 ('d', 'date', '',
148 _('record the specified date as commit date'), _('DATE')),
148 _('record the specified date as commit date'), _('DATE')),
149 ('u', 'user', '',
149 ('u', 'user', '',
150 _('record the specified user as committer'), _('USER')),
150 _('record the specified user as committer'), _('USER')),
151 ]
151 ]
152
152
153 # hidden for now
153 # hidden for now
154 formatteropts = [
154 formatteropts = [
155 ('T', 'template', '',
155 ('T', 'template', '',
156 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
156 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
157 ]
157 ]
158
158
159 templateopts = [
159 templateopts = [
160 ('', 'style', '',
160 ('', 'style', '',
161 _('display using template map file (DEPRECATED)'), _('STYLE')),
161 _('display using template map file (DEPRECATED)'), _('STYLE')),
162 ('T', 'template', '',
162 ('T', 'template', '',
163 _('display with template'), _('TEMPLATE')),
163 _('display with template'), _('TEMPLATE')),
164 ]
164 ]
165
165
166 logopts = [
166 logopts = [
167 ('p', 'patch', None, _('show patch')),
167 ('p', 'patch', None, _('show patch')),
168 ('g', 'git', None, _('use git extended diff format')),
168 ('g', 'git', None, _('use git extended diff format')),
169 ('l', 'limit', '',
169 ('l', 'limit', '',
170 _('limit number of changes displayed'), _('NUM')),
170 _('limit number of changes displayed'), _('NUM')),
171 ('M', 'no-merges', None, _('do not show merges')),
171 ('M', 'no-merges', None, _('do not show merges')),
172 ('', 'stat', None, _('output diffstat-style summary of changes')),
172 ('', 'stat', None, _('output diffstat-style summary of changes')),
173 ('G', 'graph', None, _("show the revision DAG")),
173 ('G', 'graph', None, _("show the revision DAG")),
174 ] + templateopts
174 ] + templateopts
175
175
176 diffopts = [
176 diffopts = [
177 ('a', 'text', None, _('treat all files as text')),
177 ('a', 'text', None, _('treat all files as text')),
178 ('g', 'git', None, _('use git extended diff format')),
178 ('g', 'git', None, _('use git extended diff format')),
179 ('', 'nodates', None, _('omit dates from diff headers'))
179 ('', 'nodates', None, _('omit dates from diff headers'))
180 ]
180 ]
181
181
182 diffwsopts = [
182 diffwsopts = [
183 ('w', 'ignore-all-space', None,
183 ('w', 'ignore-all-space', None,
184 _('ignore white space when comparing lines')),
184 _('ignore white space when comparing lines')),
185 ('b', 'ignore-space-change', None,
185 ('b', 'ignore-space-change', None,
186 _('ignore changes in the amount of white space')),
186 _('ignore changes in the amount of white space')),
187 ('B', 'ignore-blank-lines', None,
187 ('B', 'ignore-blank-lines', None,
188 _('ignore changes whose lines are all blank')),
188 _('ignore changes whose lines are all blank')),
189 ]
189 ]
190
190
191 diffopts2 = [
191 diffopts2 = [
192 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
192 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
193 ('p', 'show-function', None, _('show which function each change is in')),
193 ('p', 'show-function', None, _('show which function each change is in')),
194 ('', 'reverse', None, _('produce a diff that undoes the changes')),
194 ('', 'reverse', None, _('produce a diff that undoes the changes')),
195 ] + diffwsopts + [
195 ] + diffwsopts + [
196 ('U', 'unified', '',
196 ('U', 'unified', '',
197 _('number of lines of context to show'), _('NUM')),
197 _('number of lines of context to show'), _('NUM')),
198 ('', 'stat', None, _('output diffstat-style summary of changes')),
198 ('', 'stat', None, _('output diffstat-style summary of changes')),
199 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
199 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
200 ]
200 ]
201
201
202 mergetoolopts = [
202 mergetoolopts = [
203 ('t', 'tool', '', _('specify merge tool')),
203 ('t', 'tool', '', _('specify merge tool')),
204 ]
204 ]
205
205
206 similarityopts = [
206 similarityopts = [
207 ('s', 'similarity', '',
207 ('s', 'similarity', '',
208 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
208 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
209 ]
209 ]
210
210
211 subrepoopts = [
211 subrepoopts = [
212 ('S', 'subrepos', None,
212 ('S', 'subrepos', None,
213 _('recurse into subrepositories'))
213 _('recurse into subrepositories'))
214 ]
214 ]
215
215
216 debugrevlogopts = [
216 debugrevlogopts = [
217 ('c', 'changelog', False, _('open changelog')),
217 ('c', 'changelog', False, _('open changelog')),
218 ('m', 'manifest', False, _('open manifest')),
218 ('m', 'manifest', False, _('open manifest')),
219 ('', 'dir', '', _('open directory manifest')),
219 ('', 'dir', '', _('open directory manifest')),
220 ]
220 ]
221
221
222 # Commands start here, listed alphabetically
222 # Commands start here, listed alphabetically
223
223
224 @command('^add',
224 @command('^add',
225 walkopts + subrepoopts + dryrunopts,
225 walkopts + subrepoopts + dryrunopts,
226 _('[OPTION]... [FILE]...'),
226 _('[OPTION]... [FILE]...'),
227 inferrepo=True)
227 inferrepo=True)
228 def add(ui, repo, *pats, **opts):
228 def add(ui, repo, *pats, **opts):
229 """add the specified files on the next commit
229 """add the specified files on the next commit
230
230
231 Schedule files to be version controlled and added to the
231 Schedule files to be version controlled and added to the
232 repository.
232 repository.
233
233
234 The files will be added to the repository at the next commit. To
234 The files will be added to the repository at the next commit. To
235 undo an add before that, see :hg:`forget`.
235 undo an add before that, see :hg:`forget`.
236
236
237 If no names are given, add all files to the repository (except
237 If no names are given, add all files to the repository (except
238 files matching ``.hgignore``).
238 files matching ``.hgignore``).
239
239
240 .. container:: verbose
240 .. container:: verbose
241
241
242 Examples:
242 Examples:
243
243
244 - New (unknown) files are added
244 - New (unknown) files are added
245 automatically by :hg:`add`::
245 automatically by :hg:`add`::
246
246
247 $ ls
247 $ ls
248 foo.c
248 foo.c
249 $ hg status
249 $ hg status
250 ? foo.c
250 ? foo.c
251 $ hg add
251 $ hg add
252 adding foo.c
252 adding foo.c
253 $ hg status
253 $ hg status
254 A foo.c
254 A foo.c
255
255
256 - Specific files to be added can be specified::
256 - Specific files to be added can be specified::
257
257
258 $ ls
258 $ ls
259 bar.c foo.c
259 bar.c foo.c
260 $ hg status
260 $ hg status
261 ? bar.c
261 ? bar.c
262 ? foo.c
262 ? foo.c
263 $ hg add bar.c
263 $ hg add bar.c
264 $ hg status
264 $ hg status
265 A bar.c
265 A bar.c
266 ? foo.c
266 ? foo.c
267
267
268 Returns 0 if all files are successfully added.
268 Returns 0 if all files are successfully added.
269 """
269 """
270
270
271 m = scmutil.match(repo[None], pats, opts)
271 m = scmutil.match(repo[None], pats, opts)
272 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
272 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
273 return rejected and 1 or 0
273 return rejected and 1 or 0
274
274
275 @command('addremove',
275 @command('addremove',
276 similarityopts + subrepoopts + walkopts + dryrunopts,
276 similarityopts + subrepoopts + walkopts + dryrunopts,
277 _('[OPTION]... [FILE]...'),
277 _('[OPTION]... [FILE]...'),
278 inferrepo=True)
278 inferrepo=True)
279 def addremove(ui, repo, *pats, **opts):
279 def addremove(ui, repo, *pats, **opts):
280 """add all new files, delete all missing files
280 """add all new files, delete all missing files
281
281
282 Add all new files and remove all missing files from the
282 Add all new files and remove all missing files from the
283 repository.
283 repository.
284
284
285 Unless names are given, new files are ignored if they match any of
285 Unless names are given, new files are ignored if they match any of
286 the patterns in ``.hgignore``. As with add, these changes take
286 the patterns in ``.hgignore``. As with add, these changes take
287 effect at the next commit.
287 effect at the next commit.
288
288
289 Use the -s/--similarity option to detect renamed files. This
289 Use the -s/--similarity option to detect renamed files. This
290 option takes a percentage between 0 (disabled) and 100 (files must
290 option takes a percentage between 0 (disabled) and 100 (files must
291 be identical) as its parameter. With a parameter greater than 0,
291 be identical) as its parameter. With a parameter greater than 0,
292 this compares every removed file with every added file and records
292 this compares every removed file with every added file and records
293 those similar enough as renames. Detecting renamed files this way
293 those similar enough as renames. Detecting renamed files this way
294 can be expensive. After using this option, :hg:`status -C` can be
294 can be expensive. After using this option, :hg:`status -C` can be
295 used to check which files were identified as moved or renamed. If
295 used to check which files were identified as moved or renamed. If
296 not specified, -s/--similarity defaults to 100 and only renames of
296 not specified, -s/--similarity defaults to 100 and only renames of
297 identical files are detected.
297 identical files are detected.
298
298
299 .. container:: verbose
299 .. container:: verbose
300
300
301 Examples:
301 Examples:
302
302
303 - A number of files (bar.c and foo.c) are new,
303 - A number of files (bar.c and foo.c) are new,
304 while foobar.c has been removed (without using :hg:`remove`)
304 while foobar.c has been removed (without using :hg:`remove`)
305 from the repository::
305 from the repository::
306
306
307 $ ls
307 $ ls
308 bar.c foo.c
308 bar.c foo.c
309 $ hg status
309 $ hg status
310 ! foobar.c
310 ! foobar.c
311 ? bar.c
311 ? bar.c
312 ? foo.c
312 ? foo.c
313 $ hg addremove
313 $ hg addremove
314 adding bar.c
314 adding bar.c
315 adding foo.c
315 adding foo.c
316 removing foobar.c
316 removing foobar.c
317 $ hg status
317 $ hg status
318 A bar.c
318 A bar.c
319 A foo.c
319 A foo.c
320 R foobar.c
320 R foobar.c
321
321
322 - A file foobar.c was moved to foo.c without using :hg:`rename`.
322 - A file foobar.c was moved to foo.c without using :hg:`rename`.
323 Afterwards, it was edited slightly::
323 Afterwards, it was edited slightly::
324
324
325 $ ls
325 $ ls
326 foo.c
326 foo.c
327 $ hg status
327 $ hg status
328 ! foobar.c
328 ! foobar.c
329 ? foo.c
329 ? foo.c
330 $ hg addremove --similarity 90
330 $ hg addremove --similarity 90
331 removing foobar.c
331 removing foobar.c
332 adding foo.c
332 adding foo.c
333 recording removal of foobar.c as rename to foo.c (94% similar)
333 recording removal of foobar.c as rename to foo.c (94% similar)
334 $ hg status -C
334 $ hg status -C
335 A foo.c
335 A foo.c
336 foobar.c
336 foobar.c
337 R foobar.c
337 R foobar.c
338
338
339 Returns 0 if all files are successfully added.
339 Returns 0 if all files are successfully added.
340 """
340 """
341 try:
341 try:
342 sim = float(opts.get('similarity') or 100)
342 sim = float(opts.get('similarity') or 100)
343 except ValueError:
343 except ValueError:
344 raise error.Abort(_('similarity must be a number'))
344 raise error.Abort(_('similarity must be a number'))
345 if sim < 0 or sim > 100:
345 if sim < 0 or sim > 100:
346 raise error.Abort(_('similarity must be between 0 and 100'))
346 raise error.Abort(_('similarity must be between 0 and 100'))
347 matcher = scmutil.match(repo[None], pats, opts)
347 matcher = scmutil.match(repo[None], pats, opts)
348 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
348 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
349
349
350 @command('^annotate|blame',
350 @command('^annotate|blame',
351 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
351 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
352 ('', 'follow', None,
352 ('', 'follow', None,
353 _('follow copies/renames and list the filename (DEPRECATED)')),
353 _('follow copies/renames and list the filename (DEPRECATED)')),
354 ('', 'no-follow', None, _("don't follow copies and renames")),
354 ('', 'no-follow', None, _("don't follow copies and renames")),
355 ('a', 'text', None, _('treat all files as text')),
355 ('a', 'text', None, _('treat all files as text')),
356 ('u', 'user', None, _('list the author (long with -v)')),
356 ('u', 'user', None, _('list the author (long with -v)')),
357 ('f', 'file', None, _('list the filename')),
357 ('f', 'file', None, _('list the filename')),
358 ('d', 'date', None, _('list the date (short with -q)')),
358 ('d', 'date', None, _('list the date (short with -q)')),
359 ('n', 'number', None, _('list the revision number (default)')),
359 ('n', 'number', None, _('list the revision number (default)')),
360 ('c', 'changeset', None, _('list the changeset')),
360 ('c', 'changeset', None, _('list the changeset')),
361 ('l', 'line-number', None, _('show line number at the first appearance'))
361 ('l', 'line-number', None, _('show line number at the first appearance'))
362 ] + diffwsopts + walkopts + formatteropts,
362 ] + diffwsopts + walkopts + formatteropts,
363 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
363 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
364 inferrepo=True)
364 inferrepo=True)
365 def annotate(ui, repo, *pats, **opts):
365 def annotate(ui, repo, *pats, **opts):
366 """show changeset information by line for each file
366 """show changeset information by line for each file
367
367
368 List changes in files, showing the revision id responsible for
368 List changes in files, showing the revision id responsible for
369 each line.
369 each line.
370
370
371 This command is useful for discovering when a change was made and
371 This command is useful for discovering when a change was made and
372 by whom.
372 by whom.
373
373
374 If you include --file, --user, or --date, the revision number is
374 If you include --file, --user, or --date, the revision number is
375 suppressed unless you also include --number.
375 suppressed unless you also include --number.
376
376
377 Without the -a/--text option, annotate will avoid processing files
377 Without the -a/--text option, annotate will avoid processing files
378 it detects as binary. With -a, annotate will annotate the file
378 it detects as binary. With -a, annotate will annotate the file
379 anyway, although the results will probably be neither useful
379 anyway, although the results will probably be neither useful
380 nor desirable.
380 nor desirable.
381
381
382 Returns 0 on success.
382 Returns 0 on success.
383 """
383 """
384 if not pats:
384 if not pats:
385 raise error.Abort(_('at least one filename or pattern is required'))
385 raise error.Abort(_('at least one filename or pattern is required'))
386
386
387 if opts.get('follow'):
387 if opts.get('follow'):
388 # --follow is deprecated and now just an alias for -f/--file
388 # --follow is deprecated and now just an alias for -f/--file
389 # to mimic the behavior of Mercurial before version 1.5
389 # to mimic the behavior of Mercurial before version 1.5
390 opts['file'] = True
390 opts['file'] = True
391
391
392 ctx = scmutil.revsingle(repo, opts.get('rev'))
392 ctx = scmutil.revsingle(repo, opts.get('rev'))
393
393
394 fm = ui.formatter('annotate', opts)
394 fm = ui.formatter('annotate', opts)
395 if ui.quiet:
395 if ui.quiet:
396 datefunc = util.shortdate
396 datefunc = util.shortdate
397 else:
397 else:
398 datefunc = util.datestr
398 datefunc = util.datestr
399 if ctx.rev() is None:
399 if ctx.rev() is None:
400 def hexfn(node):
400 def hexfn(node):
401 if node is None:
401 if node is None:
402 return None
402 return None
403 else:
403 else:
404 return fm.hexfunc(node)
404 return fm.hexfunc(node)
405 if opts.get('changeset'):
405 if opts.get('changeset'):
406 # omit "+" suffix which is appended to node hex
406 # omit "+" suffix which is appended to node hex
407 def formatrev(rev):
407 def formatrev(rev):
408 if rev is None:
408 if rev is None:
409 return '%d' % ctx.p1().rev()
409 return '%d' % ctx.p1().rev()
410 else:
410 else:
411 return '%d' % rev
411 return '%d' % rev
412 else:
412 else:
413 def formatrev(rev):
413 def formatrev(rev):
414 if rev is None:
414 if rev is None:
415 return '%d+' % ctx.p1().rev()
415 return '%d+' % ctx.p1().rev()
416 else:
416 else:
417 return '%d ' % rev
417 return '%d ' % rev
418 def formathex(hex):
418 def formathex(hex):
419 if hex is None:
419 if hex is None:
420 return '%s+' % fm.hexfunc(ctx.p1().node())
420 return '%s+' % fm.hexfunc(ctx.p1().node())
421 else:
421 else:
422 return '%s ' % hex
422 return '%s ' % hex
423 else:
423 else:
424 hexfn = fm.hexfunc
424 hexfn = fm.hexfunc
425 formatrev = formathex = str
425 formatrev = formathex = str
426
426
427 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
427 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
428 ('number', ' ', lambda x: x[0].rev(), formatrev),
428 ('number', ' ', lambda x: x[0].rev(), formatrev),
429 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
429 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
430 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
430 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
431 ('file', ' ', lambda x: x[0].path(), str),
431 ('file', ' ', lambda x: x[0].path(), str),
432 ('line_number', ':', lambda x: x[1], str),
432 ('line_number', ':', lambda x: x[1], str),
433 ]
433 ]
434 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
434 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
435
435
436 if (not opts.get('user') and not opts.get('changeset')
436 if (not opts.get('user') and not opts.get('changeset')
437 and not opts.get('date') and not opts.get('file')):
437 and not opts.get('date') and not opts.get('file')):
438 opts['number'] = True
438 opts['number'] = True
439
439
440 linenumber = opts.get('line_number') is not None
440 linenumber = opts.get('line_number') is not None
441 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
441 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
442 raise error.Abort(_('at least one of -n/-c is required for -l'))
442 raise error.Abort(_('at least one of -n/-c is required for -l'))
443
443
444 if fm:
444 if fm:
445 def makefunc(get, fmt):
445 def makefunc(get, fmt):
446 return get
446 return get
447 else:
447 else:
448 def makefunc(get, fmt):
448 def makefunc(get, fmt):
449 return lambda x: fmt(get(x))
449 return lambda x: fmt(get(x))
450 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
450 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
451 if opts.get(op)]
451 if opts.get(op)]
452 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
452 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
453 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
453 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
454 if opts.get(op))
454 if opts.get(op))
455
455
456 def bad(x, y):
456 def bad(x, y):
457 raise error.Abort("%s: %s" % (x, y))
457 raise error.Abort("%s: %s" % (x, y))
458
458
459 m = scmutil.match(ctx, pats, opts, badfn=bad)
459 m = scmutil.match(ctx, pats, opts, badfn=bad)
460
460
461 follow = not opts.get('no_follow')
461 follow = not opts.get('no_follow')
462 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
462 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
463 whitespace=True)
463 whitespace=True)
464 for abs in ctx.walk(m):
464 for abs in ctx.walk(m):
465 fctx = ctx[abs]
465 fctx = ctx[abs]
466 if not opts.get('text') and util.binary(fctx.data()):
466 if not opts.get('text') and util.binary(fctx.data()):
467 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
467 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
468 continue
468 continue
469
469
470 lines = fctx.annotate(follow=follow, linenumber=linenumber,
470 lines = fctx.annotate(follow=follow, linenumber=linenumber,
471 diffopts=diffopts)
471 diffopts=diffopts)
472 if not lines:
472 if not lines:
473 continue
473 continue
474 formats = []
474 formats = []
475 pieces = []
475 pieces = []
476
476
477 for f, sep in funcmap:
477 for f, sep in funcmap:
478 l = [f(n) for n, dummy in lines]
478 l = [f(n) for n, dummy in lines]
479 if fm:
479 if fm:
480 formats.append(['%s' for x in l])
480 formats.append(['%s' for x in l])
481 else:
481 else:
482 sizes = [encoding.colwidth(x) for x in l]
482 sizes = [encoding.colwidth(x) for x in l]
483 ml = max(sizes)
483 ml = max(sizes)
484 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
484 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
485 pieces.append(l)
485 pieces.append(l)
486
486
487 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
487 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
488 fm.startitem()
488 fm.startitem()
489 fm.write(fields, "".join(f), *p)
489 fm.write(fields, "".join(f), *p)
490 fm.write('line', ": %s", l[1])
490 fm.write('line', ": %s", l[1])
491
491
492 if not lines[-1][1].endswith('\n'):
492 if not lines[-1][1].endswith('\n'):
493 fm.plain('\n')
493 fm.plain('\n')
494
494
495 fm.end()
495 fm.end()
496
496
497 @command('archive',
497 @command('archive',
498 [('', 'no-decode', None, _('do not pass files through decoders')),
498 [('', 'no-decode', None, _('do not pass files through decoders')),
499 ('p', 'prefix', '', _('directory prefix for files in archive'),
499 ('p', 'prefix', '', _('directory prefix for files in archive'),
500 _('PREFIX')),
500 _('PREFIX')),
501 ('r', 'rev', '', _('revision to distribute'), _('REV')),
501 ('r', 'rev', '', _('revision to distribute'), _('REV')),
502 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
502 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
503 ] + subrepoopts + walkopts,
503 ] + subrepoopts + walkopts,
504 _('[OPTION]... DEST'))
504 _('[OPTION]... DEST'))
505 def archive(ui, repo, dest, **opts):
505 def archive(ui, repo, dest, **opts):
506 '''create an unversioned archive of a repository revision
506 '''create an unversioned archive of a repository revision
507
507
508 By default, the revision used is the parent of the working
508 By default, the revision used is the parent of the working
509 directory; use -r/--rev to specify a different revision.
509 directory; use -r/--rev to specify a different revision.
510
510
511 The archive type is automatically detected based on file
511 The archive type is automatically detected based on file
512 extension (to override, use -t/--type).
512 extension (to override, use -t/--type).
513
513
514 .. container:: verbose
514 .. container:: verbose
515
515
516 Examples:
516 Examples:
517
517
518 - create a zip file containing the 1.0 release::
518 - create a zip file containing the 1.0 release::
519
519
520 hg archive -r 1.0 project-1.0.zip
520 hg archive -r 1.0 project-1.0.zip
521
521
522 - create a tarball excluding .hg files::
522 - create a tarball excluding .hg files::
523
523
524 hg archive project.tar.gz -X ".hg*"
524 hg archive project.tar.gz -X ".hg*"
525
525
526 Valid types are:
526 Valid types are:
527
527
528 :``files``: a directory full of files (default)
528 :``files``: a directory full of files (default)
529 :``tar``: tar archive, uncompressed
529 :``tar``: tar archive, uncompressed
530 :``tbz2``: tar archive, compressed using bzip2
530 :``tbz2``: tar archive, compressed using bzip2
531 :``tgz``: tar archive, compressed using gzip
531 :``tgz``: tar archive, compressed using gzip
532 :``uzip``: zip archive, uncompressed
532 :``uzip``: zip archive, uncompressed
533 :``zip``: zip archive, compressed using deflate
533 :``zip``: zip archive, compressed using deflate
534
534
535 The exact name of the destination archive or directory is given
535 The exact name of the destination archive or directory is given
536 using a format string; see :hg:`help export` for details.
536 using a format string; see :hg:`help export` for details.
537
537
538 Each member added to an archive file has a directory prefix
538 Each member added to an archive file has a directory prefix
539 prepended. Use -p/--prefix to specify a format string for the
539 prepended. Use -p/--prefix to specify a format string for the
540 prefix. The default is the basename of the archive, with suffixes
540 prefix. The default is the basename of the archive, with suffixes
541 removed.
541 removed.
542
542
543 Returns 0 on success.
543 Returns 0 on success.
544 '''
544 '''
545
545
546 ctx = scmutil.revsingle(repo, opts.get('rev'))
546 ctx = scmutil.revsingle(repo, opts.get('rev'))
547 if not ctx:
547 if not ctx:
548 raise error.Abort(_('no working directory: please specify a revision'))
548 raise error.Abort(_('no working directory: please specify a revision'))
549 node = ctx.node()
549 node = ctx.node()
550 dest = cmdutil.makefilename(repo, dest, node)
550 dest = cmdutil.makefilename(repo, dest, node)
551 if os.path.realpath(dest) == repo.root:
551 if os.path.realpath(dest) == repo.root:
552 raise error.Abort(_('repository root cannot be destination'))
552 raise error.Abort(_('repository root cannot be destination'))
553
553
554 kind = opts.get('type') or archival.guesskind(dest) or 'files'
554 kind = opts.get('type') or archival.guesskind(dest) or 'files'
555 prefix = opts.get('prefix')
555 prefix = opts.get('prefix')
556
556
557 if dest == '-':
557 if dest == '-':
558 if kind == 'files':
558 if kind == 'files':
559 raise error.Abort(_('cannot archive plain files to stdout'))
559 raise error.Abort(_('cannot archive plain files to stdout'))
560 dest = cmdutil.makefileobj(repo, dest)
560 dest = cmdutil.makefileobj(repo, dest)
561 if not prefix:
561 if not prefix:
562 prefix = os.path.basename(repo.root) + '-%h'
562 prefix = os.path.basename(repo.root) + '-%h'
563
563
564 prefix = cmdutil.makefilename(repo, prefix, node)
564 prefix = cmdutil.makefilename(repo, prefix, node)
565 matchfn = scmutil.match(ctx, [], opts)
565 matchfn = scmutil.match(ctx, [], opts)
566 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
566 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
567 matchfn, prefix, subrepos=opts.get('subrepos'))
567 matchfn, prefix, subrepos=opts.get('subrepos'))
568
568
569 @command('backout',
569 @command('backout',
570 [('', 'merge', None, _('merge with old dirstate parent after backout')),
570 [('', 'merge', None, _('merge with old dirstate parent after backout')),
571 ('', 'commit', None,
571 ('', 'commit', None,
572 _('commit if no conflicts were encountered (DEPRECATED)')),
572 _('commit if no conflicts were encountered (DEPRECATED)')),
573 ('', 'no-commit', None, _('do not commit')),
573 ('', 'no-commit', None, _('do not commit')),
574 ('', 'parent', '',
574 ('', 'parent', '',
575 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
575 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
576 ('r', 'rev', '', _('revision to backout'), _('REV')),
576 ('r', 'rev', '', _('revision to backout'), _('REV')),
577 ('e', 'edit', False, _('invoke editor on commit messages')),
577 ('e', 'edit', False, _('invoke editor on commit messages')),
578 ] + mergetoolopts + walkopts + commitopts + commitopts2,
578 ] + mergetoolopts + walkopts + commitopts + commitopts2,
579 _('[OPTION]... [-r] REV'))
579 _('[OPTION]... [-r] REV'))
580 def backout(ui, repo, node=None, rev=None, **opts):
580 def backout(ui, repo, node=None, rev=None, **opts):
581 '''reverse effect of earlier changeset
581 '''reverse effect of earlier changeset
582
582
583 Prepare a new changeset with the effect of REV undone in the
583 Prepare a new changeset with the effect of REV undone in the
584 current working directory. If no conflicts were encountered,
584 current working directory. If no conflicts were encountered,
585 it will be committed immediately.
585 it will be committed immediately.
586
586
587 If REV is the parent of the working directory, then this new changeset
587 If REV is the parent of the working directory, then this new changeset
588 is committed automatically (unless --no-commit is specified).
588 is committed automatically (unless --no-commit is specified).
589
589
590 .. note::
590 .. note::
591
591
592 :hg:`backout` cannot be used to fix either an unwanted or
592 :hg:`backout` cannot be used to fix either an unwanted or
593 incorrect merge.
593 incorrect merge.
594
594
595 .. container:: verbose
595 .. container:: verbose
596
596
597 Examples:
597 Examples:
598
598
599 - Reverse the effect of the parent of the working directory.
599 - Reverse the effect of the parent of the working directory.
600 This backout will be committed immediately::
600 This backout will be committed immediately::
601
601
602 hg backout -r .
602 hg backout -r .
603
603
604 - Reverse the effect of previous bad revision 23::
604 - Reverse the effect of previous bad revision 23::
605
605
606 hg backout -r 23
606 hg backout -r 23
607
607
608 - Reverse the effect of previous bad revision 23 and
608 - Reverse the effect of previous bad revision 23 and
609 leave changes uncommitted::
609 leave changes uncommitted::
610
610
611 hg backout -r 23 --no-commit
611 hg backout -r 23 --no-commit
612 hg commit -m "Backout revision 23"
612 hg commit -m "Backout revision 23"
613
613
614 By default, the pending changeset will have one parent,
614 By default, the pending changeset will have one parent,
615 maintaining a linear history. With --merge, the pending
615 maintaining a linear history. With --merge, the pending
616 changeset will instead have two parents: the old parent of the
616 changeset will instead have two parents: the old parent of the
617 working directory and a new child of REV that simply undoes REV.
617 working directory and a new child of REV that simply undoes REV.
618
618
619 Before version 1.7, the behavior without --merge was equivalent
619 Before version 1.7, the behavior without --merge was equivalent
620 to specifying --merge followed by :hg:`update --clean .` to
620 to specifying --merge followed by :hg:`update --clean .` to
621 cancel the merge and leave the child of REV as a head to be
621 cancel the merge and leave the child of REV as a head to be
622 merged separately.
622 merged separately.
623
623
624 See :hg:`help dates` for a list of formats valid for -d/--date.
624 See :hg:`help dates` for a list of formats valid for -d/--date.
625
625
626 See :hg:`help revert` for a way to restore files to the state
626 See :hg:`help revert` for a way to restore files to the state
627 of another revision.
627 of another revision.
628
628
629 Returns 0 on success, 1 if nothing to backout or there are unresolved
629 Returns 0 on success, 1 if nothing to backout or there are unresolved
630 files.
630 files.
631 '''
631 '''
632 wlock = lock = None
632 wlock = lock = None
633 try:
633 try:
634 wlock = repo.wlock()
634 wlock = repo.wlock()
635 lock = repo.lock()
635 lock = repo.lock()
636 return _dobackout(ui, repo, node, rev, **opts)
636 return _dobackout(ui, repo, node, rev, **opts)
637 finally:
637 finally:
638 release(lock, wlock)
638 release(lock, wlock)
639
639
640 def _dobackout(ui, repo, node=None, rev=None, **opts):
640 def _dobackout(ui, repo, node=None, rev=None, **opts):
641 if opts.get('commit') and opts.get('no_commit'):
641 if opts.get('commit') and opts.get('no_commit'):
642 raise error.Abort(_("cannot use --commit with --no-commit"))
642 raise error.Abort(_("cannot use --commit with --no-commit"))
643 if opts.get('merge') and opts.get('no_commit'):
643 if opts.get('merge') and opts.get('no_commit'):
644 raise error.Abort(_("cannot use --merge with --no-commit"))
644 raise error.Abort(_("cannot use --merge with --no-commit"))
645
645
646 if rev and node:
646 if rev and node:
647 raise error.Abort(_("please specify just one revision"))
647 raise error.Abort(_("please specify just one revision"))
648
648
649 if not rev:
649 if not rev:
650 rev = node
650 rev = node
651
651
652 if not rev:
652 if not rev:
653 raise error.Abort(_("please specify a revision to backout"))
653 raise error.Abort(_("please specify a revision to backout"))
654
654
655 date = opts.get('date')
655 date = opts.get('date')
656 if date:
656 if date:
657 opts['date'] = util.parsedate(date)
657 opts['date'] = util.parsedate(date)
658
658
659 cmdutil.checkunfinished(repo)
659 cmdutil.checkunfinished(repo)
660 cmdutil.bailifchanged(repo)
660 cmdutil.bailifchanged(repo)
661 node = scmutil.revsingle(repo, rev).node()
661 node = scmutil.revsingle(repo, rev).node()
662
662
663 op1, op2 = repo.dirstate.parents()
663 op1, op2 = repo.dirstate.parents()
664 if not repo.changelog.isancestor(node, op1):
664 if not repo.changelog.isancestor(node, op1):
665 raise error.Abort(_('cannot backout change that is not an ancestor'))
665 raise error.Abort(_('cannot backout change that is not an ancestor'))
666
666
667 p1, p2 = repo.changelog.parents(node)
667 p1, p2 = repo.changelog.parents(node)
668 if p1 == nullid:
668 if p1 == nullid:
669 raise error.Abort(_('cannot backout a change with no parents'))
669 raise error.Abort(_('cannot backout a change with no parents'))
670 if p2 != nullid:
670 if p2 != nullid:
671 if not opts.get('parent'):
671 if not opts.get('parent'):
672 raise error.Abort(_('cannot backout a merge changeset'))
672 raise error.Abort(_('cannot backout a merge changeset'))
673 p = repo.lookup(opts['parent'])
673 p = repo.lookup(opts['parent'])
674 if p not in (p1, p2):
674 if p not in (p1, p2):
675 raise error.Abort(_('%s is not a parent of %s') %
675 raise error.Abort(_('%s is not a parent of %s') %
676 (short(p), short(node)))
676 (short(p), short(node)))
677 parent = p
677 parent = p
678 else:
678 else:
679 if opts.get('parent'):
679 if opts.get('parent'):
680 raise error.Abort(_('cannot use --parent on non-merge changeset'))
680 raise error.Abort(_('cannot use --parent on non-merge changeset'))
681 parent = p1
681 parent = p1
682
682
683 # the backout should appear on the same branch
683 # the backout should appear on the same branch
684 branch = repo.dirstate.branch()
684 branch = repo.dirstate.branch()
685 bheads = repo.branchheads(branch)
685 bheads = repo.branchheads(branch)
686 rctx = scmutil.revsingle(repo, hex(parent))
686 rctx = scmutil.revsingle(repo, hex(parent))
687 if not opts.get('merge') and op1 != node:
687 if not opts.get('merge') and op1 != node:
688 dsguard = cmdutil.dirstateguard(repo, 'backout')
688 dsguard = cmdutil.dirstateguard(repo, 'backout')
689 try:
689 try:
690 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
690 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
691 'backout')
691 'backout')
692 stats = mergemod.update(repo, parent, True, True, node, False)
692 stats = mergemod.update(repo, parent, True, True, node, False)
693 repo.setparents(op1, op2)
693 repo.setparents(op1, op2)
694 dsguard.close()
694 dsguard.close()
695 hg._showstats(repo, stats)
695 hg._showstats(repo, stats)
696 if stats[3]:
696 if stats[3]:
697 repo.ui.status(_("use 'hg resolve' to retry unresolved "
697 repo.ui.status(_("use 'hg resolve' to retry unresolved "
698 "file merges\n"))
698 "file merges\n"))
699 return 1
699 return 1
700 finally:
700 finally:
701 ui.setconfig('ui', 'forcemerge', '', '')
701 ui.setconfig('ui', 'forcemerge', '', '')
702 lockmod.release(dsguard)
702 lockmod.release(dsguard)
703 else:
703 else:
704 hg.clean(repo, node, show_stats=False)
704 hg.clean(repo, node, show_stats=False)
705 repo.dirstate.setbranch(branch)
705 repo.dirstate.setbranch(branch)
706 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
706 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
707
707
708 if opts.get('no_commit'):
708 if opts.get('no_commit'):
709 msg = _("changeset %s backed out, "
709 msg = _("changeset %s backed out, "
710 "don't forget to commit.\n")
710 "don't forget to commit.\n")
711 ui.status(msg % short(node))
711 ui.status(msg % short(node))
712 return 0
712 return 0
713
713
714 def commitfunc(ui, repo, message, match, opts):
714 def commitfunc(ui, repo, message, match, opts):
715 editform = 'backout'
715 editform = 'backout'
716 e = cmdutil.getcommiteditor(editform=editform, **opts)
716 e = cmdutil.getcommiteditor(editform=editform, **opts)
717 if not message:
717 if not message:
718 # we don't translate commit messages
718 # we don't translate commit messages
719 message = "Backed out changeset %s" % short(node)
719 message = "Backed out changeset %s" % short(node)
720 e = cmdutil.getcommiteditor(edit=True, editform=editform)
720 e = cmdutil.getcommiteditor(edit=True, editform=editform)
721 return repo.commit(message, opts.get('user'), opts.get('date'),
721 return repo.commit(message, opts.get('user'), opts.get('date'),
722 match, editor=e)
722 match, editor=e)
723 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
723 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
724 if not newnode:
724 if not newnode:
725 ui.status(_("nothing changed\n"))
725 ui.status(_("nothing changed\n"))
726 return 1
726 return 1
727 cmdutil.commitstatus(repo, newnode, branch, bheads)
727 cmdutil.commitstatus(repo, newnode, branch, bheads)
728
728
729 def nice(node):
729 def nice(node):
730 return '%d:%s' % (repo.changelog.rev(node), short(node))
730 return '%d:%s' % (repo.changelog.rev(node), short(node))
731 ui.status(_('changeset %s backs out changeset %s\n') %
731 ui.status(_('changeset %s backs out changeset %s\n') %
732 (nice(repo.changelog.tip()), nice(node)))
732 (nice(repo.changelog.tip()), nice(node)))
733 if opts.get('merge') and op1 != node:
733 if opts.get('merge') and op1 != node:
734 hg.clean(repo, op1, show_stats=False)
734 hg.clean(repo, op1, show_stats=False)
735 ui.status(_('merging with changeset %s\n')
735 ui.status(_('merging with changeset %s\n')
736 % nice(repo.changelog.tip()))
736 % nice(repo.changelog.tip()))
737 try:
737 try:
738 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
738 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
739 'backout')
739 'backout')
740 return hg.merge(repo, hex(repo.changelog.tip()))
740 return hg.merge(repo, hex(repo.changelog.tip()))
741 finally:
741 finally:
742 ui.setconfig('ui', 'forcemerge', '', '')
742 ui.setconfig('ui', 'forcemerge', '', '')
743 return 0
743 return 0
744
744
745 @command('bisect',
745 @command('bisect',
746 [('r', 'reset', False, _('reset bisect state')),
746 [('r', 'reset', False, _('reset bisect state')),
747 ('g', 'good', False, _('mark changeset good')),
747 ('g', 'good', False, _('mark changeset good')),
748 ('b', 'bad', False, _('mark changeset bad')),
748 ('b', 'bad', False, _('mark changeset bad')),
749 ('s', 'skip', False, _('skip testing changeset')),
749 ('s', 'skip', False, _('skip testing changeset')),
750 ('e', 'extend', False, _('extend the bisect range')),
750 ('e', 'extend', False, _('extend the bisect range')),
751 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
751 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
752 ('U', 'noupdate', False, _('do not update to target'))],
752 ('U', 'noupdate', False, _('do not update to target'))],
753 _("[-gbsr] [-U] [-c CMD] [REV]"))
753 _("[-gbsr] [-U] [-c CMD] [REV]"))
754 def bisect(ui, repo, rev=None, extra=None, command=None,
754 def bisect(ui, repo, rev=None, extra=None, command=None,
755 reset=None, good=None, bad=None, skip=None, extend=None,
755 reset=None, good=None, bad=None, skip=None, extend=None,
756 noupdate=None):
756 noupdate=None):
757 """subdivision search of changesets
757 """subdivision search of changesets
758
758
759 This command helps to find changesets which introduce problems. To
759 This command helps to find changesets which introduce problems. To
760 use, mark the earliest changeset you know exhibits the problem as
760 use, mark the earliest changeset you know exhibits the problem as
761 bad, then mark the latest changeset which is free from the problem
761 bad, then mark the latest changeset which is free from the problem
762 as good. Bisect will update your working directory to a revision
762 as good. Bisect will update your working directory to a revision
763 for testing (unless the -U/--noupdate option is specified). Once
763 for testing (unless the -U/--noupdate option is specified). Once
764 you have performed tests, mark the working directory as good or
764 you have performed tests, mark the working directory as good or
765 bad, and bisect will either update to another candidate changeset
765 bad, and bisect will either update to another candidate changeset
766 or announce that it has found the bad revision.
766 or announce that it has found the bad revision.
767
767
768 As a shortcut, you can also use the revision argument to mark a
768 As a shortcut, you can also use the revision argument to mark a
769 revision as good or bad without checking it out first.
769 revision as good or bad without checking it out first.
770
770
771 If you supply a command, it will be used for automatic bisection.
771 If you supply a command, it will be used for automatic bisection.
772 The environment variable HG_NODE will contain the ID of the
772 The environment variable HG_NODE will contain the ID of the
773 changeset being tested. The exit status of the command will be
773 changeset being tested. The exit status of the command will be
774 used to mark revisions as good or bad: status 0 means good, 125
774 used to mark revisions as good or bad: status 0 means good, 125
775 means to skip the revision, 127 (command not found) will abort the
775 means to skip the revision, 127 (command not found) will abort the
776 bisection, and any other non-zero exit status means the revision
776 bisection, and any other non-zero exit status means the revision
777 is bad.
777 is bad.
778
778
779 .. container:: verbose
779 .. container:: verbose
780
780
781 Some examples:
781 Some examples:
782
782
783 - start a bisection with known bad revision 34, and good revision 12::
783 - start a bisection with known bad revision 34, and good revision 12::
784
784
785 hg bisect --bad 34
785 hg bisect --bad 34
786 hg bisect --good 12
786 hg bisect --good 12
787
787
788 - advance the current bisection by marking current revision as good or
788 - advance the current bisection by marking current revision as good or
789 bad::
789 bad::
790
790
791 hg bisect --good
791 hg bisect --good
792 hg bisect --bad
792 hg bisect --bad
793
793
794 - mark the current revision, or a known revision, to be skipped (e.g. if
794 - mark the current revision, or a known revision, to be skipped (e.g. if
795 that revision is not usable because of another issue)::
795 that revision is not usable because of another issue)::
796
796
797 hg bisect --skip
797 hg bisect --skip
798 hg bisect --skip 23
798 hg bisect --skip 23
799
799
800 - skip all revisions that do not touch directories ``foo`` or ``bar``::
800 - skip all revisions that do not touch directories ``foo`` or ``bar``::
801
801
802 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
802 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
803
803
804 - forget the current bisection::
804 - forget the current bisection::
805
805
806 hg bisect --reset
806 hg bisect --reset
807
807
808 - use 'make && make tests' to automatically find the first broken
808 - use 'make && make tests' to automatically find the first broken
809 revision::
809 revision::
810
810
811 hg bisect --reset
811 hg bisect --reset
812 hg bisect --bad 34
812 hg bisect --bad 34
813 hg bisect --good 12
813 hg bisect --good 12
814 hg bisect --command "make && make tests"
814 hg bisect --command "make && make tests"
815
815
816 - see all changesets whose states are already known in the current
816 - see all changesets whose states are already known in the current
817 bisection::
817 bisection::
818
818
819 hg log -r "bisect(pruned)"
819 hg log -r "bisect(pruned)"
820
820
821 - see the changeset currently being bisected (especially useful
821 - see the changeset currently being bisected (especially useful
822 if running with -U/--noupdate)::
822 if running with -U/--noupdate)::
823
823
824 hg log -r "bisect(current)"
824 hg log -r "bisect(current)"
825
825
826 - see all changesets that took part in the current bisection::
826 - see all changesets that took part in the current bisection::
827
827
828 hg log -r "bisect(range)"
828 hg log -r "bisect(range)"
829
829
830 - you can even get a nice graph::
830 - you can even get a nice graph::
831
831
832 hg log --graph -r "bisect(range)"
832 hg log --graph -r "bisect(range)"
833
833
834 See :hg:`help revsets` for more about the `bisect()` keyword.
834 See :hg:`help revsets` for more about the `bisect()` keyword.
835
835
836 Returns 0 on success.
836 Returns 0 on success.
837 """
837 """
838 def extendbisectrange(nodes, good):
838 def extendbisectrange(nodes, good):
839 # bisect is incomplete when it ends on a merge node and
839 # bisect is incomplete when it ends on a merge node and
840 # one of the parent was not checked.
840 # one of the parent was not checked.
841 parents = repo[nodes[0]].parents()
841 parents = repo[nodes[0]].parents()
842 if len(parents) > 1:
842 if len(parents) > 1:
843 if good:
843 if good:
844 side = state['bad']
844 side = state['bad']
845 else:
845 else:
846 side = state['good']
846 side = state['good']
847 num = len(set(i.node() for i in parents) & set(side))
847 num = len(set(i.node() for i in parents) & set(side))
848 if num == 1:
848 if num == 1:
849 return parents[0].ancestor(parents[1])
849 return parents[0].ancestor(parents[1])
850 return None
850 return None
851
851
852 def print_result(nodes, good):
852 def print_result(nodes, good):
853 displayer = cmdutil.show_changeset(ui, repo, {})
853 displayer = cmdutil.show_changeset(ui, repo, {})
854 if len(nodes) == 1:
854 if len(nodes) == 1:
855 # narrowed it down to a single revision
855 # narrowed it down to a single revision
856 if good:
856 if good:
857 ui.write(_("The first good revision is:\n"))
857 ui.write(_("The first good revision is:\n"))
858 else:
858 else:
859 ui.write(_("The first bad revision is:\n"))
859 ui.write(_("The first bad revision is:\n"))
860 displayer.show(repo[nodes[0]])
860 displayer.show(repo[nodes[0]])
861 extendnode = extendbisectrange(nodes, good)
861 extendnode = extendbisectrange(nodes, good)
862 if extendnode is not None:
862 if extendnode is not None:
863 ui.write(_('Not all ancestors of this changeset have been'
863 ui.write(_('Not all ancestors of this changeset have been'
864 ' checked.\nUse bisect --extend to continue the '
864 ' checked.\nUse bisect --extend to continue the '
865 'bisection from\nthe common ancestor, %s.\n')
865 'bisection from\nthe common ancestor, %s.\n')
866 % extendnode)
866 % extendnode)
867 else:
867 else:
868 # multiple possible revisions
868 # multiple possible revisions
869 if good:
869 if good:
870 ui.write(_("Due to skipped revisions, the first "
870 ui.write(_("Due to skipped revisions, the first "
871 "good revision could be any of:\n"))
871 "good revision could be any of:\n"))
872 else:
872 else:
873 ui.write(_("Due to skipped revisions, the first "
873 ui.write(_("Due to skipped revisions, the first "
874 "bad revision could be any of:\n"))
874 "bad revision could be any of:\n"))
875 for n in nodes:
875 for n in nodes:
876 displayer.show(repo[n])
876 displayer.show(repo[n])
877 displayer.close()
877 displayer.close()
878
878
879 def check_state(state, interactive=True):
879 def check_state(state, interactive=True):
880 if not state['good'] or not state['bad']:
880 if not state['good'] or not state['bad']:
881 if (good or bad or skip or reset) and interactive:
881 if (good or bad or skip or reset) and interactive:
882 return
882 return
883 if not state['good']:
883 if not state['good']:
884 raise error.Abort(_('cannot bisect (no known good revisions)'))
884 raise error.Abort(_('cannot bisect (no known good revisions)'))
885 else:
885 else:
886 raise error.Abort(_('cannot bisect (no known bad revisions)'))
886 raise error.Abort(_('cannot bisect (no known bad revisions)'))
887 return True
887 return True
888
888
889 # backward compatibility
889 # backward compatibility
890 if rev in "good bad reset init".split():
890 if rev in "good bad reset init".split():
891 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
891 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
892 cmd, rev, extra = rev, extra, None
892 cmd, rev, extra = rev, extra, None
893 if cmd == "good":
893 if cmd == "good":
894 good = True
894 good = True
895 elif cmd == "bad":
895 elif cmd == "bad":
896 bad = True
896 bad = True
897 else:
897 else:
898 reset = True
898 reset = True
899 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
899 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
900 raise error.Abort(_('incompatible arguments'))
900 raise error.Abort(_('incompatible arguments'))
901
901
902 cmdutil.checkunfinished(repo)
902 cmdutil.checkunfinished(repo)
903
903
904 if reset:
904 if reset:
905 p = repo.join("bisect.state")
905 p = repo.join("bisect.state")
906 if os.path.exists(p):
906 if os.path.exists(p):
907 os.unlink(p)
907 os.unlink(p)
908 return
908 return
909
909
910 state = hbisect.load_state(repo)
910 state = hbisect.load_state(repo)
911
911
912 if command:
912 if command:
913 changesets = 1
913 changesets = 1
914 if noupdate:
914 if noupdate:
915 try:
915 try:
916 node = state['current'][0]
916 node = state['current'][0]
917 except LookupError:
917 except LookupError:
918 raise error.Abort(_('current bisect revision is unknown - '
918 raise error.Abort(_('current bisect revision is unknown - '
919 'start a new bisect to fix'))
919 'start a new bisect to fix'))
920 else:
920 else:
921 node, p2 = repo.dirstate.parents()
921 node, p2 = repo.dirstate.parents()
922 if p2 != nullid:
922 if p2 != nullid:
923 raise error.Abort(_('current bisect revision is a merge'))
923 raise error.Abort(_('current bisect revision is a merge'))
924 try:
924 try:
925 while changesets:
925 while changesets:
926 # update state
926 # update state
927 state['current'] = [node]
927 state['current'] = [node]
928 hbisect.save_state(repo, state)
928 hbisect.save_state(repo, state)
929 status = ui.system(command, environ={'HG_NODE': hex(node)})
929 status = ui.system(command, environ={'HG_NODE': hex(node)})
930 if status == 125:
930 if status == 125:
931 transition = "skip"
931 transition = "skip"
932 elif status == 0:
932 elif status == 0:
933 transition = "good"
933 transition = "good"
934 # status < 0 means process was killed
934 # status < 0 means process was killed
935 elif status == 127:
935 elif status == 127:
936 raise error.Abort(_("failed to execute %s") % command)
936 raise error.Abort(_("failed to execute %s") % command)
937 elif status < 0:
937 elif status < 0:
938 raise error.Abort(_("%s killed") % command)
938 raise error.Abort(_("%s killed") % command)
939 else:
939 else:
940 transition = "bad"
940 transition = "bad"
941 ctx = scmutil.revsingle(repo, rev, node)
941 ctx = scmutil.revsingle(repo, rev, node)
942 rev = None # clear for future iterations
942 rev = None # clear for future iterations
943 state[transition].append(ctx.node())
943 state[transition].append(ctx.node())
944 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
944 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
945 check_state(state, interactive=False)
945 check_state(state, interactive=False)
946 # bisect
946 # bisect
947 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
947 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
948 # update to next check
948 # update to next check
949 node = nodes[0]
949 node = nodes[0]
950 if not noupdate:
950 if not noupdate:
951 cmdutil.bailifchanged(repo)
951 cmdutil.bailifchanged(repo)
952 hg.clean(repo, node, show_stats=False)
952 hg.clean(repo, node, show_stats=False)
953 finally:
953 finally:
954 state['current'] = [node]
954 state['current'] = [node]
955 hbisect.save_state(repo, state)
955 hbisect.save_state(repo, state)
956 print_result(nodes, bgood)
956 print_result(nodes, bgood)
957 return
957 return
958
958
959 # update state
959 # update state
960
960
961 if rev:
961 if rev:
962 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
962 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
963 else:
963 else:
964 nodes = [repo.lookup('.')]
964 nodes = [repo.lookup('.')]
965
965
966 if good or bad or skip:
966 if good or bad or skip:
967 if good:
967 if good:
968 state['good'] += nodes
968 state['good'] += nodes
969 elif bad:
969 elif bad:
970 state['bad'] += nodes
970 state['bad'] += nodes
971 elif skip:
971 elif skip:
972 state['skip'] += nodes
972 state['skip'] += nodes
973 hbisect.save_state(repo, state)
973 hbisect.save_state(repo, state)
974
974
975 if not check_state(state):
975 if not check_state(state):
976 return
976 return
977
977
978 # actually bisect
978 # actually bisect
979 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
979 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
980 if extend:
980 if extend:
981 if not changesets:
981 if not changesets:
982 extendnode = extendbisectrange(nodes, good)
982 extendnode = extendbisectrange(nodes, good)
983 if extendnode is not None:
983 if extendnode is not None:
984 ui.write(_("Extending search to changeset %d:%s\n")
984 ui.write(_("Extending search to changeset %d:%s\n")
985 % (extendnode.rev(), extendnode))
985 % (extendnode.rev(), extendnode))
986 state['current'] = [extendnode.node()]
986 state['current'] = [extendnode.node()]
987 hbisect.save_state(repo, state)
987 hbisect.save_state(repo, state)
988 if noupdate:
988 if noupdate:
989 return
989 return
990 cmdutil.bailifchanged(repo)
990 cmdutil.bailifchanged(repo)
991 return hg.clean(repo, extendnode.node())
991 return hg.clean(repo, extendnode.node())
992 raise error.Abort(_("nothing to extend"))
992 raise error.Abort(_("nothing to extend"))
993
993
994 if changesets == 0:
994 if changesets == 0:
995 print_result(nodes, good)
995 print_result(nodes, good)
996 else:
996 else:
997 assert len(nodes) == 1 # only a single node can be tested next
997 assert len(nodes) == 1 # only a single node can be tested next
998 node = nodes[0]
998 node = nodes[0]
999 # compute the approximate number of remaining tests
999 # compute the approximate number of remaining tests
1000 tests, size = 0, 2
1000 tests, size = 0, 2
1001 while size <= changesets:
1001 while size <= changesets:
1002 tests, size = tests + 1, size * 2
1002 tests, size = tests + 1, size * 2
1003 rev = repo.changelog.rev(node)
1003 rev = repo.changelog.rev(node)
1004 ui.write(_("Testing changeset %d:%s "
1004 ui.write(_("Testing changeset %d:%s "
1005 "(%d changesets remaining, ~%d tests)\n")
1005 "(%d changesets remaining, ~%d tests)\n")
1006 % (rev, short(node), changesets, tests))
1006 % (rev, short(node), changesets, tests))
1007 state['current'] = [node]
1007 state['current'] = [node]
1008 hbisect.save_state(repo, state)
1008 hbisect.save_state(repo, state)
1009 if not noupdate:
1009 if not noupdate:
1010 cmdutil.bailifchanged(repo)
1010 cmdutil.bailifchanged(repo)
1011 return hg.clean(repo, node)
1011 return hg.clean(repo, node)
1012
1012
1013 @command('bookmarks|bookmark',
1013 @command('bookmarks|bookmark',
1014 [('f', 'force', False, _('force')),
1014 [('f', 'force', False, _('force')),
1015 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
1015 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
1016 ('d', 'delete', False, _('delete a given bookmark')),
1016 ('d', 'delete', False, _('delete a given bookmark')),
1017 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
1017 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
1018 ('i', 'inactive', False, _('mark a bookmark inactive')),
1018 ('i', 'inactive', False, _('mark a bookmark inactive')),
1019 ] + formatteropts,
1019 ] + formatteropts,
1020 _('hg bookmarks [OPTIONS]... [NAME]...'))
1020 _('hg bookmarks [OPTIONS]... [NAME]...'))
1021 def bookmark(ui, repo, *names, **opts):
1021 def bookmark(ui, repo, *names, **opts):
1022 '''create a new bookmark or list existing bookmarks
1022 '''create a new bookmark or list existing bookmarks
1023
1023
1024 Bookmarks are labels on changesets to help track lines of development.
1024 Bookmarks are labels on changesets to help track lines of development.
1025 Bookmarks are unversioned and can be moved, renamed and deleted.
1025 Bookmarks are unversioned and can be moved, renamed and deleted.
1026 Deleting or moving a bookmark has no effect on the associated changesets.
1026 Deleting or moving a bookmark has no effect on the associated changesets.
1027
1027
1028 Creating or updating to a bookmark causes it to be marked as 'active'.
1028 Creating or updating to a bookmark causes it to be marked as 'active'.
1029 The active bookmark is indicated with a '*'.
1029 The active bookmark is indicated with a '*'.
1030 When a commit is made, the active bookmark will advance to the new commit.
1030 When a commit is made, the active bookmark will advance to the new commit.
1031 A plain :hg:`update` will also advance an active bookmark, if possible.
1031 A plain :hg:`update` will also advance an active bookmark, if possible.
1032 Updating away from a bookmark will cause it to be deactivated.
1032 Updating away from a bookmark will cause it to be deactivated.
1033
1033
1034 Bookmarks can be pushed and pulled between repositories (see
1034 Bookmarks can be pushed and pulled between repositories (see
1035 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1035 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1036 diverged, a new 'divergent bookmark' of the form 'name@path' will
1036 diverged, a new 'divergent bookmark' of the form 'name@path' will
1037 be created. Using :hg:`merge` will resolve the divergence.
1037 be created. Using :hg:`merge` will resolve the divergence.
1038
1038
1039 A bookmark named '@' has the special property that :hg:`clone` will
1039 A bookmark named '@' has the special property that :hg:`clone` will
1040 check it out by default if it exists.
1040 check it out by default if it exists.
1041
1041
1042 .. container:: verbose
1042 .. container:: verbose
1043
1043
1044 Examples:
1044 Examples:
1045
1045
1046 - create an active bookmark for a new line of development::
1046 - create an active bookmark for a new line of development::
1047
1047
1048 hg book new-feature
1048 hg book new-feature
1049
1049
1050 - create an inactive bookmark as a place marker::
1050 - create an inactive bookmark as a place marker::
1051
1051
1052 hg book -i reviewed
1052 hg book -i reviewed
1053
1053
1054 - create an inactive bookmark on another changeset::
1054 - create an inactive bookmark on another changeset::
1055
1055
1056 hg book -r .^ tested
1056 hg book -r .^ tested
1057
1057
1058 - rename bookmark turkey to dinner::
1058 - rename bookmark turkey to dinner::
1059
1059
1060 hg book -m turkey dinner
1060 hg book -m turkey dinner
1061
1061
1062 - move the '@' bookmark from another branch::
1062 - move the '@' bookmark from another branch::
1063
1063
1064 hg book -f @
1064 hg book -f @
1065 '''
1065 '''
1066 force = opts.get('force')
1066 force = opts.get('force')
1067 rev = opts.get('rev')
1067 rev = opts.get('rev')
1068 delete = opts.get('delete')
1068 delete = opts.get('delete')
1069 rename = opts.get('rename')
1069 rename = opts.get('rename')
1070 inactive = opts.get('inactive')
1070 inactive = opts.get('inactive')
1071
1071
1072 def checkformat(mark):
1072 def checkformat(mark):
1073 mark = mark.strip()
1073 mark = mark.strip()
1074 if not mark:
1074 if not mark:
1075 raise error.Abort(_("bookmark names cannot consist entirely of "
1075 raise error.Abort(_("bookmark names cannot consist entirely of "
1076 "whitespace"))
1076 "whitespace"))
1077 scmutil.checknewlabel(repo, mark, 'bookmark')
1077 scmutil.checknewlabel(repo, mark, 'bookmark')
1078 return mark
1078 return mark
1079
1079
1080 def checkconflict(repo, mark, cur, force=False, target=None):
1080 def checkconflict(repo, mark, cur, force=False, target=None):
1081 if mark in marks and not force:
1081 if mark in marks and not force:
1082 if target:
1082 if target:
1083 if marks[mark] == target and target == cur:
1083 if marks[mark] == target and target == cur:
1084 # re-activating a bookmark
1084 # re-activating a bookmark
1085 return
1085 return
1086 anc = repo.changelog.ancestors([repo[target].rev()])
1086 anc = repo.changelog.ancestors([repo[target].rev()])
1087 bmctx = repo[marks[mark]]
1087 bmctx = repo[marks[mark]]
1088 divs = [repo[b].node() for b in marks
1088 divs = [repo[b].node() for b in marks
1089 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
1089 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
1090
1090
1091 # allow resolving a single divergent bookmark even if moving
1091 # allow resolving a single divergent bookmark even if moving
1092 # the bookmark across branches when a revision is specified
1092 # the bookmark across branches when a revision is specified
1093 # that contains a divergent bookmark
1093 # that contains a divergent bookmark
1094 if bmctx.rev() not in anc and target in divs:
1094 if bmctx.rev() not in anc and target in divs:
1095 bookmarks.deletedivergent(repo, [target], mark)
1095 bookmarks.deletedivergent(repo, [target], mark)
1096 return
1096 return
1097
1097
1098 deletefrom = [b for b in divs
1098 deletefrom = [b for b in divs
1099 if repo[b].rev() in anc or b == target]
1099 if repo[b].rev() in anc or b == target]
1100 bookmarks.deletedivergent(repo, deletefrom, mark)
1100 bookmarks.deletedivergent(repo, deletefrom, mark)
1101 if bookmarks.validdest(repo, bmctx, repo[target]):
1101 if bookmarks.validdest(repo, bmctx, repo[target]):
1102 ui.status(_("moving bookmark '%s' forward from %s\n") %
1102 ui.status(_("moving bookmark '%s' forward from %s\n") %
1103 (mark, short(bmctx.node())))
1103 (mark, short(bmctx.node())))
1104 return
1104 return
1105 raise error.Abort(_("bookmark '%s' already exists "
1105 raise error.Abort(_("bookmark '%s' already exists "
1106 "(use -f to force)") % mark)
1106 "(use -f to force)") % mark)
1107 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
1107 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
1108 and not force):
1108 and not force):
1109 raise error.Abort(
1109 raise error.Abort(
1110 _("a bookmark cannot have the name of an existing branch"))
1110 _("a bookmark cannot have the name of an existing branch"))
1111
1111
1112 if delete and rename:
1112 if delete and rename:
1113 raise error.Abort(_("--delete and --rename are incompatible"))
1113 raise error.Abort(_("--delete and --rename are incompatible"))
1114 if delete and rev:
1114 if delete and rev:
1115 raise error.Abort(_("--rev is incompatible with --delete"))
1115 raise error.Abort(_("--rev is incompatible with --delete"))
1116 if rename and rev:
1116 if rename and rev:
1117 raise error.Abort(_("--rev is incompatible with --rename"))
1117 raise error.Abort(_("--rev is incompatible with --rename"))
1118 if not names and (delete or rev):
1118 if not names and (delete or rev):
1119 raise error.Abort(_("bookmark name required"))
1119 raise error.Abort(_("bookmark name required"))
1120
1120
1121 if delete or rename or names or inactive:
1121 if delete or rename or names or inactive:
1122 wlock = lock = tr = None
1122 wlock = lock = tr = None
1123 try:
1123 try:
1124 wlock = repo.wlock()
1124 wlock = repo.wlock()
1125 lock = repo.lock()
1125 lock = repo.lock()
1126 cur = repo.changectx('.').node()
1126 cur = repo.changectx('.').node()
1127 marks = repo._bookmarks
1127 marks = repo._bookmarks
1128 if delete:
1128 if delete:
1129 tr = repo.transaction('bookmark')
1129 tr = repo.transaction('bookmark')
1130 for mark in names:
1130 for mark in names:
1131 if mark not in marks:
1131 if mark not in marks:
1132 raise error.Abort(_("bookmark '%s' does not exist") %
1132 raise error.Abort(_("bookmark '%s' does not exist") %
1133 mark)
1133 mark)
1134 if mark == repo._activebookmark:
1134 if mark == repo._activebookmark:
1135 bookmarks.deactivate(repo)
1135 bookmarks.deactivate(repo)
1136 del marks[mark]
1136 del marks[mark]
1137
1137
1138 elif rename:
1138 elif rename:
1139 tr = repo.transaction('bookmark')
1139 tr = repo.transaction('bookmark')
1140 if not names:
1140 if not names:
1141 raise error.Abort(_("new bookmark name required"))
1141 raise error.Abort(_("new bookmark name required"))
1142 elif len(names) > 1:
1142 elif len(names) > 1:
1143 raise error.Abort(_("only one new bookmark name allowed"))
1143 raise error.Abort(_("only one new bookmark name allowed"))
1144 mark = checkformat(names[0])
1144 mark = checkformat(names[0])
1145 if rename not in marks:
1145 if rename not in marks:
1146 raise error.Abort(_("bookmark '%s' does not exist")
1146 raise error.Abort(_("bookmark '%s' does not exist")
1147 % rename)
1147 % rename)
1148 checkconflict(repo, mark, cur, force)
1148 checkconflict(repo, mark, cur, force)
1149 marks[mark] = marks[rename]
1149 marks[mark] = marks[rename]
1150 if repo._activebookmark == rename and not inactive:
1150 if repo._activebookmark == rename and not inactive:
1151 bookmarks.activate(repo, mark)
1151 bookmarks.activate(repo, mark)
1152 del marks[rename]
1152 del marks[rename]
1153 elif names:
1153 elif names:
1154 tr = repo.transaction('bookmark')
1154 tr = repo.transaction('bookmark')
1155 newact = None
1155 newact = None
1156 for mark in names:
1156 for mark in names:
1157 mark = checkformat(mark)
1157 mark = checkformat(mark)
1158 if newact is None:
1158 if newact is None:
1159 newact = mark
1159 newact = mark
1160 if inactive and mark == repo._activebookmark:
1160 if inactive and mark == repo._activebookmark:
1161 bookmarks.deactivate(repo)
1161 bookmarks.deactivate(repo)
1162 return
1162 return
1163 tgt = cur
1163 tgt = cur
1164 if rev:
1164 if rev:
1165 tgt = scmutil.revsingle(repo, rev).node()
1165 tgt = scmutil.revsingle(repo, rev).node()
1166 checkconflict(repo, mark, cur, force, tgt)
1166 checkconflict(repo, mark, cur, force, tgt)
1167 marks[mark] = tgt
1167 marks[mark] = tgt
1168 if not inactive and cur == marks[newact] and not rev:
1168 if not inactive and cur == marks[newact] and not rev:
1169 bookmarks.activate(repo, newact)
1169 bookmarks.activate(repo, newact)
1170 elif cur != tgt and newact == repo._activebookmark:
1170 elif cur != tgt and newact == repo._activebookmark:
1171 bookmarks.deactivate(repo)
1171 bookmarks.deactivate(repo)
1172 elif inactive:
1172 elif inactive:
1173 if len(marks) == 0:
1173 if len(marks) == 0:
1174 ui.status(_("no bookmarks set\n"))
1174 ui.status(_("no bookmarks set\n"))
1175 elif not repo._activebookmark:
1175 elif not repo._activebookmark:
1176 ui.status(_("no active bookmark\n"))
1176 ui.status(_("no active bookmark\n"))
1177 else:
1177 else:
1178 bookmarks.deactivate(repo)
1178 bookmarks.deactivate(repo)
1179 if tr is not None:
1179 if tr is not None:
1180 marks.recordchange(tr)
1180 marks.recordchange(tr)
1181 tr.close()
1181 tr.close()
1182 finally:
1182 finally:
1183 lockmod.release(tr, lock, wlock)
1183 lockmod.release(tr, lock, wlock)
1184 else: # show bookmarks
1184 else: # show bookmarks
1185 fm = ui.formatter('bookmarks', opts)
1185 fm = ui.formatter('bookmarks', opts)
1186 hexfn = fm.hexfunc
1186 hexfn = fm.hexfunc
1187 marks = repo._bookmarks
1187 marks = repo._bookmarks
1188 if len(marks) == 0 and not fm:
1188 if len(marks) == 0 and not fm:
1189 ui.status(_("no bookmarks set\n"))
1189 ui.status(_("no bookmarks set\n"))
1190 for bmark, n in sorted(marks.iteritems()):
1190 for bmark, n in sorted(marks.iteritems()):
1191 active = repo._activebookmark
1191 active = repo._activebookmark
1192 if bmark == active:
1192 if bmark == active:
1193 prefix, label = '*', activebookmarklabel
1193 prefix, label = '*', activebookmarklabel
1194 else:
1194 else:
1195 prefix, label = ' ', ''
1195 prefix, label = ' ', ''
1196
1196
1197 fm.startitem()
1197 fm.startitem()
1198 if not ui.quiet:
1198 if not ui.quiet:
1199 fm.plain(' %s ' % prefix, label=label)
1199 fm.plain(' %s ' % prefix, label=label)
1200 fm.write('bookmark', '%s', bmark, label=label)
1200 fm.write('bookmark', '%s', bmark, label=label)
1201 pad = " " * (25 - encoding.colwidth(bmark))
1201 pad = " " * (25 - encoding.colwidth(bmark))
1202 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1202 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1203 repo.changelog.rev(n), hexfn(n), label=label)
1203 repo.changelog.rev(n), hexfn(n), label=label)
1204 fm.data(active=(bmark == active))
1204 fm.data(active=(bmark == active))
1205 fm.plain('\n')
1205 fm.plain('\n')
1206 fm.end()
1206 fm.end()
1207
1207
1208 @command('branch',
1208 @command('branch',
1209 [('f', 'force', None,
1209 [('f', 'force', None,
1210 _('set branch name even if it shadows an existing branch')),
1210 _('set branch name even if it shadows an existing branch')),
1211 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1211 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1212 _('[-fC] [NAME]'))
1212 _('[-fC] [NAME]'))
1213 def branch(ui, repo, label=None, **opts):
1213 def branch(ui, repo, label=None, **opts):
1214 """set or show the current branch name
1214 """set or show the current branch name
1215
1215
1216 .. note::
1216 .. note::
1217
1217
1218 Branch names are permanent and global. Use :hg:`bookmark` to create a
1218 Branch names are permanent and global. Use :hg:`bookmark` to create a
1219 light-weight bookmark instead. See :hg:`help glossary` for more
1219 light-weight bookmark instead. See :hg:`help glossary` for more
1220 information about named branches and bookmarks.
1220 information about named branches and bookmarks.
1221
1221
1222 With no argument, show the current branch name. With one argument,
1222 With no argument, show the current branch name. With one argument,
1223 set the working directory branch name (the branch will not exist
1223 set the working directory branch name (the branch will not exist
1224 in the repository until the next commit). Standard practice
1224 in the repository until the next commit). Standard practice
1225 recommends that primary development take place on the 'default'
1225 recommends that primary development take place on the 'default'
1226 branch.
1226 branch.
1227
1227
1228 Unless -f/--force is specified, branch will not let you set a
1228 Unless -f/--force is specified, branch will not let you set a
1229 branch name that already exists.
1229 branch name that already exists.
1230
1230
1231 Use -C/--clean to reset the working directory branch to that of
1231 Use -C/--clean to reset the working directory branch to that of
1232 the parent of the working directory, negating a previous branch
1232 the parent of the working directory, negating a previous branch
1233 change.
1233 change.
1234
1234
1235 Use the command :hg:`update` to switch to an existing branch. Use
1235 Use the command :hg:`update` to switch to an existing branch. Use
1236 :hg:`commit --close-branch` to mark this branch head as closed.
1236 :hg:`commit --close-branch` to mark this branch head as closed.
1237 When all heads of a branch are closed, the branch will be
1237 When all heads of a branch are closed, the branch will be
1238 considered closed.
1238 considered closed.
1239
1239
1240 Returns 0 on success.
1240 Returns 0 on success.
1241 """
1241 """
1242 if label:
1242 if label:
1243 label = label.strip()
1243 label = label.strip()
1244
1244
1245 if not opts.get('clean') and not label:
1245 if not opts.get('clean') and not label:
1246 ui.write("%s\n" % repo.dirstate.branch())
1246 ui.write("%s\n" % repo.dirstate.branch())
1247 return
1247 return
1248
1248
1249 with repo.wlock():
1249 with repo.wlock():
1250 if opts.get('clean'):
1250 if opts.get('clean'):
1251 label = repo[None].p1().branch()
1251 label = repo[None].p1().branch()
1252 repo.dirstate.setbranch(label)
1252 repo.dirstate.setbranch(label)
1253 ui.status(_('reset working directory to branch %s\n') % label)
1253 ui.status(_('reset working directory to branch %s\n') % label)
1254 elif label:
1254 elif label:
1255 if not opts.get('force') and label in repo.branchmap():
1255 if not opts.get('force') and label in repo.branchmap():
1256 if label not in [p.branch() for p in repo[None].parents()]:
1256 if label not in [p.branch() for p in repo[None].parents()]:
1257 raise error.Abort(_('a branch of the same name already'
1257 raise error.Abort(_('a branch of the same name already'
1258 ' exists'),
1258 ' exists'),
1259 # i18n: "it" refers to an existing branch
1259 # i18n: "it" refers to an existing branch
1260 hint=_("use 'hg update' to switch to it"))
1260 hint=_("use 'hg update' to switch to it"))
1261 scmutil.checknewlabel(repo, label, 'branch')
1261 scmutil.checknewlabel(repo, label, 'branch')
1262 repo.dirstate.setbranch(label)
1262 repo.dirstate.setbranch(label)
1263 ui.status(_('marked working directory as branch %s\n') % label)
1263 ui.status(_('marked working directory as branch %s\n') % label)
1264
1264
1265 # find any open named branches aside from default
1265 # find any open named branches aside from default
1266 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1266 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1267 if n != "default" and not c]
1267 if n != "default" and not c]
1268 if not others:
1268 if not others:
1269 ui.status(_('(branches are permanent and global, '
1269 ui.status(_('(branches are permanent and global, '
1270 'did you want a bookmark?)\n'))
1270 'did you want a bookmark?)\n'))
1271
1271
1272 @command('branches',
1272 @command('branches',
1273 [('a', 'active', False,
1273 [('a', 'active', False,
1274 _('show only branches that have unmerged heads (DEPRECATED)')),
1274 _('show only branches that have unmerged heads (DEPRECATED)')),
1275 ('c', 'closed', False, _('show normal and closed branches')),
1275 ('c', 'closed', False, _('show normal and closed branches')),
1276 ] + formatteropts,
1276 ] + formatteropts,
1277 _('[-c]'))
1277 _('[-c]'))
1278 def branches(ui, repo, active=False, closed=False, **opts):
1278 def branches(ui, repo, active=False, closed=False, **opts):
1279 """list repository named branches
1279 """list repository named branches
1280
1280
1281 List the repository's named branches, indicating which ones are
1281 List the repository's named branches, indicating which ones are
1282 inactive. If -c/--closed is specified, also list branches which have
1282 inactive. If -c/--closed is specified, also list branches which have
1283 been marked closed (see :hg:`commit --close-branch`).
1283 been marked closed (see :hg:`commit --close-branch`).
1284
1284
1285 Use the command :hg:`update` to switch to an existing branch.
1285 Use the command :hg:`update` to switch to an existing branch.
1286
1286
1287 Returns 0.
1287 Returns 0.
1288 """
1288 """
1289
1289
1290 fm = ui.formatter('branches', opts)
1290 fm = ui.formatter('branches', opts)
1291 hexfunc = fm.hexfunc
1291 hexfunc = fm.hexfunc
1292
1292
1293 allheads = set(repo.heads())
1293 allheads = set(repo.heads())
1294 branches = []
1294 branches = []
1295 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1295 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1296 isactive = not isclosed and bool(set(heads) & allheads)
1296 isactive = not isclosed and bool(set(heads) & allheads)
1297 branches.append((tag, repo[tip], isactive, not isclosed))
1297 branches.append((tag, repo[tip], isactive, not isclosed))
1298 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1298 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1299 reverse=True)
1299 reverse=True)
1300
1300
1301 for tag, ctx, isactive, isopen in branches:
1301 for tag, ctx, isactive, isopen in branches:
1302 if active and not isactive:
1302 if active and not isactive:
1303 continue
1303 continue
1304 if isactive:
1304 if isactive:
1305 label = 'branches.active'
1305 label = 'branches.active'
1306 notice = ''
1306 notice = ''
1307 elif not isopen:
1307 elif not isopen:
1308 if not closed:
1308 if not closed:
1309 continue
1309 continue
1310 label = 'branches.closed'
1310 label = 'branches.closed'
1311 notice = _(' (closed)')
1311 notice = _(' (closed)')
1312 else:
1312 else:
1313 label = 'branches.inactive'
1313 label = 'branches.inactive'
1314 notice = _(' (inactive)')
1314 notice = _(' (inactive)')
1315 current = (tag == repo.dirstate.branch())
1315 current = (tag == repo.dirstate.branch())
1316 if current:
1316 if current:
1317 label = 'branches.current'
1317 label = 'branches.current'
1318
1318
1319 fm.startitem()
1319 fm.startitem()
1320 fm.write('branch', '%s', tag, label=label)
1320 fm.write('branch', '%s', tag, label=label)
1321 rev = ctx.rev()
1321 rev = ctx.rev()
1322 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1322 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1323 fmt = ' ' * padsize + ' %d:%s'
1323 fmt = ' ' * padsize + ' %d:%s'
1324 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1324 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1325 label='log.changeset changeset.%s' % ctx.phasestr())
1325 label='log.changeset changeset.%s' % ctx.phasestr())
1326 fm.data(active=isactive, closed=not isopen, current=current)
1326 fm.data(active=isactive, closed=not isopen, current=current)
1327 if not ui.quiet:
1327 if not ui.quiet:
1328 fm.plain(notice)
1328 fm.plain(notice)
1329 fm.plain('\n')
1329 fm.plain('\n')
1330 fm.end()
1330 fm.end()
1331
1331
1332 @command('bundle',
1332 @command('bundle',
1333 [('f', 'force', None, _('run even when the destination is unrelated')),
1333 [('f', 'force', None, _('run even when the destination is unrelated')),
1334 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1334 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1335 _('REV')),
1335 _('REV')),
1336 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1336 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1337 _('BRANCH')),
1337 _('BRANCH')),
1338 ('', 'base', [],
1338 ('', 'base', [],
1339 _('a base changeset assumed to be available at the destination'),
1339 _('a base changeset assumed to be available at the destination'),
1340 _('REV')),
1340 _('REV')),
1341 ('a', 'all', None, _('bundle all changesets in the repository')),
1341 ('a', 'all', None, _('bundle all changesets in the repository')),
1342 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1342 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1343 ] + remoteopts,
1343 ] + remoteopts,
1344 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1344 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1345 def bundle(ui, repo, fname, dest=None, **opts):
1345 def bundle(ui, repo, fname, dest=None, **opts):
1346 """create a changegroup file
1346 """create a changegroup file
1347
1347
1348 Generate a changegroup file collecting changesets to be added
1348 Generate a changegroup file collecting changesets to be added
1349 to a repository.
1349 to a repository.
1350
1350
1351 To create a bundle containing all changesets, use -a/--all
1351 To create a bundle containing all changesets, use -a/--all
1352 (or --base null). Otherwise, hg assumes the destination will have
1352 (or --base null). Otherwise, hg assumes the destination will have
1353 all the nodes you specify with --base parameters. Otherwise, hg
1353 all the nodes you specify with --base parameters. Otherwise, hg
1354 will assume the repository has all the nodes in destination, or
1354 will assume the repository has all the nodes in destination, or
1355 default-push/default if no destination is specified.
1355 default-push/default if no destination is specified.
1356
1356
1357 You can change bundle format with the -t/--type option. You can
1357 You can change bundle format with the -t/--type option. You can
1358 specify a compression, a bundle version or both using a dash
1358 specify a compression, a bundle version or both using a dash
1359 (comp-version). The available compression methods are: none, bzip2,
1359 (comp-version). The available compression methods are: none, bzip2,
1360 and gzip (by default, bundles are compressed using bzip2). The
1360 and gzip (by default, bundles are compressed using bzip2). The
1361 available formats are: v1, v2 (default to most suitable).
1361 available formats are: v1, v2 (default to most suitable).
1362
1362
1363 The bundle file can then be transferred using conventional means
1363 The bundle file can then be transferred using conventional means
1364 and applied to another repository with the unbundle or pull
1364 and applied to another repository with the unbundle or pull
1365 command. This is useful when direct push and pull are not
1365 command. This is useful when direct push and pull are not
1366 available or when exporting an entire repository is undesirable.
1366 available or when exporting an entire repository is undesirable.
1367
1367
1368 Applying bundles preserves all changeset contents including
1368 Applying bundles preserves all changeset contents including
1369 permissions, copy/rename information, and revision history.
1369 permissions, copy/rename information, and revision history.
1370
1370
1371 Returns 0 on success, 1 if no changes found.
1371 Returns 0 on success, 1 if no changes found.
1372 """
1372 """
1373 revs = None
1373 revs = None
1374 if 'rev' in opts:
1374 if 'rev' in opts:
1375 revstrings = opts['rev']
1375 revstrings = opts['rev']
1376 revs = scmutil.revrange(repo, revstrings)
1376 revs = scmutil.revrange(repo, revstrings)
1377 if revstrings and not revs:
1377 if revstrings and not revs:
1378 raise error.Abort(_('no commits to bundle'))
1378 raise error.Abort(_('no commits to bundle'))
1379
1379
1380 bundletype = opts.get('type', 'bzip2').lower()
1380 bundletype = opts.get('type', 'bzip2').lower()
1381 try:
1381 try:
1382 bcompression, cgversion, params = exchange.parsebundlespec(
1382 bcompression, cgversion, params = exchange.parsebundlespec(
1383 repo, bundletype, strict=False)
1383 repo, bundletype, strict=False)
1384 except error.UnsupportedBundleSpecification as e:
1384 except error.UnsupportedBundleSpecification as e:
1385 raise error.Abort(str(e),
1385 raise error.Abort(str(e),
1386 hint=_('see "hg help bundle" for supported '
1386 hint=_('see "hg help bundle" for supported '
1387 'values for --type'))
1387 'values for --type'))
1388
1388
1389 # Packed bundles are a pseudo bundle format for now.
1389 # Packed bundles are a pseudo bundle format for now.
1390 if cgversion == 's1':
1390 if cgversion == 's1':
1391 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1391 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1392 hint=_("use 'hg debugcreatestreamclonebundle'"))
1392 hint=_("use 'hg debugcreatestreamclonebundle'"))
1393
1393
1394 if opts.get('all'):
1394 if opts.get('all'):
1395 if dest:
1395 if dest:
1396 raise error.Abort(_("--all is incompatible with specifying "
1396 raise error.Abort(_("--all is incompatible with specifying "
1397 "a destination"))
1397 "a destination"))
1398 if opts.get('base'):
1398 if opts.get('base'):
1399 ui.warn(_("ignoring --base because --all was specified\n"))
1399 ui.warn(_("ignoring --base because --all was specified\n"))
1400 base = ['null']
1400 base = ['null']
1401 else:
1401 else:
1402 base = scmutil.revrange(repo, opts.get('base'))
1402 base = scmutil.revrange(repo, opts.get('base'))
1403 # TODO: get desired bundlecaps from command line.
1403 # TODO: get desired bundlecaps from command line.
1404 bundlecaps = None
1404 bundlecaps = None
1405 if cgversion not in changegroup.supportedoutgoingversions(repo):
1405 if cgversion not in changegroup.supportedoutgoingversions(repo):
1406 raise error.Abort(_("repository does not support bundle version %s") %
1406 raise error.Abort(_("repository does not support bundle version %s") %
1407 cgversion)
1407 cgversion)
1408
1408
1409 if base:
1409 if base:
1410 if dest:
1410 if dest:
1411 raise error.Abort(_("--base is incompatible with specifying "
1411 raise error.Abort(_("--base is incompatible with specifying "
1412 "a destination"))
1412 "a destination"))
1413 common = [repo.lookup(rev) for rev in base]
1413 common = [repo.lookup(rev) for rev in base]
1414 heads = revs and map(repo.lookup, revs) or revs
1414 heads = revs and map(repo.lookup, revs) or revs
1415 cg = changegroup.getchangegroup(repo, 'bundle', heads=heads,
1415 cg = changegroup.getchangegroup(repo, 'bundle', heads=heads,
1416 common=common, bundlecaps=bundlecaps,
1416 common=common, bundlecaps=bundlecaps,
1417 version=cgversion)
1417 version=cgversion)
1418 outgoing = None
1418 outgoing = None
1419 else:
1419 else:
1420 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1420 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1421 dest, branches = hg.parseurl(dest, opts.get('branch'))
1421 dest, branches = hg.parseurl(dest, opts.get('branch'))
1422 other = hg.peer(repo, opts, dest)
1422 other = hg.peer(repo, opts, dest)
1423 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1423 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1424 heads = revs and map(repo.lookup, revs) or revs
1424 heads = revs and map(repo.lookup, revs) or revs
1425 outgoing = discovery.findcommonoutgoing(repo, other,
1425 outgoing = discovery.findcommonoutgoing(repo, other,
1426 onlyheads=heads,
1426 onlyheads=heads,
1427 force=opts.get('force'),
1427 force=opts.get('force'),
1428 portable=True)
1428 portable=True)
1429 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1429 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1430 bundlecaps, version=cgversion)
1430 bundlecaps, version=cgversion)
1431 if not cg:
1431 if not cg:
1432 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1432 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1433 return 1
1433 return 1
1434
1434
1435 if cgversion == '01': #bundle1
1435 if cgversion == '01': #bundle1
1436 if bcompression is None:
1436 if bcompression is None:
1437 bcompression = 'UN'
1437 bcompression = 'UN'
1438 bversion = 'HG10' + bcompression
1438 bversion = 'HG10' + bcompression
1439 bcompression = None
1439 bcompression = None
1440 else:
1440 else:
1441 assert cgversion == '02'
1441 assert cgversion == '02'
1442 bversion = 'HG20'
1442 bversion = 'HG20'
1443
1443
1444 bundle2.writebundle(ui, cg, fname, bversion, compression=bcompression)
1444 bundle2.writebundle(ui, cg, fname, bversion, compression=bcompression)
1445
1445
1446 @command('cat',
1446 @command('cat',
1447 [('o', 'output', '',
1447 [('o', 'output', '',
1448 _('print output to file with formatted name'), _('FORMAT')),
1448 _('print output to file with formatted name'), _('FORMAT')),
1449 ('r', 'rev', '', _('print the given revision'), _('REV')),
1449 ('r', 'rev', '', _('print the given revision'), _('REV')),
1450 ('', 'decode', None, _('apply any matching decode filter')),
1450 ('', 'decode', None, _('apply any matching decode filter')),
1451 ] + walkopts,
1451 ] + walkopts,
1452 _('[OPTION]... FILE...'),
1452 _('[OPTION]... FILE...'),
1453 inferrepo=True)
1453 inferrepo=True)
1454 def cat(ui, repo, file1, *pats, **opts):
1454 def cat(ui, repo, file1, *pats, **opts):
1455 """output the current or given revision of files
1455 """output the current or given revision of files
1456
1456
1457 Print the specified files as they were at the given revision. If
1457 Print the specified files as they were at the given revision. If
1458 no revision is given, the parent of the working directory is used.
1458 no revision is given, the parent of the working directory is used.
1459
1459
1460 Output may be to a file, in which case the name of the file is
1460 Output may be to a file, in which case the name of the file is
1461 given using a format string. The formatting rules as follows:
1461 given using a format string. The formatting rules as follows:
1462
1462
1463 :``%%``: literal "%" character
1463 :``%%``: literal "%" character
1464 :``%s``: basename of file being printed
1464 :``%s``: basename of file being printed
1465 :``%d``: dirname of file being printed, or '.' if in repository root
1465 :``%d``: dirname of file being printed, or '.' if in repository root
1466 :``%p``: root-relative path name of file being printed
1466 :``%p``: root-relative path name of file being printed
1467 :``%H``: changeset hash (40 hexadecimal digits)
1467 :``%H``: changeset hash (40 hexadecimal digits)
1468 :``%R``: changeset revision number
1468 :``%R``: changeset revision number
1469 :``%h``: short-form changeset hash (12 hexadecimal digits)
1469 :``%h``: short-form changeset hash (12 hexadecimal digits)
1470 :``%r``: zero-padded changeset revision number
1470 :``%r``: zero-padded changeset revision number
1471 :``%b``: basename of the exporting repository
1471 :``%b``: basename of the exporting repository
1472
1472
1473 Returns 0 on success.
1473 Returns 0 on success.
1474 """
1474 """
1475 ctx = scmutil.revsingle(repo, opts.get('rev'))
1475 ctx = scmutil.revsingle(repo, opts.get('rev'))
1476 m = scmutil.match(ctx, (file1,) + pats, opts)
1476 m = scmutil.match(ctx, (file1,) + pats, opts)
1477
1477
1478 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1478 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1479
1479
1480 @command('^clone',
1480 @command('^clone',
1481 [('U', 'noupdate', None, _('the clone will include an empty working '
1481 [('U', 'noupdate', None, _('the clone will include an empty working '
1482 'directory (only a repository)')),
1482 'directory (only a repository)')),
1483 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1483 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1484 _('REV')),
1484 _('REV')),
1485 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1485 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1486 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1486 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1487 ('', 'pull', None, _('use pull protocol to copy metadata')),
1487 ('', 'pull', None, _('use pull protocol to copy metadata')),
1488 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1488 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1489 ] + remoteopts,
1489 ] + remoteopts,
1490 _('[OPTION]... SOURCE [DEST]'),
1490 _('[OPTION]... SOURCE [DEST]'),
1491 norepo=True)
1491 norepo=True)
1492 def clone(ui, source, dest=None, **opts):
1492 def clone(ui, source, dest=None, **opts):
1493 """make a copy of an existing repository
1493 """make a copy of an existing repository
1494
1494
1495 Create a copy of an existing repository in a new directory.
1495 Create a copy of an existing repository in a new directory.
1496
1496
1497 If no destination directory name is specified, it defaults to the
1497 If no destination directory name is specified, it defaults to the
1498 basename of the source.
1498 basename of the source.
1499
1499
1500 The location of the source is added to the new repository's
1500 The location of the source is added to the new repository's
1501 ``.hg/hgrc`` file, as the default to be used for future pulls.
1501 ``.hg/hgrc`` file, as the default to be used for future pulls.
1502
1502
1503 Only local paths and ``ssh://`` URLs are supported as
1503 Only local paths and ``ssh://`` URLs are supported as
1504 destinations. For ``ssh://`` destinations, no working directory or
1504 destinations. For ``ssh://`` destinations, no working directory or
1505 ``.hg/hgrc`` will be created on the remote side.
1505 ``.hg/hgrc`` will be created on the remote side.
1506
1506
1507 If the source repository has a bookmark called '@' set, that
1507 If the source repository has a bookmark called '@' set, that
1508 revision will be checked out in the new repository by default.
1508 revision will be checked out in the new repository by default.
1509
1509
1510 To check out a particular version, use -u/--update, or
1510 To check out a particular version, use -u/--update, or
1511 -U/--noupdate to create a clone with no working directory.
1511 -U/--noupdate to create a clone with no working directory.
1512
1512
1513 To pull only a subset of changesets, specify one or more revisions
1513 To pull only a subset of changesets, specify one or more revisions
1514 identifiers with -r/--rev or branches with -b/--branch. The
1514 identifiers with -r/--rev or branches with -b/--branch. The
1515 resulting clone will contain only the specified changesets and
1515 resulting clone will contain only the specified changesets and
1516 their ancestors. These options (or 'clone src#rev dest') imply
1516 their ancestors. These options (or 'clone src#rev dest') imply
1517 --pull, even for local source repositories.
1517 --pull, even for local source repositories.
1518
1518
1519 .. note::
1519 .. note::
1520
1520
1521 Specifying a tag will include the tagged changeset but not the
1521 Specifying a tag will include the tagged changeset but not the
1522 changeset containing the tag.
1522 changeset containing the tag.
1523
1523
1524 .. container:: verbose
1524 .. container:: verbose
1525
1525
1526 For efficiency, hardlinks are used for cloning whenever the
1526 For efficiency, hardlinks are used for cloning whenever the
1527 source and destination are on the same filesystem (note this
1527 source and destination are on the same filesystem (note this
1528 applies only to the repository data, not to the working
1528 applies only to the repository data, not to the working
1529 directory). Some filesystems, such as AFS, implement hardlinking
1529 directory). Some filesystems, such as AFS, implement hardlinking
1530 incorrectly, but do not report errors. In these cases, use the
1530 incorrectly, but do not report errors. In these cases, use the
1531 --pull option to avoid hardlinking.
1531 --pull option to avoid hardlinking.
1532
1532
1533 In some cases, you can clone repositories and the working
1533 In some cases, you can clone repositories and the working
1534 directory using full hardlinks with ::
1534 directory using full hardlinks with ::
1535
1535
1536 $ cp -al REPO REPOCLONE
1536 $ cp -al REPO REPOCLONE
1537
1537
1538 This is the fastest way to clone, but it is not always safe. The
1538 This is the fastest way to clone, but it is not always safe. The
1539 operation is not atomic (making sure REPO is not modified during
1539 operation is not atomic (making sure REPO is not modified during
1540 the operation is up to you) and you have to make sure your
1540 the operation is up to you) and you have to make sure your
1541 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1541 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1542 so). Also, this is not compatible with certain extensions that
1542 so). Also, this is not compatible with certain extensions that
1543 place their metadata under the .hg directory, such as mq.
1543 place their metadata under the .hg directory, such as mq.
1544
1544
1545 Mercurial will update the working directory to the first applicable
1545 Mercurial will update the working directory to the first applicable
1546 revision from this list:
1546 revision from this list:
1547
1547
1548 a) null if -U or the source repository has no changesets
1548 a) null if -U or the source repository has no changesets
1549 b) if -u . and the source repository is local, the first parent of
1549 b) if -u . and the source repository is local, the first parent of
1550 the source repository's working directory
1550 the source repository's working directory
1551 c) the changeset specified with -u (if a branch name, this means the
1551 c) the changeset specified with -u (if a branch name, this means the
1552 latest head of that branch)
1552 latest head of that branch)
1553 d) the changeset specified with -r
1553 d) the changeset specified with -r
1554 e) the tipmost head specified with -b
1554 e) the tipmost head specified with -b
1555 f) the tipmost head specified with the url#branch source syntax
1555 f) the tipmost head specified with the url#branch source syntax
1556 g) the revision marked with the '@' bookmark, if present
1556 g) the revision marked with the '@' bookmark, if present
1557 h) the tipmost head of the default branch
1557 h) the tipmost head of the default branch
1558 i) tip
1558 i) tip
1559
1559
1560 When cloning from servers that support it, Mercurial may fetch
1560 When cloning from servers that support it, Mercurial may fetch
1561 pre-generated data from a server-advertised URL. When this is done,
1561 pre-generated data from a server-advertised URL. When this is done,
1562 hooks operating on incoming changesets and changegroups may fire twice,
1562 hooks operating on incoming changesets and changegroups may fire twice,
1563 once for the bundle fetched from the URL and another for any additional
1563 once for the bundle fetched from the URL and another for any additional
1564 data not fetched from this URL. In addition, if an error occurs, the
1564 data not fetched from this URL. In addition, if an error occurs, the
1565 repository may be rolled back to a partial clone. This behavior may
1565 repository may be rolled back to a partial clone. This behavior may
1566 change in future releases. See :hg:`help -e clonebundles` for more.
1566 change in future releases. See :hg:`help -e clonebundles` for more.
1567
1567
1568 Examples:
1568 Examples:
1569
1569
1570 - clone a remote repository to a new directory named hg/::
1570 - clone a remote repository to a new directory named hg/::
1571
1571
1572 hg clone http://selenic.com/hg
1572 hg clone http://selenic.com/hg
1573
1573
1574 - create a lightweight local clone::
1574 - create a lightweight local clone::
1575
1575
1576 hg clone project/ project-feature/
1576 hg clone project/ project-feature/
1577
1577
1578 - clone from an absolute path on an ssh server (note double-slash)::
1578 - clone from an absolute path on an ssh server (note double-slash)::
1579
1579
1580 hg clone ssh://user@server//home/projects/alpha/
1580 hg clone ssh://user@server//home/projects/alpha/
1581
1581
1582 - do a high-speed clone over a LAN while checking out a
1582 - do a high-speed clone over a LAN while checking out a
1583 specified version::
1583 specified version::
1584
1584
1585 hg clone --uncompressed http://server/repo -u 1.5
1585 hg clone --uncompressed http://server/repo -u 1.5
1586
1586
1587 - create a repository without changesets after a particular revision::
1587 - create a repository without changesets after a particular revision::
1588
1588
1589 hg clone -r 04e544 experimental/ good/
1589 hg clone -r 04e544 experimental/ good/
1590
1590
1591 - clone (and track) a particular named branch::
1591 - clone (and track) a particular named branch::
1592
1592
1593 hg clone http://selenic.com/hg#stable
1593 hg clone http://selenic.com/hg#stable
1594
1594
1595 See :hg:`help urls` for details on specifying URLs.
1595 See :hg:`help urls` for details on specifying URLs.
1596
1596
1597 Returns 0 on success.
1597 Returns 0 on success.
1598 """
1598 """
1599 if opts.get('noupdate') and opts.get('updaterev'):
1599 if opts.get('noupdate') and opts.get('updaterev'):
1600 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1600 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1601
1601
1602 r = hg.clone(ui, opts, source, dest,
1602 r = hg.clone(ui, opts, source, dest,
1603 pull=opts.get('pull'),
1603 pull=opts.get('pull'),
1604 stream=opts.get('uncompressed'),
1604 stream=opts.get('uncompressed'),
1605 rev=opts.get('rev'),
1605 rev=opts.get('rev'),
1606 update=opts.get('updaterev') or not opts.get('noupdate'),
1606 update=opts.get('updaterev') or not opts.get('noupdate'),
1607 branch=opts.get('branch'),
1607 branch=opts.get('branch'),
1608 shareopts=opts.get('shareopts'))
1608 shareopts=opts.get('shareopts'))
1609
1609
1610 return r is None
1610 return r is None
1611
1611
1612 @command('^commit|ci',
1612 @command('^commit|ci',
1613 [('A', 'addremove', None,
1613 [('A', 'addremove', None,
1614 _('mark new/missing files as added/removed before committing')),
1614 _('mark new/missing files as added/removed before committing')),
1615 ('', 'close-branch', None,
1615 ('', 'close-branch', None,
1616 _('mark a branch head as closed')),
1616 _('mark a branch head as closed')),
1617 ('', 'amend', None, _('amend the parent of the working directory')),
1617 ('', 'amend', None, _('amend the parent of the working directory')),
1618 ('s', 'secret', None, _('use the secret phase for committing')),
1618 ('s', 'secret', None, _('use the secret phase for committing')),
1619 ('e', 'edit', None, _('invoke editor on commit messages')),
1619 ('e', 'edit', None, _('invoke editor on commit messages')),
1620 ('i', 'interactive', None, _('use interactive mode')),
1620 ('i', 'interactive', None, _('use interactive mode')),
1621 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1621 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1622 _('[OPTION]... [FILE]...'),
1622 _('[OPTION]... [FILE]...'),
1623 inferrepo=True)
1623 inferrepo=True)
1624 def commit(ui, repo, *pats, **opts):
1624 def commit(ui, repo, *pats, **opts):
1625 """commit the specified files or all outstanding changes
1625 """commit the specified files or all outstanding changes
1626
1626
1627 Commit changes to the given files into the repository. Unlike a
1627 Commit changes to the given files into the repository. Unlike a
1628 centralized SCM, this operation is a local operation. See
1628 centralized SCM, this operation is a local operation. See
1629 :hg:`push` for a way to actively distribute your changes.
1629 :hg:`push` for a way to actively distribute your changes.
1630
1630
1631 If a list of files is omitted, all changes reported by :hg:`status`
1631 If a list of files is omitted, all changes reported by :hg:`status`
1632 will be committed.
1632 will be committed.
1633
1633
1634 If you are committing the result of a merge, do not provide any
1634 If you are committing the result of a merge, do not provide any
1635 filenames or -I/-X filters.
1635 filenames or -I/-X filters.
1636
1636
1637 If no commit message is specified, Mercurial starts your
1637 If no commit message is specified, Mercurial starts your
1638 configured editor where you can enter a message. In case your
1638 configured editor where you can enter a message. In case your
1639 commit fails, you will find a backup of your message in
1639 commit fails, you will find a backup of your message in
1640 ``.hg/last-message.txt``.
1640 ``.hg/last-message.txt``.
1641
1641
1642 The --close-branch flag can be used to mark the current branch
1642 The --close-branch flag can be used to mark the current branch
1643 head closed. When all heads of a branch are closed, the branch
1643 head closed. When all heads of a branch are closed, the branch
1644 will be considered closed and no longer listed.
1644 will be considered closed and no longer listed.
1645
1645
1646 The --amend flag can be used to amend the parent of the
1646 The --amend flag can be used to amend the parent of the
1647 working directory with a new commit that contains the changes
1647 working directory with a new commit that contains the changes
1648 in the parent in addition to those currently reported by :hg:`status`,
1648 in the parent in addition to those currently reported by :hg:`status`,
1649 if there are any. The old commit is stored in a backup bundle in
1649 if there are any. The old commit is stored in a backup bundle in
1650 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1650 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1651 on how to restore it).
1651 on how to restore it).
1652
1652
1653 Message, user and date are taken from the amended commit unless
1653 Message, user and date are taken from the amended commit unless
1654 specified. When a message isn't specified on the command line,
1654 specified. When a message isn't specified on the command line,
1655 the editor will open with the message of the amended commit.
1655 the editor will open with the message of the amended commit.
1656
1656
1657 It is not possible to amend public changesets (see :hg:`help phases`)
1657 It is not possible to amend public changesets (see :hg:`help phases`)
1658 or changesets that have children.
1658 or changesets that have children.
1659
1659
1660 See :hg:`help dates` for a list of formats valid for -d/--date.
1660 See :hg:`help dates` for a list of formats valid for -d/--date.
1661
1661
1662 Returns 0 on success, 1 if nothing changed.
1662 Returns 0 on success, 1 if nothing changed.
1663
1663
1664 .. container:: verbose
1664 .. container:: verbose
1665
1665
1666 Examples:
1666 Examples:
1667
1667
1668 - commit all files ending in .py::
1668 - commit all files ending in .py::
1669
1669
1670 hg commit --include "set:**.py"
1670 hg commit --include "set:**.py"
1671
1671
1672 - commit all non-binary files::
1672 - commit all non-binary files::
1673
1673
1674 hg commit --exclude "set:binary()"
1674 hg commit --exclude "set:binary()"
1675
1675
1676 - amend the current commit and set the date to now::
1676 - amend the current commit and set the date to now::
1677
1677
1678 hg commit --amend --date now
1678 hg commit --amend --date now
1679 """
1679 """
1680 wlock = lock = None
1680 wlock = lock = None
1681 try:
1681 try:
1682 wlock = repo.wlock()
1682 wlock = repo.wlock()
1683 lock = repo.lock()
1683 lock = repo.lock()
1684 return _docommit(ui, repo, *pats, **opts)
1684 return _docommit(ui, repo, *pats, **opts)
1685 finally:
1685 finally:
1686 release(lock, wlock)
1686 release(lock, wlock)
1687
1687
1688 def _docommit(ui, repo, *pats, **opts):
1688 def _docommit(ui, repo, *pats, **opts):
1689 if opts.get('interactive'):
1689 if opts.get('interactive'):
1690 opts.pop('interactive')
1690 opts.pop('interactive')
1691 cmdutil.dorecord(ui, repo, commit, None, False,
1691 cmdutil.dorecord(ui, repo, commit, None, False,
1692 cmdutil.recordfilter, *pats, **opts)
1692 cmdutil.recordfilter, *pats, **opts)
1693 return
1693 return
1694
1694
1695 if opts.get('subrepos'):
1695 if opts.get('subrepos'):
1696 if opts.get('amend'):
1696 if opts.get('amend'):
1697 raise error.Abort(_('cannot amend with --subrepos'))
1697 raise error.Abort(_('cannot amend with --subrepos'))
1698 # Let --subrepos on the command line override config setting.
1698 # Let --subrepos on the command line override config setting.
1699 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1699 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1700
1700
1701 cmdutil.checkunfinished(repo, commit=True)
1701 cmdutil.checkunfinished(repo, commit=True)
1702
1702
1703 branch = repo[None].branch()
1703 branch = repo[None].branch()
1704 bheads = repo.branchheads(branch)
1704 bheads = repo.branchheads(branch)
1705
1705
1706 extra = {}
1706 extra = {}
1707 if opts.get('close_branch'):
1707 if opts.get('close_branch'):
1708 extra['close'] = 1
1708 extra['close'] = 1
1709
1709
1710 if not bheads:
1710 if not bheads:
1711 raise error.Abort(_('can only close branch heads'))
1711 raise error.Abort(_('can only close branch heads'))
1712 elif opts.get('amend'):
1712 elif opts.get('amend'):
1713 if repo[None].parents()[0].p1().branch() != branch and \
1713 if repo[None].parents()[0].p1().branch() != branch and \
1714 repo[None].parents()[0].p2().branch() != branch:
1714 repo[None].parents()[0].p2().branch() != branch:
1715 raise error.Abort(_('can only close branch heads'))
1715 raise error.Abort(_('can only close branch heads'))
1716
1716
1717 if opts.get('amend'):
1717 if opts.get('amend'):
1718 if ui.configbool('ui', 'commitsubrepos'):
1718 if ui.configbool('ui', 'commitsubrepos'):
1719 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1719 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1720
1720
1721 old = repo['.']
1721 old = repo['.']
1722 if not old.mutable():
1722 if not old.mutable():
1723 raise error.Abort(_('cannot amend public changesets'))
1723 raise error.Abort(_('cannot amend public changesets'))
1724 if len(repo[None].parents()) > 1:
1724 if len(repo[None].parents()) > 1:
1725 raise error.Abort(_('cannot amend while merging'))
1725 raise error.Abort(_('cannot amend while merging'))
1726 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1726 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1727 if not allowunstable and old.children():
1727 if not allowunstable and old.children():
1728 raise error.Abort(_('cannot amend changeset with children'))
1728 raise error.Abort(_('cannot amend changeset with children'))
1729
1729
1730 # Currently histedit gets confused if an amend happens while histedit
1730 # Currently histedit gets confused if an amend happens while histedit
1731 # is in progress. Since we have a checkunfinished command, we are
1731 # is in progress. Since we have a checkunfinished command, we are
1732 # temporarily honoring it.
1732 # temporarily honoring it.
1733 #
1733 #
1734 # Note: eventually this guard will be removed. Please do not expect
1734 # Note: eventually this guard will be removed. Please do not expect
1735 # this behavior to remain.
1735 # this behavior to remain.
1736 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1736 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1737 cmdutil.checkunfinished(repo)
1737 cmdutil.checkunfinished(repo)
1738
1738
1739 # commitfunc is used only for temporary amend commit by cmdutil.amend
1739 # commitfunc is used only for temporary amend commit by cmdutil.amend
1740 def commitfunc(ui, repo, message, match, opts):
1740 def commitfunc(ui, repo, message, match, opts):
1741 return repo.commit(message,
1741 return repo.commit(message,
1742 opts.get('user') or old.user(),
1742 opts.get('user') or old.user(),
1743 opts.get('date') or old.date(),
1743 opts.get('date') or old.date(),
1744 match,
1744 match,
1745 extra=extra)
1745 extra=extra)
1746
1746
1747 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1747 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1748 if node == old.node():
1748 if node == old.node():
1749 ui.status(_("nothing changed\n"))
1749 ui.status(_("nothing changed\n"))
1750 return 1
1750 return 1
1751 else:
1751 else:
1752 def commitfunc(ui, repo, message, match, opts):
1752 def commitfunc(ui, repo, message, match, opts):
1753 backup = ui.backupconfig('phases', 'new-commit')
1753 backup = ui.backupconfig('phases', 'new-commit')
1754 baseui = repo.baseui
1754 baseui = repo.baseui
1755 basebackup = baseui.backupconfig('phases', 'new-commit')
1755 basebackup = baseui.backupconfig('phases', 'new-commit')
1756 try:
1756 try:
1757 if opts.get('secret'):
1757 if opts.get('secret'):
1758 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1758 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1759 # Propagate to subrepos
1759 # Propagate to subrepos
1760 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1760 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1761
1761
1762 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1762 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1763 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1763 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1764 return repo.commit(message, opts.get('user'), opts.get('date'),
1764 return repo.commit(message, opts.get('user'), opts.get('date'),
1765 match,
1765 match,
1766 editor=editor,
1766 editor=editor,
1767 extra=extra)
1767 extra=extra)
1768 finally:
1768 finally:
1769 ui.restoreconfig(backup)
1769 ui.restoreconfig(backup)
1770 repo.baseui.restoreconfig(basebackup)
1770 repo.baseui.restoreconfig(basebackup)
1771
1771
1772
1772
1773 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1773 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1774
1774
1775 if not node:
1775 if not node:
1776 stat = cmdutil.postcommitstatus(repo, pats, opts)
1776 stat = cmdutil.postcommitstatus(repo, pats, opts)
1777 if stat[3]:
1777 if stat[3]:
1778 ui.status(_("nothing changed (%d missing files, see "
1778 ui.status(_("nothing changed (%d missing files, see "
1779 "'hg status')\n") % len(stat[3]))
1779 "'hg status')\n") % len(stat[3]))
1780 else:
1780 else:
1781 ui.status(_("nothing changed\n"))
1781 ui.status(_("nothing changed\n"))
1782 return 1
1782 return 1
1783
1783
1784 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1784 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1785
1785
1786 @command('config|showconfig|debugconfig',
1786 @command('config|showconfig|debugconfig',
1787 [('u', 'untrusted', None, _('show untrusted configuration options')),
1787 [('u', 'untrusted', None, _('show untrusted configuration options')),
1788 ('e', 'edit', None, _('edit user config')),
1788 ('e', 'edit', None, _('edit user config')),
1789 ('l', 'local', None, _('edit repository config')),
1789 ('l', 'local', None, _('edit repository config')),
1790 ('g', 'global', None, _('edit global config'))],
1790 ('g', 'global', None, _('edit global config'))],
1791 _('[-u] [NAME]...'),
1791 _('[-u] [NAME]...'),
1792 optionalrepo=True)
1792 optionalrepo=True)
1793 def config(ui, repo, *values, **opts):
1793 def config(ui, repo, *values, **opts):
1794 """show combined config settings from all hgrc files
1794 """show combined config settings from all hgrc files
1795
1795
1796 With no arguments, print names and values of all config items.
1796 With no arguments, print names and values of all config items.
1797
1797
1798 With one argument of the form section.name, print just the value
1798 With one argument of the form section.name, print just the value
1799 of that config item.
1799 of that config item.
1800
1800
1801 With multiple arguments, print names and values of all config
1801 With multiple arguments, print names and values of all config
1802 items with matching section names.
1802 items with matching section names.
1803
1803
1804 With --edit, start an editor on the user-level config file. With
1804 With --edit, start an editor on the user-level config file. With
1805 --global, edit the system-wide config file. With --local, edit the
1805 --global, edit the system-wide config file. With --local, edit the
1806 repository-level config file.
1806 repository-level config file.
1807
1807
1808 With --debug, the source (filename and line number) is printed
1808 With --debug, the source (filename and line number) is printed
1809 for each config item.
1809 for each config item.
1810
1810
1811 See :hg:`help config` for more information about config files.
1811 See :hg:`help config` for more information about config files.
1812
1812
1813 Returns 0 on success, 1 if NAME does not exist.
1813 Returns 0 on success, 1 if NAME does not exist.
1814
1814
1815 """
1815 """
1816
1816
1817 if opts.get('edit') or opts.get('local') or opts.get('global'):
1817 if opts.get('edit') or opts.get('local') or opts.get('global'):
1818 if opts.get('local') and opts.get('global'):
1818 if opts.get('local') and opts.get('global'):
1819 raise error.Abort(_("can't use --local and --global together"))
1819 raise error.Abort(_("can't use --local and --global together"))
1820
1820
1821 if opts.get('local'):
1821 if opts.get('local'):
1822 if not repo:
1822 if not repo:
1823 raise error.Abort(_("can't use --local outside a repository"))
1823 raise error.Abort(_("can't use --local outside a repository"))
1824 paths = [repo.join('hgrc')]
1824 paths = [repo.join('hgrc')]
1825 elif opts.get('global'):
1825 elif opts.get('global'):
1826 paths = scmutil.systemrcpath()
1826 paths = scmutil.systemrcpath()
1827 else:
1827 else:
1828 paths = scmutil.userrcpath()
1828 paths = scmutil.userrcpath()
1829
1829
1830 for f in paths:
1830 for f in paths:
1831 if os.path.exists(f):
1831 if os.path.exists(f):
1832 break
1832 break
1833 else:
1833 else:
1834 if opts.get('global'):
1834 if opts.get('global'):
1835 samplehgrc = uimod.samplehgrcs['global']
1835 samplehgrc = uimod.samplehgrcs['global']
1836 elif opts.get('local'):
1836 elif opts.get('local'):
1837 samplehgrc = uimod.samplehgrcs['local']
1837 samplehgrc = uimod.samplehgrcs['local']
1838 else:
1838 else:
1839 samplehgrc = uimod.samplehgrcs['user']
1839 samplehgrc = uimod.samplehgrcs['user']
1840
1840
1841 f = paths[0]
1841 f = paths[0]
1842 fp = open(f, "w")
1842 fp = open(f, "w")
1843 fp.write(samplehgrc)
1843 fp.write(samplehgrc)
1844 fp.close()
1844 fp.close()
1845
1845
1846 editor = ui.geteditor()
1846 editor = ui.geteditor()
1847 ui.system("%s \"%s\"" % (editor, f),
1847 ui.system("%s \"%s\"" % (editor, f),
1848 onerr=error.Abort, errprefix=_("edit failed"))
1848 onerr=error.Abort, errprefix=_("edit failed"))
1849 return
1849 return
1850
1850
1851 for f in scmutil.rcpath():
1851 for f in scmutil.rcpath():
1852 ui.debug('read config from: %s\n' % f)
1852 ui.debug('read config from: %s\n' % f)
1853 untrusted = bool(opts.get('untrusted'))
1853 untrusted = bool(opts.get('untrusted'))
1854 if values:
1854 if values:
1855 sections = [v for v in values if '.' not in v]
1855 sections = [v for v in values if '.' not in v]
1856 items = [v for v in values if '.' in v]
1856 items = [v for v in values if '.' in v]
1857 if len(items) > 1 or items and sections:
1857 if len(items) > 1 or items and sections:
1858 raise error.Abort(_('only one config item permitted'))
1858 raise error.Abort(_('only one config item permitted'))
1859 matched = False
1859 matched = False
1860 for section, name, value in ui.walkconfig(untrusted=untrusted):
1860 for section, name, value in ui.walkconfig(untrusted=untrusted):
1861 value = str(value).replace('\n', '\\n')
1861 value = str(value).replace('\n', '\\n')
1862 sectname = section + '.' + name
1862 sectname = section + '.' + name
1863 if values:
1863 if values:
1864 for v in values:
1864 for v in values:
1865 if v == section:
1865 if v == section:
1866 ui.debug('%s: ' %
1866 ui.debug('%s: ' %
1867 ui.configsource(section, name, untrusted))
1867 ui.configsource(section, name, untrusted))
1868 ui.write('%s=%s\n' % (sectname, value))
1868 ui.write('%s=%s\n' % (sectname, value))
1869 matched = True
1869 matched = True
1870 elif v == sectname:
1870 elif v == sectname:
1871 ui.debug('%s: ' %
1871 ui.debug('%s: ' %
1872 ui.configsource(section, name, untrusted))
1872 ui.configsource(section, name, untrusted))
1873 ui.write(value, '\n')
1873 ui.write(value, '\n')
1874 matched = True
1874 matched = True
1875 else:
1875 else:
1876 ui.debug('%s: ' %
1876 ui.debug('%s: ' %
1877 ui.configsource(section, name, untrusted))
1877 ui.configsource(section, name, untrusted))
1878 ui.write('%s=%s\n' % (sectname, value))
1878 ui.write('%s=%s\n' % (sectname, value))
1879 matched = True
1879 matched = True
1880 if matched:
1880 if matched:
1881 return 0
1881 return 0
1882 return 1
1882 return 1
1883
1883
1884 @command('copy|cp',
1884 @command('copy|cp',
1885 [('A', 'after', None, _('record a copy that has already occurred')),
1885 [('A', 'after', None, _('record a copy that has already occurred')),
1886 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1886 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1887 ] + walkopts + dryrunopts,
1887 ] + walkopts + dryrunopts,
1888 _('[OPTION]... [SOURCE]... DEST'))
1888 _('[OPTION]... [SOURCE]... DEST'))
1889 def copy(ui, repo, *pats, **opts):
1889 def copy(ui, repo, *pats, **opts):
1890 """mark files as copied for the next commit
1890 """mark files as copied for the next commit
1891
1891
1892 Mark dest as having copies of source files. If dest is a
1892 Mark dest as having copies of source files. If dest is a
1893 directory, copies are put in that directory. If dest is a file,
1893 directory, copies are put in that directory. If dest is a file,
1894 the source must be a single file.
1894 the source must be a single file.
1895
1895
1896 By default, this command copies the contents of files as they
1896 By default, this command copies the contents of files as they
1897 exist in the working directory. If invoked with -A/--after, the
1897 exist in the working directory. If invoked with -A/--after, the
1898 operation is recorded, but no copying is performed.
1898 operation is recorded, but no copying is performed.
1899
1899
1900 This command takes effect with the next commit. To undo a copy
1900 This command takes effect with the next commit. To undo a copy
1901 before that, see :hg:`revert`.
1901 before that, see :hg:`revert`.
1902
1902
1903 Returns 0 on success, 1 if errors are encountered.
1903 Returns 0 on success, 1 if errors are encountered.
1904 """
1904 """
1905 with repo.wlock(False):
1905 with repo.wlock(False):
1906 return cmdutil.copy(ui, repo, pats, opts)
1906 return cmdutil.copy(ui, repo, pats, opts)
1907
1907
1908 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1908 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1909 def debugancestor(ui, repo, *args):
1909 def debugancestor(ui, repo, *args):
1910 """find the ancestor revision of two revisions in a given index"""
1910 """find the ancestor revision of two revisions in a given index"""
1911 if len(args) == 3:
1911 if len(args) == 3:
1912 index, rev1, rev2 = args
1912 index, rev1, rev2 = args
1913 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1913 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1914 lookup = r.lookup
1914 lookup = r.lookup
1915 elif len(args) == 2:
1915 elif len(args) == 2:
1916 if not repo:
1916 if not repo:
1917 raise error.Abort(_("there is no Mercurial repository here "
1917 raise error.Abort(_("there is no Mercurial repository here "
1918 "(.hg not found)"))
1918 "(.hg not found)"))
1919 rev1, rev2 = args
1919 rev1, rev2 = args
1920 r = repo.changelog
1920 r = repo.changelog
1921 lookup = repo.lookup
1921 lookup = repo.lookup
1922 else:
1922 else:
1923 raise error.Abort(_('either two or three arguments required'))
1923 raise error.Abort(_('either two or three arguments required'))
1924 a = r.ancestor(lookup(rev1), lookup(rev2))
1924 a = r.ancestor(lookup(rev1), lookup(rev2))
1925 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1925 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1926
1926
1927 @command('debugbuilddag',
1927 @command('debugbuilddag',
1928 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1928 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1929 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1929 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1930 ('n', 'new-file', None, _('add new file at each rev'))],
1930 ('n', 'new-file', None, _('add new file at each rev'))],
1931 _('[OPTION]... [TEXT]'))
1931 _('[OPTION]... [TEXT]'))
1932 def debugbuilddag(ui, repo, text=None,
1932 def debugbuilddag(ui, repo, text=None,
1933 mergeable_file=False,
1933 mergeable_file=False,
1934 overwritten_file=False,
1934 overwritten_file=False,
1935 new_file=False):
1935 new_file=False):
1936 """builds a repo with a given DAG from scratch in the current empty repo
1936 """builds a repo with a given DAG from scratch in the current empty repo
1937
1937
1938 The description of the DAG is read from stdin if not given on the
1938 The description of the DAG is read from stdin if not given on the
1939 command line.
1939 command line.
1940
1940
1941 Elements:
1941 Elements:
1942
1942
1943 - "+n" is a linear run of n nodes based on the current default parent
1943 - "+n" is a linear run of n nodes based on the current default parent
1944 - "." is a single node based on the current default parent
1944 - "." is a single node based on the current default parent
1945 - "$" resets the default parent to null (implied at the start);
1945 - "$" resets the default parent to null (implied at the start);
1946 otherwise the default parent is always the last node created
1946 otherwise the default parent is always the last node created
1947 - "<p" sets the default parent to the backref p
1947 - "<p" sets the default parent to the backref p
1948 - "*p" is a fork at parent p, which is a backref
1948 - "*p" is a fork at parent p, which is a backref
1949 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1949 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1950 - "/p2" is a merge of the preceding node and p2
1950 - "/p2" is a merge of the preceding node and p2
1951 - ":tag" defines a local tag for the preceding node
1951 - ":tag" defines a local tag for the preceding node
1952 - "@branch" sets the named branch for subsequent nodes
1952 - "@branch" sets the named branch for subsequent nodes
1953 - "#...\\n" is a comment up to the end of the line
1953 - "#...\\n" is a comment up to the end of the line
1954
1954
1955 Whitespace between the above elements is ignored.
1955 Whitespace between the above elements is ignored.
1956
1956
1957 A backref is either
1957 A backref is either
1958
1958
1959 - a number n, which references the node curr-n, where curr is the current
1959 - a number n, which references the node curr-n, where curr is the current
1960 node, or
1960 node, or
1961 - the name of a local tag you placed earlier using ":tag", or
1961 - the name of a local tag you placed earlier using ":tag", or
1962 - empty to denote the default parent.
1962 - empty to denote the default parent.
1963
1963
1964 All string valued-elements are either strictly alphanumeric, or must
1964 All string valued-elements are either strictly alphanumeric, or must
1965 be enclosed in double quotes ("..."), with "\\" as escape character.
1965 be enclosed in double quotes ("..."), with "\\" as escape character.
1966 """
1966 """
1967
1967
1968 if text is None:
1968 if text is None:
1969 ui.status(_("reading DAG from stdin\n"))
1969 ui.status(_("reading DAG from stdin\n"))
1970 text = ui.fin.read()
1970 text = ui.fin.read()
1971
1971
1972 cl = repo.changelog
1972 cl = repo.changelog
1973 if len(cl) > 0:
1973 if len(cl) > 0:
1974 raise error.Abort(_('repository is not empty'))
1974 raise error.Abort(_('repository is not empty'))
1975
1975
1976 # determine number of revs in DAG
1976 # determine number of revs in DAG
1977 total = 0
1977 total = 0
1978 for type, data in dagparser.parsedag(text):
1978 for type, data in dagparser.parsedag(text):
1979 if type == 'n':
1979 if type == 'n':
1980 total += 1
1980 total += 1
1981
1981
1982 if mergeable_file:
1982 if mergeable_file:
1983 linesperrev = 2
1983 linesperrev = 2
1984 # make a file with k lines per rev
1984 # make a file with k lines per rev
1985 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1985 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1986 initialmergedlines.append("")
1986 initialmergedlines.append("")
1987
1987
1988 tags = []
1988 tags = []
1989
1989
1990 wlock = lock = tr = None
1990 wlock = lock = tr = None
1991 try:
1991 try:
1992 wlock = repo.wlock()
1992 wlock = repo.wlock()
1993 lock = repo.lock()
1993 lock = repo.lock()
1994 tr = repo.transaction("builddag")
1994 tr = repo.transaction("builddag")
1995
1995
1996 at = -1
1996 at = -1
1997 atbranch = 'default'
1997 atbranch = 'default'
1998 nodeids = []
1998 nodeids = []
1999 id = 0
1999 id = 0
2000 ui.progress(_('building'), id, unit=_('revisions'), total=total)
2000 ui.progress(_('building'), id, unit=_('revisions'), total=total)
2001 for type, data in dagparser.parsedag(text):
2001 for type, data in dagparser.parsedag(text):
2002 if type == 'n':
2002 if type == 'n':
2003 ui.note(('node %s\n' % str(data)))
2003 ui.note(('node %s\n' % str(data)))
2004 id, ps = data
2004 id, ps = data
2005
2005
2006 files = []
2006 files = []
2007 fctxs = {}
2007 fctxs = {}
2008
2008
2009 p2 = None
2009 p2 = None
2010 if mergeable_file:
2010 if mergeable_file:
2011 fn = "mf"
2011 fn = "mf"
2012 p1 = repo[ps[0]]
2012 p1 = repo[ps[0]]
2013 if len(ps) > 1:
2013 if len(ps) > 1:
2014 p2 = repo[ps[1]]
2014 p2 = repo[ps[1]]
2015 pa = p1.ancestor(p2)
2015 pa = p1.ancestor(p2)
2016 base, local, other = [x[fn].data() for x in (pa, p1,
2016 base, local, other = [x[fn].data() for x in (pa, p1,
2017 p2)]
2017 p2)]
2018 m3 = simplemerge.Merge3Text(base, local, other)
2018 m3 = simplemerge.Merge3Text(base, local, other)
2019 ml = [l.strip() for l in m3.merge_lines()]
2019 ml = [l.strip() for l in m3.merge_lines()]
2020 ml.append("")
2020 ml.append("")
2021 elif at > 0:
2021 elif at > 0:
2022 ml = p1[fn].data().split("\n")
2022 ml = p1[fn].data().split("\n")
2023 else:
2023 else:
2024 ml = initialmergedlines
2024 ml = initialmergedlines
2025 ml[id * linesperrev] += " r%i" % id
2025 ml[id * linesperrev] += " r%i" % id
2026 mergedtext = "\n".join(ml)
2026 mergedtext = "\n".join(ml)
2027 files.append(fn)
2027 files.append(fn)
2028 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
2028 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
2029
2029
2030 if overwritten_file:
2030 if overwritten_file:
2031 fn = "of"
2031 fn = "of"
2032 files.append(fn)
2032 files.append(fn)
2033 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
2033 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
2034
2034
2035 if new_file:
2035 if new_file:
2036 fn = "nf%i" % id
2036 fn = "nf%i" % id
2037 files.append(fn)
2037 files.append(fn)
2038 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
2038 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
2039 if len(ps) > 1:
2039 if len(ps) > 1:
2040 if not p2:
2040 if not p2:
2041 p2 = repo[ps[1]]
2041 p2 = repo[ps[1]]
2042 for fn in p2:
2042 for fn in p2:
2043 if fn.startswith("nf"):
2043 if fn.startswith("nf"):
2044 files.append(fn)
2044 files.append(fn)
2045 fctxs[fn] = p2[fn]
2045 fctxs[fn] = p2[fn]
2046
2046
2047 def fctxfn(repo, cx, path):
2047 def fctxfn(repo, cx, path):
2048 return fctxs.get(path)
2048 return fctxs.get(path)
2049
2049
2050 if len(ps) == 0 or ps[0] < 0:
2050 if len(ps) == 0 or ps[0] < 0:
2051 pars = [None, None]
2051 pars = [None, None]
2052 elif len(ps) == 1:
2052 elif len(ps) == 1:
2053 pars = [nodeids[ps[0]], None]
2053 pars = [nodeids[ps[0]], None]
2054 else:
2054 else:
2055 pars = [nodeids[p] for p in ps]
2055 pars = [nodeids[p] for p in ps]
2056 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
2056 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
2057 date=(id, 0),
2057 date=(id, 0),
2058 user="debugbuilddag",
2058 user="debugbuilddag",
2059 extra={'branch': atbranch})
2059 extra={'branch': atbranch})
2060 nodeid = repo.commitctx(cx)
2060 nodeid = repo.commitctx(cx)
2061 nodeids.append(nodeid)
2061 nodeids.append(nodeid)
2062 at = id
2062 at = id
2063 elif type == 'l':
2063 elif type == 'l':
2064 id, name = data
2064 id, name = data
2065 ui.note(('tag %s\n' % name))
2065 ui.note(('tag %s\n' % name))
2066 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
2066 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
2067 elif type == 'a':
2067 elif type == 'a':
2068 ui.note(('branch %s\n' % data))
2068 ui.note(('branch %s\n' % data))
2069 atbranch = data
2069 atbranch = data
2070 ui.progress(_('building'), id, unit=_('revisions'), total=total)
2070 ui.progress(_('building'), id, unit=_('revisions'), total=total)
2071 tr.close()
2071 tr.close()
2072
2072
2073 if tags:
2073 if tags:
2074 repo.vfs.write("localtags", "".join(tags))
2074 repo.vfs.write("localtags", "".join(tags))
2075 finally:
2075 finally:
2076 ui.progress(_('building'), None)
2076 ui.progress(_('building'), None)
2077 release(tr, lock, wlock)
2077 release(tr, lock, wlock)
2078
2078
2079 @command('debugbundle',
2079 @command('debugbundle',
2080 [('a', 'all', None, _('show all details')),
2080 [('a', 'all', None, _('show all details')),
2081 ('', 'spec', None, _('print the bundlespec of the bundle'))],
2081 ('', 'spec', None, _('print the bundlespec of the bundle'))],
2082 _('FILE'),
2082 _('FILE'),
2083 norepo=True)
2083 norepo=True)
2084 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
2084 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
2085 """lists the contents of a bundle"""
2085 """lists the contents of a bundle"""
2086 with hg.openpath(ui, bundlepath) as f:
2086 with hg.openpath(ui, bundlepath) as f:
2087 if spec:
2087 if spec:
2088 spec = exchange.getbundlespec(ui, f)
2088 spec = exchange.getbundlespec(ui, f)
2089 ui.write('%s\n' % spec)
2089 ui.write('%s\n' % spec)
2090 return
2090 return
2091
2091
2092 gen = exchange.readbundle(ui, f, bundlepath)
2092 gen = exchange.readbundle(ui, f, bundlepath)
2093 if isinstance(gen, bundle2.unbundle20):
2093 if isinstance(gen, bundle2.unbundle20):
2094 return _debugbundle2(ui, gen, all=all, **opts)
2094 return _debugbundle2(ui, gen, all=all, **opts)
2095 _debugchangegroup(ui, gen, all=all, **opts)
2095 _debugchangegroup(ui, gen, all=all, **opts)
2096
2096
2097 def _debugchangegroup(ui, gen, all=None, indent=0, **opts):
2097 def _debugchangegroup(ui, gen, all=None, indent=0, **opts):
2098 indent_string = ' ' * indent
2098 indent_string = ' ' * indent
2099 if all:
2099 if all:
2100 ui.write(("%sformat: id, p1, p2, cset, delta base, len(delta)\n")
2100 ui.write(("%sformat: id, p1, p2, cset, delta base, len(delta)\n")
2101 % indent_string)
2101 % indent_string)
2102
2102
2103 def showchunks(named):
2103 def showchunks(named):
2104 ui.write("\n%s%s\n" % (indent_string, named))
2104 ui.write("\n%s%s\n" % (indent_string, named))
2105 chain = None
2105 chain = None
2106 for chunkdata in iter(lambda: gen.deltachunk(chain), {}):
2106 for chunkdata in iter(lambda: gen.deltachunk(chain), {}):
2107 node = chunkdata['node']
2107 node = chunkdata['node']
2108 p1 = chunkdata['p1']
2108 p1 = chunkdata['p1']
2109 p2 = chunkdata['p2']
2109 p2 = chunkdata['p2']
2110 cs = chunkdata['cs']
2110 cs = chunkdata['cs']
2111 deltabase = chunkdata['deltabase']
2111 deltabase = chunkdata['deltabase']
2112 delta = chunkdata['delta']
2112 delta = chunkdata['delta']
2113 ui.write("%s%s %s %s %s %s %s\n" %
2113 ui.write("%s%s %s %s %s %s %s\n" %
2114 (indent_string, hex(node), hex(p1), hex(p2),
2114 (indent_string, hex(node), hex(p1), hex(p2),
2115 hex(cs), hex(deltabase), len(delta)))
2115 hex(cs), hex(deltabase), len(delta)))
2116 chain = node
2116 chain = node
2117
2117
2118 chunkdata = gen.changelogheader()
2118 chunkdata = gen.changelogheader()
2119 showchunks("changelog")
2119 showchunks("changelog")
2120 chunkdata = gen.manifestheader()
2120 chunkdata = gen.manifestheader()
2121 showchunks("manifest")
2121 showchunks("manifest")
2122 for chunkdata in iter(gen.filelogheader, {}):
2122 for chunkdata in iter(gen.filelogheader, {}):
2123 fname = chunkdata['filename']
2123 fname = chunkdata['filename']
2124 showchunks(fname)
2124 showchunks(fname)
2125 else:
2125 else:
2126 if isinstance(gen, bundle2.unbundle20):
2126 if isinstance(gen, bundle2.unbundle20):
2127 raise error.Abort(_('use debugbundle2 for this file'))
2127 raise error.Abort(_('use debugbundle2 for this file'))
2128 chunkdata = gen.changelogheader()
2128 chunkdata = gen.changelogheader()
2129 chain = None
2129 chain = None
2130 for chunkdata in iter(lambda: gen.deltachunk(chain), {}):
2130 for chunkdata in iter(lambda: gen.deltachunk(chain), {}):
2131 node = chunkdata['node']
2131 node = chunkdata['node']
2132 ui.write("%s%s\n" % (indent_string, hex(node)))
2132 ui.write("%s%s\n" % (indent_string, hex(node)))
2133 chain = node
2133 chain = node
2134
2134
2135 def _debugbundle2(ui, gen, all=None, **opts):
2135 def _debugbundle2(ui, gen, all=None, **opts):
2136 """lists the contents of a bundle2"""
2136 """lists the contents of a bundle2"""
2137 if not isinstance(gen, bundle2.unbundle20):
2137 if not isinstance(gen, bundle2.unbundle20):
2138 raise error.Abort(_('not a bundle2 file'))
2138 raise error.Abort(_('not a bundle2 file'))
2139 ui.write(('Stream params: %s\n' % repr(gen.params)))
2139 ui.write(('Stream params: %s\n' % repr(gen.params)))
2140 for part in gen.iterparts():
2140 for part in gen.iterparts():
2141 ui.write('%s -- %r\n' % (part.type, repr(part.params)))
2141 ui.write('%s -- %r\n' % (part.type, repr(part.params)))
2142 if part.type == 'changegroup':
2142 if part.type == 'changegroup':
2143 version = part.params.get('version', '01')
2143 version = part.params.get('version', '01')
2144 cg = changegroup.getunbundler(version, part, 'UN')
2144 cg = changegroup.getunbundler(version, part, 'UN')
2145 _debugchangegroup(ui, cg, all=all, indent=4, **opts)
2145 _debugchangegroup(ui, cg, all=all, indent=4, **opts)
2146
2146
2147 @command('debugcreatestreamclonebundle', [], 'FILE')
2147 @command('debugcreatestreamclonebundle', [], 'FILE')
2148 def debugcreatestreamclonebundle(ui, repo, fname):
2148 def debugcreatestreamclonebundle(ui, repo, fname):
2149 """create a stream clone bundle file
2149 """create a stream clone bundle file
2150
2150
2151 Stream bundles are special bundles that are essentially archives of
2151 Stream bundles are special bundles that are essentially archives of
2152 revlog files. They are commonly used for cloning very quickly.
2152 revlog files. They are commonly used for cloning very quickly.
2153 """
2153 """
2154 requirements, gen = streamclone.generatebundlev1(repo)
2154 requirements, gen = streamclone.generatebundlev1(repo)
2155 changegroup.writechunks(ui, gen, fname)
2155 changegroup.writechunks(ui, gen, fname)
2156
2156
2157 ui.write(_('bundle requirements: %s\n') % ', '.join(sorted(requirements)))
2157 ui.write(_('bundle requirements: %s\n') % ', '.join(sorted(requirements)))
2158
2158
2159 @command('debugapplystreamclonebundle', [], 'FILE')
2159 @command('debugapplystreamclonebundle', [], 'FILE')
2160 def debugapplystreamclonebundle(ui, repo, fname):
2160 def debugapplystreamclonebundle(ui, repo, fname):
2161 """apply a stream clone bundle file"""
2161 """apply a stream clone bundle file"""
2162 f = hg.openpath(ui, fname)
2162 f = hg.openpath(ui, fname)
2163 gen = exchange.readbundle(ui, f, fname)
2163 gen = exchange.readbundle(ui, f, fname)
2164 gen.apply(repo)
2164 gen.apply(repo)
2165
2165
2166 @command('debugcheckstate', [], '')
2166 @command('debugcheckstate', [], '')
2167 def debugcheckstate(ui, repo):
2167 def debugcheckstate(ui, repo):
2168 """validate the correctness of the current dirstate"""
2168 """validate the correctness of the current dirstate"""
2169 parent1, parent2 = repo.dirstate.parents()
2169 parent1, parent2 = repo.dirstate.parents()
2170 m1 = repo[parent1].manifest()
2170 m1 = repo[parent1].manifest()
2171 m2 = repo[parent2].manifest()
2171 m2 = repo[parent2].manifest()
2172 errors = 0
2172 errors = 0
2173 for f in repo.dirstate:
2173 for f in repo.dirstate:
2174 state = repo.dirstate[f]
2174 state = repo.dirstate[f]
2175 if state in "nr" and f not in m1:
2175 if state in "nr" and f not in m1:
2176 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
2176 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
2177 errors += 1
2177 errors += 1
2178 if state in "a" and f in m1:
2178 if state in "a" and f in m1:
2179 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
2179 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
2180 errors += 1
2180 errors += 1
2181 if state in "m" and f not in m1 and f not in m2:
2181 if state in "m" and f not in m1 and f not in m2:
2182 ui.warn(_("%s in state %s, but not in either manifest\n") %
2182 ui.warn(_("%s in state %s, but not in either manifest\n") %
2183 (f, state))
2183 (f, state))
2184 errors += 1
2184 errors += 1
2185 for f in m1:
2185 for f in m1:
2186 state = repo.dirstate[f]
2186 state = repo.dirstate[f]
2187 if state not in "nrm":
2187 if state not in "nrm":
2188 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
2188 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
2189 errors += 1
2189 errors += 1
2190 if errors:
2190 if errors:
2191 error = _(".hg/dirstate inconsistent with current parent's manifest")
2191 error = _(".hg/dirstate inconsistent with current parent's manifest")
2192 raise error.Abort(error)
2192 raise error.Abort(error)
2193
2193
2194 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
2194 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
2195 def debugcommands(ui, cmd='', *args):
2195 def debugcommands(ui, cmd='', *args):
2196 """list all available commands and options"""
2196 """list all available commands and options"""
2197 for cmd, vals in sorted(table.iteritems()):
2197 for cmd, vals in sorted(table.iteritems()):
2198 cmd = cmd.split('|')[0].strip('^')
2198 cmd = cmd.split('|')[0].strip('^')
2199 opts = ', '.join([i[1] for i in vals[1]])
2199 opts = ', '.join([i[1] for i in vals[1]])
2200 ui.write('%s: %s\n' % (cmd, opts))
2200 ui.write('%s: %s\n' % (cmd, opts))
2201
2201
2202 @command('debugcomplete',
2202 @command('debugcomplete',
2203 [('o', 'options', None, _('show the command options'))],
2203 [('o', 'options', None, _('show the command options'))],
2204 _('[-o] CMD'),
2204 _('[-o] CMD'),
2205 norepo=True)
2205 norepo=True)
2206 def debugcomplete(ui, cmd='', **opts):
2206 def debugcomplete(ui, cmd='', **opts):
2207 """returns the completion list associated with the given command"""
2207 """returns the completion list associated with the given command"""
2208
2208
2209 if opts.get('options'):
2209 if opts.get('options'):
2210 options = []
2210 options = []
2211 otables = [globalopts]
2211 otables = [globalopts]
2212 if cmd:
2212 if cmd:
2213 aliases, entry = cmdutil.findcmd(cmd, table, False)
2213 aliases, entry = cmdutil.findcmd(cmd, table, False)
2214 otables.append(entry[1])
2214 otables.append(entry[1])
2215 for t in otables:
2215 for t in otables:
2216 for o in t:
2216 for o in t:
2217 if "(DEPRECATED)" in o[3]:
2217 if "(DEPRECATED)" in o[3]:
2218 continue
2218 continue
2219 if o[0]:
2219 if o[0]:
2220 options.append('-%s' % o[0])
2220 options.append('-%s' % o[0])
2221 options.append('--%s' % o[1])
2221 options.append('--%s' % o[1])
2222 ui.write("%s\n" % "\n".join(options))
2222 ui.write("%s\n" % "\n".join(options))
2223 return
2223 return
2224
2224
2225 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2225 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2226 if ui.verbose:
2226 if ui.verbose:
2227 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
2227 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
2228 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
2228 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
2229
2229
2230 @command('debugdag',
2230 @command('debugdag',
2231 [('t', 'tags', None, _('use tags as labels')),
2231 [('t', 'tags', None, _('use tags as labels')),
2232 ('b', 'branches', None, _('annotate with branch names')),
2232 ('b', 'branches', None, _('annotate with branch names')),
2233 ('', 'dots', None, _('use dots for runs')),
2233 ('', 'dots', None, _('use dots for runs')),
2234 ('s', 'spaces', None, _('separate elements by spaces'))],
2234 ('s', 'spaces', None, _('separate elements by spaces'))],
2235 _('[OPTION]... [FILE [REV]...]'),
2235 _('[OPTION]... [FILE [REV]...]'),
2236 optionalrepo=True)
2236 optionalrepo=True)
2237 def debugdag(ui, repo, file_=None, *revs, **opts):
2237 def debugdag(ui, repo, file_=None, *revs, **opts):
2238 """format the changelog or an index DAG as a concise textual description
2238 """format the changelog or an index DAG as a concise textual description
2239
2239
2240 If you pass a revlog index, the revlog's DAG is emitted. If you list
2240 If you pass a revlog index, the revlog's DAG is emitted. If you list
2241 revision numbers, they get labeled in the output as rN.
2241 revision numbers, they get labeled in the output as rN.
2242
2242
2243 Otherwise, the changelog DAG of the current repo is emitted.
2243 Otherwise, the changelog DAG of the current repo is emitted.
2244 """
2244 """
2245 spaces = opts.get('spaces')
2245 spaces = opts.get('spaces')
2246 dots = opts.get('dots')
2246 dots = opts.get('dots')
2247 if file_:
2247 if file_:
2248 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2248 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2249 revs = set((int(r) for r in revs))
2249 revs = set((int(r) for r in revs))
2250 def events():
2250 def events():
2251 for r in rlog:
2251 for r in rlog:
2252 yield 'n', (r, list(p for p in rlog.parentrevs(r)
2252 yield 'n', (r, list(p for p in rlog.parentrevs(r)
2253 if p != -1))
2253 if p != -1))
2254 if r in revs:
2254 if r in revs:
2255 yield 'l', (r, "r%i" % r)
2255 yield 'l', (r, "r%i" % r)
2256 elif repo:
2256 elif repo:
2257 cl = repo.changelog
2257 cl = repo.changelog
2258 tags = opts.get('tags')
2258 tags = opts.get('tags')
2259 branches = opts.get('branches')
2259 branches = opts.get('branches')
2260 if tags:
2260 if tags:
2261 labels = {}
2261 labels = {}
2262 for l, n in repo.tags().items():
2262 for l, n in repo.tags().items():
2263 labels.setdefault(cl.rev(n), []).append(l)
2263 labels.setdefault(cl.rev(n), []).append(l)
2264 def events():
2264 def events():
2265 b = "default"
2265 b = "default"
2266 for r in cl:
2266 for r in cl:
2267 if branches:
2267 if branches:
2268 newb = cl.read(cl.node(r))[5]['branch']
2268 newb = cl.read(cl.node(r))[5]['branch']
2269 if newb != b:
2269 if newb != b:
2270 yield 'a', newb
2270 yield 'a', newb
2271 b = newb
2271 b = newb
2272 yield 'n', (r, list(p for p in cl.parentrevs(r)
2272 yield 'n', (r, list(p for p in cl.parentrevs(r)
2273 if p != -1))
2273 if p != -1))
2274 if tags:
2274 if tags:
2275 ls = labels.get(r)
2275 ls = labels.get(r)
2276 if ls:
2276 if ls:
2277 for l in ls:
2277 for l in ls:
2278 yield 'l', (r, l)
2278 yield 'l', (r, l)
2279 else:
2279 else:
2280 raise error.Abort(_('need repo for changelog dag'))
2280 raise error.Abort(_('need repo for changelog dag'))
2281
2281
2282 for line in dagparser.dagtextlines(events(),
2282 for line in dagparser.dagtextlines(events(),
2283 addspaces=spaces,
2283 addspaces=spaces,
2284 wraplabels=True,
2284 wraplabels=True,
2285 wrapannotations=True,
2285 wrapannotations=True,
2286 wrapnonlinear=dots,
2286 wrapnonlinear=dots,
2287 usedots=dots,
2287 usedots=dots,
2288 maxlinewidth=70):
2288 maxlinewidth=70):
2289 ui.write(line)
2289 ui.write(line)
2290 ui.write("\n")
2290 ui.write("\n")
2291
2291
2292 @command('debugdata', debugrevlogopts, _('-c|-m|FILE REV'))
2292 @command('debugdata', debugrevlogopts, _('-c|-m|FILE REV'))
2293 def debugdata(ui, repo, file_, rev=None, **opts):
2293 def debugdata(ui, repo, file_, rev=None, **opts):
2294 """dump the contents of a data file revision"""
2294 """dump the contents of a data file revision"""
2295 if opts.get('changelog') or opts.get('manifest') or opts.get('dir'):
2295 if opts.get('changelog') or opts.get('manifest') or opts.get('dir'):
2296 if rev is not None:
2296 if rev is not None:
2297 raise error.CommandError('debugdata', _('invalid arguments'))
2297 raise error.CommandError('debugdata', _('invalid arguments'))
2298 file_, rev = None, file_
2298 file_, rev = None, file_
2299 elif rev is None:
2299 elif rev is None:
2300 raise error.CommandError('debugdata', _('invalid arguments'))
2300 raise error.CommandError('debugdata', _('invalid arguments'))
2301 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
2301 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
2302 try:
2302 try:
2303 ui.write(r.revision(r.lookup(rev)))
2303 ui.write(r.revision(r.lookup(rev)))
2304 except KeyError:
2304 except KeyError:
2305 raise error.Abort(_('invalid revision identifier %s') % rev)
2305 raise error.Abort(_('invalid revision identifier %s') % rev)
2306
2306
2307 @command('debugdate',
2307 @command('debugdate',
2308 [('e', 'extended', None, _('try extended date formats'))],
2308 [('e', 'extended', None, _('try extended date formats'))],
2309 _('[-e] DATE [RANGE]'),
2309 _('[-e] DATE [RANGE]'),
2310 norepo=True, optionalrepo=True)
2310 norepo=True, optionalrepo=True)
2311 def debugdate(ui, date, range=None, **opts):
2311 def debugdate(ui, date, range=None, **opts):
2312 """parse and display a date"""
2312 """parse and display a date"""
2313 if opts["extended"]:
2313 if opts["extended"]:
2314 d = util.parsedate(date, util.extendeddateformats)
2314 d = util.parsedate(date, util.extendeddateformats)
2315 else:
2315 else:
2316 d = util.parsedate(date)
2316 d = util.parsedate(date)
2317 ui.write(("internal: %s %s\n") % d)
2317 ui.write(("internal: %s %s\n") % d)
2318 ui.write(("standard: %s\n") % util.datestr(d))
2318 ui.write(("standard: %s\n") % util.datestr(d))
2319 if range:
2319 if range:
2320 m = util.matchdate(range)
2320 m = util.matchdate(range)
2321 ui.write(("match: %s\n") % m(d[0]))
2321 ui.write(("match: %s\n") % m(d[0]))
2322
2322
2323 @command('debugdiscovery',
2323 @command('debugdiscovery',
2324 [('', 'old', None, _('use old-style discovery')),
2324 [('', 'old', None, _('use old-style discovery')),
2325 ('', 'nonheads', None,
2325 ('', 'nonheads', None,
2326 _('use old-style discovery with non-heads included')),
2326 _('use old-style discovery with non-heads included')),
2327 ] + remoteopts,
2327 ] + remoteopts,
2328 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
2328 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
2329 def debugdiscovery(ui, repo, remoteurl="default", **opts):
2329 def debugdiscovery(ui, repo, remoteurl="default", **opts):
2330 """runs the changeset discovery protocol in isolation"""
2330 """runs the changeset discovery protocol in isolation"""
2331 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
2331 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
2332 opts.get('branch'))
2332 opts.get('branch'))
2333 remote = hg.peer(repo, opts, remoteurl)
2333 remote = hg.peer(repo, opts, remoteurl)
2334 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
2334 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
2335
2335
2336 # make sure tests are repeatable
2336 # make sure tests are repeatable
2337 random.seed(12323)
2337 random.seed(12323)
2338
2338
2339 def doit(localheads, remoteheads, remote=remote):
2339 def doit(localheads, remoteheads, remote=remote):
2340 if opts.get('old'):
2340 if opts.get('old'):
2341 if localheads:
2341 if localheads:
2342 raise error.Abort('cannot use localheads with old style '
2342 raise error.Abort('cannot use localheads with old style '
2343 'discovery')
2343 'discovery')
2344 if not util.safehasattr(remote, 'branches'):
2344 if not util.safehasattr(remote, 'branches'):
2345 # enable in-client legacy support
2345 # enable in-client legacy support
2346 remote = localrepo.locallegacypeer(remote.local())
2346 remote = localrepo.locallegacypeer(remote.local())
2347 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2347 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2348 force=True)
2348 force=True)
2349 common = set(common)
2349 common = set(common)
2350 if not opts.get('nonheads'):
2350 if not opts.get('nonheads'):
2351 ui.write(("unpruned common: %s\n") %
2351 ui.write(("unpruned common: %s\n") %
2352 " ".join(sorted(short(n) for n in common)))
2352 " ".join(sorted(short(n) for n in common)))
2353 dag = dagutil.revlogdag(repo.changelog)
2353 dag = dagutil.revlogdag(repo.changelog)
2354 all = dag.ancestorset(dag.internalizeall(common))
2354 all = dag.ancestorset(dag.internalizeall(common))
2355 common = dag.externalizeall(dag.headsetofconnecteds(all))
2355 common = dag.externalizeall(dag.headsetofconnecteds(all))
2356 else:
2356 else:
2357 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2357 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2358 common = set(common)
2358 common = set(common)
2359 rheads = set(hds)
2359 rheads = set(hds)
2360 lheads = set(repo.heads())
2360 lheads = set(repo.heads())
2361 ui.write(("common heads: %s\n") %
2361 ui.write(("common heads: %s\n") %
2362 " ".join(sorted(short(n) for n in common)))
2362 " ".join(sorted(short(n) for n in common)))
2363 if lheads <= common:
2363 if lheads <= common:
2364 ui.write(("local is subset\n"))
2364 ui.write(("local is subset\n"))
2365 elif rheads <= common:
2365 elif rheads <= common:
2366 ui.write(("remote is subset\n"))
2366 ui.write(("remote is subset\n"))
2367
2367
2368 serverlogs = opts.get('serverlog')
2368 serverlogs = opts.get('serverlog')
2369 if serverlogs:
2369 if serverlogs:
2370 for filename in serverlogs:
2370 for filename in serverlogs:
2371 with open(filename, 'r') as logfile:
2371 with open(filename, 'r') as logfile:
2372 line = logfile.readline()
2372 line = logfile.readline()
2373 while line:
2373 while line:
2374 parts = line.strip().split(';')
2374 parts = line.strip().split(';')
2375 op = parts[1]
2375 op = parts[1]
2376 if op == 'cg':
2376 if op == 'cg':
2377 pass
2377 pass
2378 elif op == 'cgss':
2378 elif op == 'cgss':
2379 doit(parts[2].split(' '), parts[3].split(' '))
2379 doit(parts[2].split(' '), parts[3].split(' '))
2380 elif op == 'unb':
2380 elif op == 'unb':
2381 doit(parts[3].split(' '), parts[2].split(' '))
2381 doit(parts[3].split(' '), parts[2].split(' '))
2382 line = logfile.readline()
2382 line = logfile.readline()
2383 else:
2383 else:
2384 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2384 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2385 opts.get('remote_head'))
2385 opts.get('remote_head'))
2386 localrevs = opts.get('local_head')
2386 localrevs = opts.get('local_head')
2387 doit(localrevs, remoterevs)
2387 doit(localrevs, remoterevs)
2388
2388
2389 @command('debugextensions', formatteropts, [], norepo=True)
2389 @command('debugextensions', formatteropts, [], norepo=True)
2390 def debugextensions(ui, **opts):
2390 def debugextensions(ui, **opts):
2391 '''show information about active extensions'''
2391 '''show information about active extensions'''
2392 exts = extensions.extensions(ui)
2392 exts = extensions.extensions(ui)
2393 hgver = util.version()
2393 hgver = util.version()
2394 fm = ui.formatter('debugextensions', opts)
2394 fm = ui.formatter('debugextensions', opts)
2395 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
2395 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
2396 extsource = extmod.__file__
2396 extsource = extmod.__file__
2397 exttestedwith = getattr(extmod, 'testedwith', '').split()
2397 exttestedwith = getattr(extmod, 'testedwith', '').split()
2398 extbuglink = getattr(extmod, 'buglink', None)
2398 extbuglink = getattr(extmod, 'buglink', None)
2399
2399
2400 fm.startitem()
2400 fm.startitem()
2401
2401
2402 if ui.quiet or ui.verbose:
2402 if ui.quiet or ui.verbose:
2403 fm.write('name', '%s\n', extname)
2403 fm.write('name', '%s\n', extname)
2404 else:
2404 else:
2405 fm.write('name', '%s', extname)
2405 fm.write('name', '%s', extname)
2406 if not exttestedwith:
2406 if not exttestedwith:
2407 fm.plain(_(' (untested!)\n'))
2407 fm.plain(_(' (untested!)\n'))
2408 elif exttestedwith == ['internal'] or hgver in exttestedwith:
2408 elif exttestedwith == ['internal'] or hgver in exttestedwith:
2409 fm.plain('\n')
2409 fm.plain('\n')
2410 else:
2410 else:
2411 lasttestedversion = exttestedwith[-1]
2411 lasttestedversion = exttestedwith[-1]
2412 fm.plain(' (%s!)\n' % lasttestedversion)
2412 fm.plain(' (%s!)\n' % lasttestedversion)
2413
2413
2414 fm.condwrite(ui.verbose and extsource, 'source',
2414 fm.condwrite(ui.verbose and extsource, 'source',
2415 _(' location: %s\n'), extsource or "")
2415 _(' location: %s\n'), extsource or "")
2416
2416
2417 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
2417 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
2418 _(' tested with: %s\n'),
2418 _(' tested with: %s\n'),
2419 fm.formatlist(exttestedwith, name='ver'))
2419 fm.formatlist(exttestedwith, name='ver'))
2420
2420
2421 fm.condwrite(ui.verbose and extbuglink, 'buglink',
2421 fm.condwrite(ui.verbose and extbuglink, 'buglink',
2422 _(' bug reporting: %s\n'), extbuglink or "")
2422 _(' bug reporting: %s\n'), extbuglink or "")
2423
2423
2424 fm.end()
2424 fm.end()
2425
2425
2426 @command('debugfileset',
2426 @command('debugfileset',
2427 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2427 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2428 _('[-r REV] FILESPEC'))
2428 _('[-r REV] FILESPEC'))
2429 def debugfileset(ui, repo, expr, **opts):
2429 def debugfileset(ui, repo, expr, **opts):
2430 '''parse and apply a fileset specification'''
2430 '''parse and apply a fileset specification'''
2431 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2431 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2432 if ui.verbose:
2432 if ui.verbose:
2433 tree = fileset.parse(expr)
2433 tree = fileset.parse(expr)
2434 ui.note(fileset.prettyformat(tree), "\n")
2434 ui.note(fileset.prettyformat(tree), "\n")
2435
2435
2436 for f in ctx.getfileset(expr):
2436 for f in ctx.getfileset(expr):
2437 ui.write("%s\n" % f)
2437 ui.write("%s\n" % f)
2438
2438
2439 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2439 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2440 def debugfsinfo(ui, path="."):
2440 def debugfsinfo(ui, path="."):
2441 """show information detected about current filesystem"""
2441 """show information detected about current filesystem"""
2442 util.writefile('.debugfsinfo', '')
2442 util.writefile('.debugfsinfo', '')
2443 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2443 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2444 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2444 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2445 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2445 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2446 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2446 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2447 and 'yes' or 'no'))
2447 and 'yes' or 'no'))
2448 os.unlink('.debugfsinfo')
2448 os.unlink('.debugfsinfo')
2449
2449
2450 @command('debuggetbundle',
2450 @command('debuggetbundle',
2451 [('H', 'head', [], _('id of head node'), _('ID')),
2451 [('H', 'head', [], _('id of head node'), _('ID')),
2452 ('C', 'common', [], _('id of common node'), _('ID')),
2452 ('C', 'common', [], _('id of common node'), _('ID')),
2453 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2453 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2454 _('REPO FILE [-H|-C ID]...'),
2454 _('REPO FILE [-H|-C ID]...'),
2455 norepo=True)
2455 norepo=True)
2456 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2456 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2457 """retrieves a bundle from a repo
2457 """retrieves a bundle from a repo
2458
2458
2459 Every ID must be a full-length hex node id string. Saves the bundle to the
2459 Every ID must be a full-length hex node id string. Saves the bundle to the
2460 given file.
2460 given file.
2461 """
2461 """
2462 repo = hg.peer(ui, opts, repopath)
2462 repo = hg.peer(ui, opts, repopath)
2463 if not repo.capable('getbundle'):
2463 if not repo.capable('getbundle'):
2464 raise error.Abort("getbundle() not supported by target repository")
2464 raise error.Abort("getbundle() not supported by target repository")
2465 args = {}
2465 args = {}
2466 if common:
2466 if common:
2467 args['common'] = [bin(s) for s in common]
2467 args['common'] = [bin(s) for s in common]
2468 if head:
2468 if head:
2469 args['heads'] = [bin(s) for s in head]
2469 args['heads'] = [bin(s) for s in head]
2470 # TODO: get desired bundlecaps from command line.
2470 # TODO: get desired bundlecaps from command line.
2471 args['bundlecaps'] = None
2471 args['bundlecaps'] = None
2472 bundle = repo.getbundle('debug', **args)
2472 bundle = repo.getbundle('debug', **args)
2473
2473
2474 bundletype = opts.get('type', 'bzip2').lower()
2474 bundletype = opts.get('type', 'bzip2').lower()
2475 btypes = {'none': 'HG10UN',
2475 btypes = {'none': 'HG10UN',
2476 'bzip2': 'HG10BZ',
2476 'bzip2': 'HG10BZ',
2477 'gzip': 'HG10GZ',
2477 'gzip': 'HG10GZ',
2478 'bundle2': 'HG20'}
2478 'bundle2': 'HG20'}
2479 bundletype = btypes.get(bundletype)
2479 bundletype = btypes.get(bundletype)
2480 if bundletype not in bundle2.bundletypes:
2480 if bundletype not in bundle2.bundletypes:
2481 raise error.Abort(_('unknown bundle type specified with --type'))
2481 raise error.Abort(_('unknown bundle type specified with --type'))
2482 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
2482 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
2483
2483
2484 @command('debugignore', [], '[FILE]')
2484 @command('debugignore', [], '[FILE]')
2485 def debugignore(ui, repo, *files, **opts):
2485 def debugignore(ui, repo, *files, **opts):
2486 """display the combined ignore pattern and information about ignored files
2486 """display the combined ignore pattern and information about ignored files
2487
2487
2488 With no argument display the combined ignore pattern.
2488 With no argument display the combined ignore pattern.
2489
2489
2490 Given space separated file names, shows if the given file is ignored and
2490 Given space separated file names, shows if the given file is ignored and
2491 if so, show the ignore rule (file and line number) that matched it.
2491 if so, show the ignore rule (file and line number) that matched it.
2492 """
2492 """
2493 ignore = repo.dirstate._ignore
2493 ignore = repo.dirstate._ignore
2494 if not files:
2494 if not files:
2495 # Show all the patterns
2495 # Show all the patterns
2496 includepat = getattr(ignore, 'includepat', None)
2496 includepat = getattr(ignore, 'includepat', None)
2497 if includepat is not None:
2497 if includepat is not None:
2498 ui.write("%s\n" % includepat)
2498 ui.write("%s\n" % includepat)
2499 else:
2499 else:
2500 raise error.Abort(_("no ignore patterns found"))
2500 raise error.Abort(_("no ignore patterns found"))
2501 else:
2501 else:
2502 for f in files:
2502 for f in files:
2503 nf = util.normpath(f)
2503 nf = util.normpath(f)
2504 ignored = None
2504 ignored = None
2505 ignoredata = None
2505 ignoredata = None
2506 if nf != '.':
2506 if nf != '.':
2507 if ignore(nf):
2507 if ignore(nf):
2508 ignored = nf
2508 ignored = nf
2509 ignoredata = repo.dirstate._ignorefileandline(nf)
2509 ignoredata = repo.dirstate._ignorefileandline(nf)
2510 else:
2510 else:
2511 for p in util.finddirs(nf):
2511 for p in util.finddirs(nf):
2512 if ignore(p):
2512 if ignore(p):
2513 ignored = p
2513 ignored = p
2514 ignoredata = repo.dirstate._ignorefileandline(p)
2514 ignoredata = repo.dirstate._ignorefileandline(p)
2515 break
2515 break
2516 if ignored:
2516 if ignored:
2517 if ignored == nf:
2517 if ignored == nf:
2518 ui.write(_("%s is ignored\n") % f)
2518 ui.write(_("%s is ignored\n") % f)
2519 else:
2519 else:
2520 ui.write(_("%s is ignored because of "
2520 ui.write(_("%s is ignored because of "
2521 "containing folder %s\n")
2521 "containing folder %s\n")
2522 % (f, ignored))
2522 % (f, ignored))
2523 ignorefile, lineno, line = ignoredata
2523 ignorefile, lineno, line = ignoredata
2524 ui.write(_("(ignore rule in %s, line %d: '%s')\n")
2524 ui.write(_("(ignore rule in %s, line %d: '%s')\n")
2525 % (ignorefile, lineno, line))
2525 % (ignorefile, lineno, line))
2526 else:
2526 else:
2527 ui.write(_("%s is not ignored\n") % f)
2527 ui.write(_("%s is not ignored\n") % f)
2528
2528
2529 @command('debugindex', debugrevlogopts +
2529 @command('debugindex', debugrevlogopts +
2530 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2530 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2531 _('[-f FORMAT] -c|-m|FILE'),
2531 _('[-f FORMAT] -c|-m|FILE'),
2532 optionalrepo=True)
2532 optionalrepo=True)
2533 def debugindex(ui, repo, file_=None, **opts):
2533 def debugindex(ui, repo, file_=None, **opts):
2534 """dump the contents of an index file"""
2534 """dump the contents of an index file"""
2535 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2535 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2536 format = opts.get('format', 0)
2536 format = opts.get('format', 0)
2537 if format not in (0, 1):
2537 if format not in (0, 1):
2538 raise error.Abort(_("unknown format %d") % format)
2538 raise error.Abort(_("unknown format %d") % format)
2539
2539
2540 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2540 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2541 if generaldelta:
2541 if generaldelta:
2542 basehdr = ' delta'
2542 basehdr = ' delta'
2543 else:
2543 else:
2544 basehdr = ' base'
2544 basehdr = ' base'
2545
2545
2546 if ui.debugflag:
2546 if ui.debugflag:
2547 shortfn = hex
2547 shortfn = hex
2548 else:
2548 else:
2549 shortfn = short
2549 shortfn = short
2550
2550
2551 # There might not be anything in r, so have a sane default
2551 # There might not be anything in r, so have a sane default
2552 idlen = 12
2552 idlen = 12
2553 for i in r:
2553 for i in r:
2554 idlen = len(shortfn(r.node(i)))
2554 idlen = len(shortfn(r.node(i)))
2555 break
2555 break
2556
2556
2557 if format == 0:
2557 if format == 0:
2558 ui.write((" rev offset length " + basehdr + " linkrev"
2558 ui.write((" rev offset length " + basehdr + " linkrev"
2559 " %s %s p2\n") % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2559 " %s %s p2\n") % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2560 elif format == 1:
2560 elif format == 1:
2561 ui.write((" rev flag offset length"
2561 ui.write((" rev flag offset length"
2562 " size " + basehdr + " link p1 p2"
2562 " size " + basehdr + " link p1 p2"
2563 " %s\n") % "nodeid".rjust(idlen))
2563 " %s\n") % "nodeid".rjust(idlen))
2564
2564
2565 for i in r:
2565 for i in r:
2566 node = r.node(i)
2566 node = r.node(i)
2567 if generaldelta:
2567 if generaldelta:
2568 base = r.deltaparent(i)
2568 base = r.deltaparent(i)
2569 else:
2569 else:
2570 base = r.chainbase(i)
2570 base = r.chainbase(i)
2571 if format == 0:
2571 if format == 0:
2572 try:
2572 try:
2573 pp = r.parents(node)
2573 pp = r.parents(node)
2574 except Exception:
2574 except Exception:
2575 pp = [nullid, nullid]
2575 pp = [nullid, nullid]
2576 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2576 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2577 i, r.start(i), r.length(i), base, r.linkrev(i),
2577 i, r.start(i), r.length(i), base, r.linkrev(i),
2578 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2578 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2579 elif format == 1:
2579 elif format == 1:
2580 pr = r.parentrevs(i)
2580 pr = r.parentrevs(i)
2581 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2581 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2582 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2582 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2583 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2583 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2584
2584
2585 @command('debugindexdot', debugrevlogopts,
2585 @command('debugindexdot', debugrevlogopts,
2586 _('-c|-m|FILE'), optionalrepo=True)
2586 _('-c|-m|FILE'), optionalrepo=True)
2587 def debugindexdot(ui, repo, file_=None, **opts):
2587 def debugindexdot(ui, repo, file_=None, **opts):
2588 """dump an index DAG as a graphviz dot file"""
2588 """dump an index DAG as a graphviz dot file"""
2589 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
2589 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
2590 ui.write(("digraph G {\n"))
2590 ui.write(("digraph G {\n"))
2591 for i in r:
2591 for i in r:
2592 node = r.node(i)
2592 node = r.node(i)
2593 pp = r.parents(node)
2593 pp = r.parents(node)
2594 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2594 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2595 if pp[1] != nullid:
2595 if pp[1] != nullid:
2596 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2596 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2597 ui.write("}\n")
2597 ui.write("}\n")
2598
2598
2599 @command('debugdeltachain',
2599 @command('debugdeltachain',
2600 debugrevlogopts + formatteropts,
2600 debugrevlogopts + formatteropts,
2601 _('-c|-m|FILE'),
2601 _('-c|-m|FILE'),
2602 optionalrepo=True)
2602 optionalrepo=True)
2603 def debugdeltachain(ui, repo, file_=None, **opts):
2603 def debugdeltachain(ui, repo, file_=None, **opts):
2604 """dump information about delta chains in a revlog
2604 """dump information about delta chains in a revlog
2605
2605
2606 Output can be templatized. Available template keywords are:
2606 Output can be templatized. Available template keywords are:
2607
2607
2608 :``rev``: revision number
2608 :``rev``: revision number
2609 :``chainid``: delta chain identifier (numbered by unique base)
2609 :``chainid``: delta chain identifier (numbered by unique base)
2610 :``chainlen``: delta chain length to this revision
2610 :``chainlen``: delta chain length to this revision
2611 :``prevrev``: previous revision in delta chain
2611 :``prevrev``: previous revision in delta chain
2612 :``deltatype``: role of delta / how it was computed
2612 :``deltatype``: role of delta / how it was computed
2613 :``compsize``: compressed size of revision
2613 :``compsize``: compressed size of revision
2614 :``uncompsize``: uncompressed size of revision
2614 :``uncompsize``: uncompressed size of revision
2615 :``chainsize``: total size of compressed revisions in chain
2615 :``chainsize``: total size of compressed revisions in chain
2616 :``chainratio``: total chain size divided by uncompressed revision size
2616 :``chainratio``: total chain size divided by uncompressed revision size
2617 (new delta chains typically start at ratio 2.00)
2617 (new delta chains typically start at ratio 2.00)
2618 :``lindist``: linear distance from base revision in delta chain to end
2618 :``lindist``: linear distance from base revision in delta chain to end
2619 of this revision
2619 of this revision
2620 :``extradist``: total size of revisions not part of this delta chain from
2620 :``extradist``: total size of revisions not part of this delta chain from
2621 base of delta chain to end of this revision; a measurement
2621 base of delta chain to end of this revision; a measurement
2622 of how much extra data we need to read/seek across to read
2622 of how much extra data we need to read/seek across to read
2623 the delta chain for this revision
2623 the delta chain for this revision
2624 :``extraratio``: extradist divided by chainsize; another representation of
2624 :``extraratio``: extradist divided by chainsize; another representation of
2625 how much unrelated data is needed to load this delta chain
2625 how much unrelated data is needed to load this delta chain
2626 """
2626 """
2627 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
2627 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
2628 index = r.index
2628 index = r.index
2629 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2629 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2630
2630
2631 def revinfo(rev):
2631 def revinfo(rev):
2632 e = index[rev]
2632 e = index[rev]
2633 compsize = e[1]
2633 compsize = e[1]
2634 uncompsize = e[2]
2634 uncompsize = e[2]
2635 chainsize = 0
2635 chainsize = 0
2636
2636
2637 if generaldelta:
2637 if generaldelta:
2638 if e[3] == e[5]:
2638 if e[3] == e[5]:
2639 deltatype = 'p1'
2639 deltatype = 'p1'
2640 elif e[3] == e[6]:
2640 elif e[3] == e[6]:
2641 deltatype = 'p2'
2641 deltatype = 'p2'
2642 elif e[3] == rev - 1:
2642 elif e[3] == rev - 1:
2643 deltatype = 'prev'
2643 deltatype = 'prev'
2644 elif e[3] == rev:
2644 elif e[3] == rev:
2645 deltatype = 'base'
2645 deltatype = 'base'
2646 else:
2646 else:
2647 deltatype = 'other'
2647 deltatype = 'other'
2648 else:
2648 else:
2649 if e[3] == rev:
2649 if e[3] == rev:
2650 deltatype = 'base'
2650 deltatype = 'base'
2651 else:
2651 else:
2652 deltatype = 'prev'
2652 deltatype = 'prev'
2653
2653
2654 chain = r._deltachain(rev)[0]
2654 chain = r._deltachain(rev)[0]
2655 for iterrev in chain:
2655 for iterrev in chain:
2656 e = index[iterrev]
2656 e = index[iterrev]
2657 chainsize += e[1]
2657 chainsize += e[1]
2658
2658
2659 return compsize, uncompsize, deltatype, chain, chainsize
2659 return compsize, uncompsize, deltatype, chain, chainsize
2660
2660
2661 fm = ui.formatter('debugdeltachain', opts)
2661 fm = ui.formatter('debugdeltachain', opts)
2662
2662
2663 fm.plain(' rev chain# chainlen prev delta '
2663 fm.plain(' rev chain# chainlen prev delta '
2664 'size rawsize chainsize ratio lindist extradist '
2664 'size rawsize chainsize ratio lindist extradist '
2665 'extraratio\n')
2665 'extraratio\n')
2666
2666
2667 chainbases = {}
2667 chainbases = {}
2668 for rev in r:
2668 for rev in r:
2669 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
2669 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
2670 chainbase = chain[0]
2670 chainbase = chain[0]
2671 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
2671 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
2672 basestart = r.start(chainbase)
2672 basestart = r.start(chainbase)
2673 revstart = r.start(rev)
2673 revstart = r.start(rev)
2674 lineardist = revstart + comp - basestart
2674 lineardist = revstart + comp - basestart
2675 extradist = lineardist - chainsize
2675 extradist = lineardist - chainsize
2676 try:
2676 try:
2677 prevrev = chain[-2]
2677 prevrev = chain[-2]
2678 except IndexError:
2678 except IndexError:
2679 prevrev = -1
2679 prevrev = -1
2680
2680
2681 chainratio = float(chainsize) / float(uncomp)
2681 chainratio = float(chainsize) / float(uncomp)
2682 extraratio = float(extradist) / float(chainsize)
2682 extraratio = float(extradist) / float(chainsize)
2683
2683
2684 fm.startitem()
2684 fm.startitem()
2685 fm.write('rev chainid chainlen prevrev deltatype compsize '
2685 fm.write('rev chainid chainlen prevrev deltatype compsize '
2686 'uncompsize chainsize chainratio lindist extradist '
2686 'uncompsize chainsize chainratio lindist extradist '
2687 'extraratio',
2687 'extraratio',
2688 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f\n',
2688 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f\n',
2689 rev, chainid, len(chain), prevrev, deltatype, comp,
2689 rev, chainid, len(chain), prevrev, deltatype, comp,
2690 uncomp, chainsize, chainratio, lineardist, extradist,
2690 uncomp, chainsize, chainratio, lineardist, extradist,
2691 extraratio,
2691 extraratio,
2692 rev=rev, chainid=chainid, chainlen=len(chain),
2692 rev=rev, chainid=chainid, chainlen=len(chain),
2693 prevrev=prevrev, deltatype=deltatype, compsize=comp,
2693 prevrev=prevrev, deltatype=deltatype, compsize=comp,
2694 uncompsize=uncomp, chainsize=chainsize,
2694 uncompsize=uncomp, chainsize=chainsize,
2695 chainratio=chainratio, lindist=lineardist,
2695 chainratio=chainratio, lindist=lineardist,
2696 extradist=extradist, extraratio=extraratio)
2696 extradist=extradist, extraratio=extraratio)
2697
2697
2698 fm.end()
2698 fm.end()
2699
2699
2700 @command('debuginstall', [] + formatteropts, '', norepo=True)
2700 @command('debuginstall', [] + formatteropts, '', norepo=True)
2701 def debuginstall(ui, **opts):
2701 def debuginstall(ui, **opts):
2702 '''test Mercurial installation
2702 '''test Mercurial installation
2703
2703
2704 Returns 0 on success.
2704 Returns 0 on success.
2705 '''
2705 '''
2706
2706
2707 def writetemp(contents):
2707 def writetemp(contents):
2708 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2708 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2709 f = os.fdopen(fd, "wb")
2709 f = os.fdopen(fd, "wb")
2710 f.write(contents)
2710 f.write(contents)
2711 f.close()
2711 f.close()
2712 return name
2712 return name
2713
2713
2714 problems = 0
2714 problems = 0
2715
2715
2716 fm = ui.formatter('debuginstall', opts)
2716 fm = ui.formatter('debuginstall', opts)
2717 fm.startitem()
2717 fm.startitem()
2718
2718
2719 # encoding
2719 # encoding
2720 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
2720 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
2721 err = None
2721 err = None
2722 try:
2722 try:
2723 encoding.fromlocal("test")
2723 encoding.fromlocal("test")
2724 except error.Abort as inst:
2724 except error.Abort as inst:
2725 err = inst
2725 err = inst
2726 problems += 1
2726 problems += 1
2727 fm.condwrite(err, 'encodingerror', _(" %s\n"
2727 fm.condwrite(err, 'encodingerror', _(" %s\n"
2728 " (check that your locale is properly set)\n"), err)
2728 " (check that your locale is properly set)\n"), err)
2729
2729
2730 # Python
2730 # Python
2731 fm.write('pythonexe', _("checking Python executable (%s)\n"),
2731 fm.write('pythonexe', _("checking Python executable (%s)\n"),
2732 sys.executable)
2732 sys.executable)
2733 fm.write('pythonver', _("checking Python version (%s)\n"),
2733 fm.write('pythonver', _("checking Python version (%s)\n"),
2734 ("%s.%s.%s" % sys.version_info[:3]))
2734 ("%s.%s.%s" % sys.version_info[:3]))
2735 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
2735 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
2736 os.path.dirname(os.__file__))
2736 os.path.dirname(os.__file__))
2737
2737
2738 # hg version
2738 # hg version
2739 hgver = util.version()
2739 hgver = util.version()
2740 fm.write('hgver', _("checking Mercurial version (%s)\n"),
2740 fm.write('hgver', _("checking Mercurial version (%s)\n"),
2741 hgver.split('+')[0])
2741 hgver.split('+')[0])
2742 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
2742 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
2743 '+'.join(hgver.split('+')[1:]))
2743 '+'.join(hgver.split('+')[1:]))
2744
2744
2745 # compiled modules
2745 # compiled modules
2746 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
2746 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
2747 policy.policy)
2747 policy.policy)
2748 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
2748 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
2749 os.path.dirname(__file__))
2749 os.path.dirname(__file__))
2750
2750
2751 err = None
2751 err = None
2752 try:
2752 try:
2753 from . import (
2753 from . import (
2754 base85,
2754 base85,
2755 bdiff,
2755 bdiff,
2756 mpatch,
2756 mpatch,
2757 osutil,
2757 osutil,
2758 )
2758 )
2759 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2759 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2760 except Exception as inst:
2760 except Exception as inst:
2761 err = inst
2761 err = inst
2762 problems += 1
2762 problems += 1
2763 fm.condwrite(err, 'extensionserror', " %s\n", err)
2763 fm.condwrite(err, 'extensionserror', " %s\n", err)
2764
2764
2765 # templates
2765 # templates
2766 p = templater.templatepaths()
2766 p = templater.templatepaths()
2767 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
2767 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
2768 fm.condwrite(not p, '', _(" no template directories found\n"))
2768 fm.condwrite(not p, '', _(" no template directories found\n"))
2769 if p:
2769 if p:
2770 m = templater.templatepath("map-cmdline.default")
2770 m = templater.templatepath("map-cmdline.default")
2771 if m:
2771 if m:
2772 # template found, check if it is working
2772 # template found, check if it is working
2773 err = None
2773 err = None
2774 try:
2774 try:
2775 templater.templater.frommapfile(m)
2775 templater.templater.frommapfile(m)
2776 except Exception as inst:
2776 except Exception as inst:
2777 err = inst
2777 err = inst
2778 p = None
2778 p = None
2779 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
2779 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
2780 else:
2780 else:
2781 p = None
2781 p = None
2782 fm.condwrite(p, 'defaulttemplate',
2782 fm.condwrite(p, 'defaulttemplate',
2783 _("checking default template (%s)\n"), m)
2783 _("checking default template (%s)\n"), m)
2784 fm.condwrite(not m, 'defaulttemplatenotfound',
2784 fm.condwrite(not m, 'defaulttemplatenotfound',
2785 _(" template '%s' not found\n"), "default")
2785 _(" template '%s' not found\n"), "default")
2786 if not p:
2786 if not p:
2787 problems += 1
2787 problems += 1
2788 fm.condwrite(not p, '',
2788 fm.condwrite(not p, '',
2789 _(" (templates seem to have been installed incorrectly)\n"))
2789 _(" (templates seem to have been installed incorrectly)\n"))
2790
2790
2791 # editor
2791 # editor
2792 editor = ui.geteditor()
2792 editor = ui.geteditor()
2793 editor = util.expandpath(editor)
2793 editor = util.expandpath(editor)
2794 fm.write('editor', _("checking commit editor... (%s)\n"), editor)
2794 fm.write('editor', _("checking commit editor... (%s)\n"), editor)
2795 cmdpath = util.findexe(shlex.split(editor)[0])
2795 cmdpath = util.findexe(shlex.split(editor)[0])
2796 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
2796 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
2797 _(" No commit editor set and can't find %s in PATH\n"
2797 _(" No commit editor set and can't find %s in PATH\n"
2798 " (specify a commit editor in your configuration"
2798 " (specify a commit editor in your configuration"
2799 " file)\n"), not cmdpath and editor == 'vi' and editor)
2799 " file)\n"), not cmdpath and editor == 'vi' and editor)
2800 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
2800 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
2801 _(" Can't find editor '%s' in PATH\n"
2801 _(" Can't find editor '%s' in PATH\n"
2802 " (specify a commit editor in your configuration"
2802 " (specify a commit editor in your configuration"
2803 " file)\n"), not cmdpath and editor)
2803 " file)\n"), not cmdpath and editor)
2804 if not cmdpath and editor != 'vi':
2804 if not cmdpath and editor != 'vi':
2805 problems += 1
2805 problems += 1
2806
2806
2807 # check username
2807 # check username
2808 username = None
2808 username = None
2809 err = None
2809 err = None
2810 try:
2810 try:
2811 username = ui.username()
2811 username = ui.username()
2812 except error.Abort as e:
2812 except error.Abort as e:
2813 err = e
2813 err = e
2814 problems += 1
2814 problems += 1
2815
2815
2816 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
2816 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
2817 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
2817 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
2818 " (specify a username in your configuration file)\n"), err)
2818 " (specify a username in your configuration file)\n"), err)
2819
2819
2820 fm.condwrite(not problems, '',
2820 fm.condwrite(not problems, '',
2821 _("no problems detected\n"))
2821 _("no problems detected\n"))
2822 if not problems:
2822 if not problems:
2823 fm.data(problems=problems)
2823 fm.data(problems=problems)
2824 fm.condwrite(problems, 'problems',
2824 fm.condwrite(problems, 'problems',
2825 _("%s problems detected,"
2825 _("%s problems detected,"
2826 " please check your install!\n"), problems)
2826 " please check your install!\n"), problems)
2827 fm.end()
2827 fm.end()
2828
2828
2829 return problems
2829 return problems
2830
2830
2831 @command('debugknown', [], _('REPO ID...'), norepo=True)
2831 @command('debugknown', [], _('REPO ID...'), norepo=True)
2832 def debugknown(ui, repopath, *ids, **opts):
2832 def debugknown(ui, repopath, *ids, **opts):
2833 """test whether node ids are known to a repo
2833 """test whether node ids are known to a repo
2834
2834
2835 Every ID must be a full-length hex node id string. Returns a list of 0s
2835 Every ID must be a full-length hex node id string. Returns a list of 0s
2836 and 1s indicating unknown/known.
2836 and 1s indicating unknown/known.
2837 """
2837 """
2838 repo = hg.peer(ui, opts, repopath)
2838 repo = hg.peer(ui, opts, repopath)
2839 if not repo.capable('known'):
2839 if not repo.capable('known'):
2840 raise error.Abort("known() not supported by target repository")
2840 raise error.Abort("known() not supported by target repository")
2841 flags = repo.known([bin(s) for s in ids])
2841 flags = repo.known([bin(s) for s in ids])
2842 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2842 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2843
2843
2844 @command('debuglabelcomplete', [], _('LABEL...'))
2844 @command('debuglabelcomplete', [], _('LABEL...'))
2845 def debuglabelcomplete(ui, repo, *args):
2845 def debuglabelcomplete(ui, repo, *args):
2846 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2846 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2847 debugnamecomplete(ui, repo, *args)
2847 debugnamecomplete(ui, repo, *args)
2848
2848
2849 @command('debugmergestate', [], '')
2849 @command('debugmergestate', [], '')
2850 def debugmergestate(ui, repo, *args):
2850 def debugmergestate(ui, repo, *args):
2851 """print merge state
2851 """print merge state
2852
2852
2853 Use --verbose to print out information about whether v1 or v2 merge state
2853 Use --verbose to print out information about whether v1 or v2 merge state
2854 was chosen."""
2854 was chosen."""
2855 def _hashornull(h):
2855 def _hashornull(h):
2856 if h == nullhex:
2856 if h == nullhex:
2857 return 'null'
2857 return 'null'
2858 else:
2858 else:
2859 return h
2859 return h
2860
2860
2861 def printrecords(version):
2861 def printrecords(version):
2862 ui.write(('* version %s records\n') % version)
2862 ui.write(('* version %s records\n') % version)
2863 if version == 1:
2863 if version == 1:
2864 records = v1records
2864 records = v1records
2865 else:
2865 else:
2866 records = v2records
2866 records = v2records
2867
2867
2868 for rtype, record in records:
2868 for rtype, record in records:
2869 # pretty print some record types
2869 # pretty print some record types
2870 if rtype == 'L':
2870 if rtype == 'L':
2871 ui.write(('local: %s\n') % record)
2871 ui.write(('local: %s\n') % record)
2872 elif rtype == 'O':
2872 elif rtype == 'O':
2873 ui.write(('other: %s\n') % record)
2873 ui.write(('other: %s\n') % record)
2874 elif rtype == 'm':
2874 elif rtype == 'm':
2875 driver, mdstate = record.split('\0', 1)
2875 driver, mdstate = record.split('\0', 1)
2876 ui.write(('merge driver: %s (state "%s")\n')
2876 ui.write(('merge driver: %s (state "%s")\n')
2877 % (driver, mdstate))
2877 % (driver, mdstate))
2878 elif rtype in 'FDC':
2878 elif rtype in 'FDC':
2879 r = record.split('\0')
2879 r = record.split('\0')
2880 f, state, hash, lfile, afile, anode, ofile = r[0:7]
2880 f, state, hash, lfile, afile, anode, ofile = r[0:7]
2881 if version == 1:
2881 if version == 1:
2882 onode = 'not stored in v1 format'
2882 onode = 'not stored in v1 format'
2883 flags = r[7]
2883 flags = r[7]
2884 else:
2884 else:
2885 onode, flags = r[7:9]
2885 onode, flags = r[7:9]
2886 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
2886 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
2887 % (f, rtype, state, _hashornull(hash)))
2887 % (f, rtype, state, _hashornull(hash)))
2888 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
2888 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
2889 ui.write((' ancestor path: %s (node %s)\n')
2889 ui.write((' ancestor path: %s (node %s)\n')
2890 % (afile, _hashornull(anode)))
2890 % (afile, _hashornull(anode)))
2891 ui.write((' other path: %s (node %s)\n')
2891 ui.write((' other path: %s (node %s)\n')
2892 % (ofile, _hashornull(onode)))
2892 % (ofile, _hashornull(onode)))
2893 elif rtype == 'f':
2893 elif rtype == 'f':
2894 filename, rawextras = record.split('\0', 1)
2894 filename, rawextras = record.split('\0', 1)
2895 extras = rawextras.split('\0')
2895 extras = rawextras.split('\0')
2896 i = 0
2896 i = 0
2897 extrastrings = []
2897 extrastrings = []
2898 while i < len(extras):
2898 while i < len(extras):
2899 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
2899 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
2900 i += 2
2900 i += 2
2901
2901
2902 ui.write(('file extras: %s (%s)\n')
2902 ui.write(('file extras: %s (%s)\n')
2903 % (filename, ', '.join(extrastrings)))
2903 % (filename, ', '.join(extrastrings)))
2904 elif rtype == 'l':
2904 elif rtype == 'l':
2905 labels = record.split('\0', 2)
2905 labels = record.split('\0', 2)
2906 labels = [l for l in labels if len(l) > 0]
2906 labels = [l for l in labels if len(l) > 0]
2907 ui.write(('labels:\n'))
2907 ui.write(('labels:\n'))
2908 ui.write((' local: %s\n' % labels[0]))
2908 ui.write((' local: %s\n' % labels[0]))
2909 ui.write((' other: %s\n' % labels[1]))
2909 ui.write((' other: %s\n' % labels[1]))
2910 if len(labels) > 2:
2910 if len(labels) > 2:
2911 ui.write((' base: %s\n' % labels[2]))
2911 ui.write((' base: %s\n' % labels[2]))
2912 else:
2912 else:
2913 ui.write(('unrecognized entry: %s\t%s\n')
2913 ui.write(('unrecognized entry: %s\t%s\n')
2914 % (rtype, record.replace('\0', '\t')))
2914 % (rtype, record.replace('\0', '\t')))
2915
2915
2916 # Avoid mergestate.read() since it may raise an exception for unsupported
2916 # Avoid mergestate.read() since it may raise an exception for unsupported
2917 # merge state records. We shouldn't be doing this, but this is OK since this
2917 # merge state records. We shouldn't be doing this, but this is OK since this
2918 # command is pretty low-level.
2918 # command is pretty low-level.
2919 ms = mergemod.mergestate(repo)
2919 ms = mergemod.mergestate(repo)
2920
2920
2921 # sort so that reasonable information is on top
2921 # sort so that reasonable information is on top
2922 v1records = ms._readrecordsv1()
2922 v1records = ms._readrecordsv1()
2923 v2records = ms._readrecordsv2()
2923 v2records = ms._readrecordsv2()
2924 order = 'LOml'
2924 order = 'LOml'
2925 def key(r):
2925 def key(r):
2926 idx = order.find(r[0])
2926 idx = order.find(r[0])
2927 if idx == -1:
2927 if idx == -1:
2928 return (1, r[1])
2928 return (1, r[1])
2929 else:
2929 else:
2930 return (0, idx)
2930 return (0, idx)
2931 v1records.sort(key=key)
2931 v1records.sort(key=key)
2932 v2records.sort(key=key)
2932 v2records.sort(key=key)
2933
2933
2934 if not v1records and not v2records:
2934 if not v1records and not v2records:
2935 ui.write(('no merge state found\n'))
2935 ui.write(('no merge state found\n'))
2936 elif not v2records:
2936 elif not v2records:
2937 ui.note(('no version 2 merge state\n'))
2937 ui.note(('no version 2 merge state\n'))
2938 printrecords(1)
2938 printrecords(1)
2939 elif ms._v1v2match(v1records, v2records):
2939 elif ms._v1v2match(v1records, v2records):
2940 ui.note(('v1 and v2 states match: using v2\n'))
2940 ui.note(('v1 and v2 states match: using v2\n'))
2941 printrecords(2)
2941 printrecords(2)
2942 else:
2942 else:
2943 ui.note(('v1 and v2 states mismatch: using v1\n'))
2943 ui.note(('v1 and v2 states mismatch: using v1\n'))
2944 printrecords(1)
2944 printrecords(1)
2945 if ui.verbose:
2945 if ui.verbose:
2946 printrecords(2)
2946 printrecords(2)
2947
2947
2948 @command('debugnamecomplete', [], _('NAME...'))
2948 @command('debugnamecomplete', [], _('NAME...'))
2949 def debugnamecomplete(ui, repo, *args):
2949 def debugnamecomplete(ui, repo, *args):
2950 '''complete "names" - tags, open branch names, bookmark names'''
2950 '''complete "names" - tags, open branch names, bookmark names'''
2951
2951
2952 names = set()
2952 names = set()
2953 # since we previously only listed open branches, we will handle that
2953 # since we previously only listed open branches, we will handle that
2954 # specially (after this for loop)
2954 # specially (after this for loop)
2955 for name, ns in repo.names.iteritems():
2955 for name, ns in repo.names.iteritems():
2956 if name != 'branches':
2956 if name != 'branches':
2957 names.update(ns.listnames(repo))
2957 names.update(ns.listnames(repo))
2958 names.update(tag for (tag, heads, tip, closed)
2958 names.update(tag for (tag, heads, tip, closed)
2959 in repo.branchmap().iterbranches() if not closed)
2959 in repo.branchmap().iterbranches() if not closed)
2960 completions = set()
2960 completions = set()
2961 if not args:
2961 if not args:
2962 args = ['']
2962 args = ['']
2963 for a in args:
2963 for a in args:
2964 completions.update(n for n in names if n.startswith(a))
2964 completions.update(n for n in names if n.startswith(a))
2965 ui.write('\n'.join(sorted(completions)))
2965 ui.write('\n'.join(sorted(completions)))
2966 ui.write('\n')
2966 ui.write('\n')
2967
2967
2968 @command('debuglocks',
2968 @command('debuglocks',
2969 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2969 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2970 ('W', 'force-wlock', None,
2970 ('W', 'force-wlock', None,
2971 _('free the working state lock (DANGEROUS)'))],
2971 _('free the working state lock (DANGEROUS)'))],
2972 _('[OPTION]...'))
2972 _('[OPTION]...'))
2973 def debuglocks(ui, repo, **opts):
2973 def debuglocks(ui, repo, **opts):
2974 """show or modify state of locks
2974 """show or modify state of locks
2975
2975
2976 By default, this command will show which locks are held. This
2976 By default, this command will show which locks are held. This
2977 includes the user and process holding the lock, the amount of time
2977 includes the user and process holding the lock, the amount of time
2978 the lock has been held, and the machine name where the process is
2978 the lock has been held, and the machine name where the process is
2979 running if it's not local.
2979 running if it's not local.
2980
2980
2981 Locks protect the integrity of Mercurial's data, so should be
2981 Locks protect the integrity of Mercurial's data, so should be
2982 treated with care. System crashes or other interruptions may cause
2982 treated with care. System crashes or other interruptions may cause
2983 locks to not be properly released, though Mercurial will usually
2983 locks to not be properly released, though Mercurial will usually
2984 detect and remove such stale locks automatically.
2984 detect and remove such stale locks automatically.
2985
2985
2986 However, detecting stale locks may not always be possible (for
2986 However, detecting stale locks may not always be possible (for
2987 instance, on a shared filesystem). Removing locks may also be
2987 instance, on a shared filesystem). Removing locks may also be
2988 blocked by filesystem permissions.
2988 blocked by filesystem permissions.
2989
2989
2990 Returns 0 if no locks are held.
2990 Returns 0 if no locks are held.
2991
2991
2992 """
2992 """
2993
2993
2994 if opts.get('force_lock'):
2994 if opts.get('force_lock'):
2995 repo.svfs.unlink('lock')
2995 repo.svfs.unlink('lock')
2996 if opts.get('force_wlock'):
2996 if opts.get('force_wlock'):
2997 repo.vfs.unlink('wlock')
2997 repo.vfs.unlink('wlock')
2998 if opts.get('force_lock') or opts.get('force_lock'):
2998 if opts.get('force_lock') or opts.get('force_lock'):
2999 return 0
2999 return 0
3000
3000
3001 now = time.time()
3001 now = time.time()
3002 held = 0
3002 held = 0
3003
3003
3004 def report(vfs, name, method):
3004 def report(vfs, name, method):
3005 # this causes stale locks to get reaped for more accurate reporting
3005 # this causes stale locks to get reaped for more accurate reporting
3006 try:
3006 try:
3007 l = method(False)
3007 l = method(False)
3008 except error.LockHeld:
3008 except error.LockHeld:
3009 l = None
3009 l = None
3010
3010
3011 if l:
3011 if l:
3012 l.release()
3012 l.release()
3013 else:
3013 else:
3014 try:
3014 try:
3015 stat = vfs.lstat(name)
3015 stat = vfs.lstat(name)
3016 age = now - stat.st_mtime
3016 age = now - stat.st_mtime
3017 user = util.username(stat.st_uid)
3017 user = util.username(stat.st_uid)
3018 locker = vfs.readlock(name)
3018 locker = vfs.readlock(name)
3019 if ":" in locker:
3019 if ":" in locker:
3020 host, pid = locker.split(':')
3020 host, pid = locker.split(':')
3021 if host == socket.gethostname():
3021 if host == socket.gethostname():
3022 locker = 'user %s, process %s' % (user, pid)
3022 locker = 'user %s, process %s' % (user, pid)
3023 else:
3023 else:
3024 locker = 'user %s, process %s, host %s' \
3024 locker = 'user %s, process %s, host %s' \
3025 % (user, pid, host)
3025 % (user, pid, host)
3026 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
3026 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
3027 return 1
3027 return 1
3028 except OSError as e:
3028 except OSError as e:
3029 if e.errno != errno.ENOENT:
3029 if e.errno != errno.ENOENT:
3030 raise
3030 raise
3031
3031
3032 ui.write(("%-6s free\n") % (name + ":"))
3032 ui.write(("%-6s free\n") % (name + ":"))
3033 return 0
3033 return 0
3034
3034
3035 held += report(repo.svfs, "lock", repo.lock)
3035 held += report(repo.svfs, "lock", repo.lock)
3036 held += report(repo.vfs, "wlock", repo.wlock)
3036 held += report(repo.vfs, "wlock", repo.wlock)
3037
3037
3038 return held
3038 return held
3039
3039
3040 @command('debugobsolete',
3040 @command('debugobsolete',
3041 [('', 'flags', 0, _('markers flag')),
3041 [('', 'flags', 0, _('markers flag')),
3042 ('', 'record-parents', False,
3042 ('', 'record-parents', False,
3043 _('record parent information for the precursor')),
3043 _('record parent information for the precursor')),
3044 ('r', 'rev', [], _('display markers relevant to REV')),
3044 ('r', 'rev', [], _('display markers relevant to REV')),
3045 ('', 'index', False, _('display index of the marker')),
3045 ('', 'index', False, _('display index of the marker')),
3046 ('', 'delete', [], _('delete markers specified by indices')),
3046 ('', 'delete', [], _('delete markers specified by indices')),
3047 ] + commitopts2,
3047 ] + commitopts2 + formatteropts,
3048 _('[OBSOLETED [REPLACEMENT ...]]'))
3048 _('[OBSOLETED [REPLACEMENT ...]]'))
3049 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
3049 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
3050 """create arbitrary obsolete marker
3050 """create arbitrary obsolete marker
3051
3051
3052 With no arguments, displays the list of obsolescence markers."""
3052 With no arguments, displays the list of obsolescence markers."""
3053
3053
3054 def parsenodeid(s):
3054 def parsenodeid(s):
3055 try:
3055 try:
3056 # We do not use revsingle/revrange functions here to accept
3056 # We do not use revsingle/revrange functions here to accept
3057 # arbitrary node identifiers, possibly not present in the
3057 # arbitrary node identifiers, possibly not present in the
3058 # local repository.
3058 # local repository.
3059 n = bin(s)
3059 n = bin(s)
3060 if len(n) != len(nullid):
3060 if len(n) != len(nullid):
3061 raise TypeError()
3061 raise TypeError()
3062 return n
3062 return n
3063 except TypeError:
3063 except TypeError:
3064 raise error.Abort('changeset references must be full hexadecimal '
3064 raise error.Abort('changeset references must be full hexadecimal '
3065 'node identifiers')
3065 'node identifiers')
3066
3066
3067 if opts.get('delete'):
3067 if opts.get('delete'):
3068 indices = []
3068 indices = []
3069 for v in opts.get('delete'):
3069 for v in opts.get('delete'):
3070 try:
3070 try:
3071 indices.append(int(v))
3071 indices.append(int(v))
3072 except ValueError:
3072 except ValueError:
3073 raise error.Abort(_('invalid index value: %r') % v,
3073 raise error.Abort(_('invalid index value: %r') % v,
3074 hint=_('use integers for indices'))
3074 hint=_('use integers for indices'))
3075
3075
3076 if repo.currenttransaction():
3076 if repo.currenttransaction():
3077 raise error.Abort(_('cannot delete obsmarkers in the middle '
3077 raise error.Abort(_('cannot delete obsmarkers in the middle '
3078 'of transaction.'))
3078 'of transaction.'))
3079
3079
3080 with repo.lock():
3080 with repo.lock():
3081 n = repair.deleteobsmarkers(repo.obsstore, indices)
3081 n = repair.deleteobsmarkers(repo.obsstore, indices)
3082 ui.write(_('deleted %i obsolescense markers\n') % n)
3082 ui.write(_('deleted %i obsolescense markers\n') % n)
3083
3083
3084 return
3084 return
3085
3085
3086 if precursor is not None:
3086 if precursor is not None:
3087 if opts['rev']:
3087 if opts['rev']:
3088 raise error.Abort('cannot select revision when creating marker')
3088 raise error.Abort('cannot select revision when creating marker')
3089 metadata = {}
3089 metadata = {}
3090 metadata['user'] = opts['user'] or ui.username()
3090 metadata['user'] = opts['user'] or ui.username()
3091 succs = tuple(parsenodeid(succ) for succ in successors)
3091 succs = tuple(parsenodeid(succ) for succ in successors)
3092 l = repo.lock()
3092 l = repo.lock()
3093 try:
3093 try:
3094 tr = repo.transaction('debugobsolete')
3094 tr = repo.transaction('debugobsolete')
3095 try:
3095 try:
3096 date = opts.get('date')
3096 date = opts.get('date')
3097 if date:
3097 if date:
3098 date = util.parsedate(date)
3098 date = util.parsedate(date)
3099 else:
3099 else:
3100 date = None
3100 date = None
3101 prec = parsenodeid(precursor)
3101 prec = parsenodeid(precursor)
3102 parents = None
3102 parents = None
3103 if opts['record_parents']:
3103 if opts['record_parents']:
3104 if prec not in repo.unfiltered():
3104 if prec not in repo.unfiltered():
3105 raise error.Abort('cannot used --record-parents on '
3105 raise error.Abort('cannot used --record-parents on '
3106 'unknown changesets')
3106 'unknown changesets')
3107 parents = repo.unfiltered()[prec].parents()
3107 parents = repo.unfiltered()[prec].parents()
3108 parents = tuple(p.node() for p in parents)
3108 parents = tuple(p.node() for p in parents)
3109 repo.obsstore.create(tr, prec, succs, opts['flags'],
3109 repo.obsstore.create(tr, prec, succs, opts['flags'],
3110 parents=parents, date=date,
3110 parents=parents, date=date,
3111 metadata=metadata)
3111 metadata=metadata)
3112 tr.close()
3112 tr.close()
3113 except ValueError as exc:
3113 except ValueError as exc:
3114 raise error.Abort(_('bad obsmarker input: %s') % exc)
3114 raise error.Abort(_('bad obsmarker input: %s') % exc)
3115 finally:
3115 finally:
3116 tr.release()
3116 tr.release()
3117 finally:
3117 finally:
3118 l.release()
3118 l.release()
3119 else:
3119 else:
3120 if opts['rev']:
3120 if opts['rev']:
3121 revs = scmutil.revrange(repo, opts['rev'])
3121 revs = scmutil.revrange(repo, opts['rev'])
3122 nodes = [repo[r].node() for r in revs]
3122 nodes = [repo[r].node() for r in revs]
3123 markers = list(obsolete.getmarkers(repo, nodes=nodes))
3123 markers = list(obsolete.getmarkers(repo, nodes=nodes))
3124 markers.sort(key=lambda x: x._data)
3124 markers.sort(key=lambda x: x._data)
3125 else:
3125 else:
3126 markers = obsolete.getmarkers(repo)
3126 markers = obsolete.getmarkers(repo)
3127
3127
3128 markerstoiter = markers
3128 markerstoiter = markers
3129 isrelevant = lambda m: True
3129 isrelevant = lambda m: True
3130 if opts.get('rev') and opts.get('index'):
3130 if opts.get('rev') and opts.get('index'):
3131 markerstoiter = obsolete.getmarkers(repo)
3131 markerstoiter = obsolete.getmarkers(repo)
3132 markerset = set(markers)
3132 markerset = set(markers)
3133 isrelevant = lambda m: m in markerset
3133 isrelevant = lambda m: m in markerset
3134
3134
3135 fm = ui.formatter('debugobsolete', opts)
3135 for i, m in enumerate(markerstoiter):
3136 for i, m in enumerate(markerstoiter):
3136 if not isrelevant(m):
3137 if not isrelevant(m):
3137 # marker can be irrelevant when we're iterating over a set
3138 # marker can be irrelevant when we're iterating over a set
3138 # of markers (markerstoiter) which is bigger than the set
3139 # of markers (markerstoiter) which is bigger than the set
3139 # of markers we want to display (markers)
3140 # of markers we want to display (markers)
3140 # this can happen if both --index and --rev options are
3141 # this can happen if both --index and --rev options are
3141 # provided and thus we need to iterate over all of the markers
3142 # provided and thus we need to iterate over all of the markers
3142 # to get the correct indices, but only display the ones that
3143 # to get the correct indices, but only display the ones that
3143 # are relevant to --rev value
3144 # are relevant to --rev value
3144 continue
3145 continue
3146 fm.startitem()
3145 ind = i if opts.get('index') else None
3147 ind = i if opts.get('index') else None
3146 cmdutil.showmarker(ui, m, index=ind)
3148 cmdutil.showmarker(fm, m, index=ind)
3149 fm.end()
3147
3150
3148 @command('debugpathcomplete',
3151 @command('debugpathcomplete',
3149 [('f', 'full', None, _('complete an entire path')),
3152 [('f', 'full', None, _('complete an entire path')),
3150 ('n', 'normal', None, _('show only normal files')),
3153 ('n', 'normal', None, _('show only normal files')),
3151 ('a', 'added', None, _('show only added files')),
3154 ('a', 'added', None, _('show only added files')),
3152 ('r', 'removed', None, _('show only removed files'))],
3155 ('r', 'removed', None, _('show only removed files'))],
3153 _('FILESPEC...'))
3156 _('FILESPEC...'))
3154 def debugpathcomplete(ui, repo, *specs, **opts):
3157 def debugpathcomplete(ui, repo, *specs, **opts):
3155 '''complete part or all of a tracked path
3158 '''complete part or all of a tracked path
3156
3159
3157 This command supports shells that offer path name completion. It
3160 This command supports shells that offer path name completion. It
3158 currently completes only files already known to the dirstate.
3161 currently completes only files already known to the dirstate.
3159
3162
3160 Completion extends only to the next path segment unless
3163 Completion extends only to the next path segment unless
3161 --full is specified, in which case entire paths are used.'''
3164 --full is specified, in which case entire paths are used.'''
3162
3165
3163 def complete(path, acceptable):
3166 def complete(path, acceptable):
3164 dirstate = repo.dirstate
3167 dirstate = repo.dirstate
3165 spec = os.path.normpath(os.path.join(os.getcwd(), path))
3168 spec = os.path.normpath(os.path.join(os.getcwd(), path))
3166 rootdir = repo.root + os.sep
3169 rootdir = repo.root + os.sep
3167 if spec != repo.root and not spec.startswith(rootdir):
3170 if spec != repo.root and not spec.startswith(rootdir):
3168 return [], []
3171 return [], []
3169 if os.path.isdir(spec):
3172 if os.path.isdir(spec):
3170 spec += '/'
3173 spec += '/'
3171 spec = spec[len(rootdir):]
3174 spec = spec[len(rootdir):]
3172 fixpaths = os.sep != '/'
3175 fixpaths = os.sep != '/'
3173 if fixpaths:
3176 if fixpaths:
3174 spec = spec.replace(os.sep, '/')
3177 spec = spec.replace(os.sep, '/')
3175 speclen = len(spec)
3178 speclen = len(spec)
3176 fullpaths = opts['full']
3179 fullpaths = opts['full']
3177 files, dirs = set(), set()
3180 files, dirs = set(), set()
3178 adddir, addfile = dirs.add, files.add
3181 adddir, addfile = dirs.add, files.add
3179 for f, st in dirstate.iteritems():
3182 for f, st in dirstate.iteritems():
3180 if f.startswith(spec) and st[0] in acceptable:
3183 if f.startswith(spec) and st[0] in acceptable:
3181 if fixpaths:
3184 if fixpaths:
3182 f = f.replace('/', os.sep)
3185 f = f.replace('/', os.sep)
3183 if fullpaths:
3186 if fullpaths:
3184 addfile(f)
3187 addfile(f)
3185 continue
3188 continue
3186 s = f.find(os.sep, speclen)
3189 s = f.find(os.sep, speclen)
3187 if s >= 0:
3190 if s >= 0:
3188 adddir(f[:s])
3191 adddir(f[:s])
3189 else:
3192 else:
3190 addfile(f)
3193 addfile(f)
3191 return files, dirs
3194 return files, dirs
3192
3195
3193 acceptable = ''
3196 acceptable = ''
3194 if opts['normal']:
3197 if opts['normal']:
3195 acceptable += 'nm'
3198 acceptable += 'nm'
3196 if opts['added']:
3199 if opts['added']:
3197 acceptable += 'a'
3200 acceptable += 'a'
3198 if opts['removed']:
3201 if opts['removed']:
3199 acceptable += 'r'
3202 acceptable += 'r'
3200 cwd = repo.getcwd()
3203 cwd = repo.getcwd()
3201 if not specs:
3204 if not specs:
3202 specs = ['.']
3205 specs = ['.']
3203
3206
3204 files, dirs = set(), set()
3207 files, dirs = set(), set()
3205 for spec in specs:
3208 for spec in specs:
3206 f, d = complete(spec, acceptable or 'nmar')
3209 f, d = complete(spec, acceptable or 'nmar')
3207 files.update(f)
3210 files.update(f)
3208 dirs.update(d)
3211 dirs.update(d)
3209 files.update(dirs)
3212 files.update(dirs)
3210 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
3213 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
3211 ui.write('\n')
3214 ui.write('\n')
3212
3215
3213 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
3216 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
3214 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
3217 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
3215 '''access the pushkey key/value protocol
3218 '''access the pushkey key/value protocol
3216
3219
3217 With two args, list the keys in the given namespace.
3220 With two args, list the keys in the given namespace.
3218
3221
3219 With five args, set a key to new if it currently is set to old.
3222 With five args, set a key to new if it currently is set to old.
3220 Reports success or failure.
3223 Reports success or failure.
3221 '''
3224 '''
3222
3225
3223 target = hg.peer(ui, {}, repopath)
3226 target = hg.peer(ui, {}, repopath)
3224 if keyinfo:
3227 if keyinfo:
3225 key, old, new = keyinfo
3228 key, old, new = keyinfo
3226 r = target.pushkey(namespace, key, old, new)
3229 r = target.pushkey(namespace, key, old, new)
3227 ui.status(str(r) + '\n')
3230 ui.status(str(r) + '\n')
3228 return not r
3231 return not r
3229 else:
3232 else:
3230 for k, v in sorted(target.listkeys(namespace).iteritems()):
3233 for k, v in sorted(target.listkeys(namespace).iteritems()):
3231 ui.write("%s\t%s\n" % (k.encode('string-escape'),
3234 ui.write("%s\t%s\n" % (k.encode('string-escape'),
3232 v.encode('string-escape')))
3235 v.encode('string-escape')))
3233
3236
3234 @command('debugpvec', [], _('A B'))
3237 @command('debugpvec', [], _('A B'))
3235 def debugpvec(ui, repo, a, b=None):
3238 def debugpvec(ui, repo, a, b=None):
3236 ca = scmutil.revsingle(repo, a)
3239 ca = scmutil.revsingle(repo, a)
3237 cb = scmutil.revsingle(repo, b)
3240 cb = scmutil.revsingle(repo, b)
3238 pa = pvec.ctxpvec(ca)
3241 pa = pvec.ctxpvec(ca)
3239 pb = pvec.ctxpvec(cb)
3242 pb = pvec.ctxpvec(cb)
3240 if pa == pb:
3243 if pa == pb:
3241 rel = "="
3244 rel = "="
3242 elif pa > pb:
3245 elif pa > pb:
3243 rel = ">"
3246 rel = ">"
3244 elif pa < pb:
3247 elif pa < pb:
3245 rel = "<"
3248 rel = "<"
3246 elif pa | pb:
3249 elif pa | pb:
3247 rel = "|"
3250 rel = "|"
3248 ui.write(_("a: %s\n") % pa)
3251 ui.write(_("a: %s\n") % pa)
3249 ui.write(_("b: %s\n") % pb)
3252 ui.write(_("b: %s\n") % pb)
3250 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
3253 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
3251 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
3254 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
3252 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
3255 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
3253 pa.distance(pb), rel))
3256 pa.distance(pb), rel))
3254
3257
3255 @command('debugrebuilddirstate|debugrebuildstate',
3258 @command('debugrebuilddirstate|debugrebuildstate',
3256 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
3259 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
3257 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
3260 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
3258 'the working copy parent')),
3261 'the working copy parent')),
3259 ],
3262 ],
3260 _('[-r REV]'))
3263 _('[-r REV]'))
3261 def debugrebuilddirstate(ui, repo, rev, **opts):
3264 def debugrebuilddirstate(ui, repo, rev, **opts):
3262 """rebuild the dirstate as it would look like for the given revision
3265 """rebuild the dirstate as it would look like for the given revision
3263
3266
3264 If no revision is specified the first current parent will be used.
3267 If no revision is specified the first current parent will be used.
3265
3268
3266 The dirstate will be set to the files of the given revision.
3269 The dirstate will be set to the files of the given revision.
3267 The actual working directory content or existing dirstate
3270 The actual working directory content or existing dirstate
3268 information such as adds or removes is not considered.
3271 information such as adds or removes is not considered.
3269
3272
3270 ``minimal`` will only rebuild the dirstate status for files that claim to be
3273 ``minimal`` will only rebuild the dirstate status for files that claim to be
3271 tracked but are not in the parent manifest, or that exist in the parent
3274 tracked but are not in the parent manifest, or that exist in the parent
3272 manifest but are not in the dirstate. It will not change adds, removes, or
3275 manifest but are not in the dirstate. It will not change adds, removes, or
3273 modified files that are in the working copy parent.
3276 modified files that are in the working copy parent.
3274
3277
3275 One use of this command is to make the next :hg:`status` invocation
3278 One use of this command is to make the next :hg:`status` invocation
3276 check the actual file content.
3279 check the actual file content.
3277 """
3280 """
3278 ctx = scmutil.revsingle(repo, rev)
3281 ctx = scmutil.revsingle(repo, rev)
3279 with repo.wlock():
3282 with repo.wlock():
3280 dirstate = repo.dirstate
3283 dirstate = repo.dirstate
3281 changedfiles = None
3284 changedfiles = None
3282 # See command doc for what minimal does.
3285 # See command doc for what minimal does.
3283 if opts.get('minimal'):
3286 if opts.get('minimal'):
3284 manifestfiles = set(ctx.manifest().keys())
3287 manifestfiles = set(ctx.manifest().keys())
3285 dirstatefiles = set(dirstate)
3288 dirstatefiles = set(dirstate)
3286 manifestonly = manifestfiles - dirstatefiles
3289 manifestonly = manifestfiles - dirstatefiles
3287 dsonly = dirstatefiles - manifestfiles
3290 dsonly = dirstatefiles - manifestfiles
3288 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
3291 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
3289 changedfiles = manifestonly | dsnotadded
3292 changedfiles = manifestonly | dsnotadded
3290
3293
3291 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
3294 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
3292
3295
3293 @command('debugrebuildfncache', [], '')
3296 @command('debugrebuildfncache', [], '')
3294 def debugrebuildfncache(ui, repo):
3297 def debugrebuildfncache(ui, repo):
3295 """rebuild the fncache file"""
3298 """rebuild the fncache file"""
3296 repair.rebuildfncache(ui, repo)
3299 repair.rebuildfncache(ui, repo)
3297
3300
3298 @command('debugrename',
3301 @command('debugrename',
3299 [('r', 'rev', '', _('revision to debug'), _('REV'))],
3302 [('r', 'rev', '', _('revision to debug'), _('REV'))],
3300 _('[-r REV] FILE'))
3303 _('[-r REV] FILE'))
3301 def debugrename(ui, repo, file1, *pats, **opts):
3304 def debugrename(ui, repo, file1, *pats, **opts):
3302 """dump rename information"""
3305 """dump rename information"""
3303
3306
3304 ctx = scmutil.revsingle(repo, opts.get('rev'))
3307 ctx = scmutil.revsingle(repo, opts.get('rev'))
3305 m = scmutil.match(ctx, (file1,) + pats, opts)
3308 m = scmutil.match(ctx, (file1,) + pats, opts)
3306 for abs in ctx.walk(m):
3309 for abs in ctx.walk(m):
3307 fctx = ctx[abs]
3310 fctx = ctx[abs]
3308 o = fctx.filelog().renamed(fctx.filenode())
3311 o = fctx.filelog().renamed(fctx.filenode())
3309 rel = m.rel(abs)
3312 rel = m.rel(abs)
3310 if o:
3313 if o:
3311 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
3314 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
3312 else:
3315 else:
3313 ui.write(_("%s not renamed\n") % rel)
3316 ui.write(_("%s not renamed\n") % rel)
3314
3317
3315 @command('debugrevlog', debugrevlogopts +
3318 @command('debugrevlog', debugrevlogopts +
3316 [('d', 'dump', False, _('dump index data'))],
3319 [('d', 'dump', False, _('dump index data'))],
3317 _('-c|-m|FILE'),
3320 _('-c|-m|FILE'),
3318 optionalrepo=True)
3321 optionalrepo=True)
3319 def debugrevlog(ui, repo, file_=None, **opts):
3322 def debugrevlog(ui, repo, file_=None, **opts):
3320 """show data and statistics about a revlog"""
3323 """show data and statistics about a revlog"""
3321 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
3324 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
3322
3325
3323 if opts.get("dump"):
3326 if opts.get("dump"):
3324 numrevs = len(r)
3327 numrevs = len(r)
3325 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
3328 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
3326 " rawsize totalsize compression heads chainlen\n"))
3329 " rawsize totalsize compression heads chainlen\n"))
3327 ts = 0
3330 ts = 0
3328 heads = set()
3331 heads = set()
3329
3332
3330 for rev in xrange(numrevs):
3333 for rev in xrange(numrevs):
3331 dbase = r.deltaparent(rev)
3334 dbase = r.deltaparent(rev)
3332 if dbase == -1:
3335 if dbase == -1:
3333 dbase = rev
3336 dbase = rev
3334 cbase = r.chainbase(rev)
3337 cbase = r.chainbase(rev)
3335 clen = r.chainlen(rev)
3338 clen = r.chainlen(rev)
3336 p1, p2 = r.parentrevs(rev)
3339 p1, p2 = r.parentrevs(rev)
3337 rs = r.rawsize(rev)
3340 rs = r.rawsize(rev)
3338 ts = ts + rs
3341 ts = ts + rs
3339 heads -= set(r.parentrevs(rev))
3342 heads -= set(r.parentrevs(rev))
3340 heads.add(rev)
3343 heads.add(rev)
3341 try:
3344 try:
3342 compression = ts / r.end(rev)
3345 compression = ts / r.end(rev)
3343 except ZeroDivisionError:
3346 except ZeroDivisionError:
3344 compression = 0
3347 compression = 0
3345 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
3348 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
3346 "%11d %5d %8d\n" %
3349 "%11d %5d %8d\n" %
3347 (rev, p1, p2, r.start(rev), r.end(rev),
3350 (rev, p1, p2, r.start(rev), r.end(rev),
3348 r.start(dbase), r.start(cbase),
3351 r.start(dbase), r.start(cbase),
3349 r.start(p1), r.start(p2),
3352 r.start(p1), r.start(p2),
3350 rs, ts, compression, len(heads), clen))
3353 rs, ts, compression, len(heads), clen))
3351 return 0
3354 return 0
3352
3355
3353 v = r.version
3356 v = r.version
3354 format = v & 0xFFFF
3357 format = v & 0xFFFF
3355 flags = []
3358 flags = []
3356 gdelta = False
3359 gdelta = False
3357 if v & revlog.REVLOGNGINLINEDATA:
3360 if v & revlog.REVLOGNGINLINEDATA:
3358 flags.append('inline')
3361 flags.append('inline')
3359 if v & revlog.REVLOGGENERALDELTA:
3362 if v & revlog.REVLOGGENERALDELTA:
3360 gdelta = True
3363 gdelta = True
3361 flags.append('generaldelta')
3364 flags.append('generaldelta')
3362 if not flags:
3365 if not flags:
3363 flags = ['(none)']
3366 flags = ['(none)']
3364
3367
3365 nummerges = 0
3368 nummerges = 0
3366 numfull = 0
3369 numfull = 0
3367 numprev = 0
3370 numprev = 0
3368 nump1 = 0
3371 nump1 = 0
3369 nump2 = 0
3372 nump2 = 0
3370 numother = 0
3373 numother = 0
3371 nump1prev = 0
3374 nump1prev = 0
3372 nump2prev = 0
3375 nump2prev = 0
3373 chainlengths = []
3376 chainlengths = []
3374
3377
3375 datasize = [None, 0, 0L]
3378 datasize = [None, 0, 0L]
3376 fullsize = [None, 0, 0L]
3379 fullsize = [None, 0, 0L]
3377 deltasize = [None, 0, 0L]
3380 deltasize = [None, 0, 0L]
3378
3381
3379 def addsize(size, l):
3382 def addsize(size, l):
3380 if l[0] is None or size < l[0]:
3383 if l[0] is None or size < l[0]:
3381 l[0] = size
3384 l[0] = size
3382 if size > l[1]:
3385 if size > l[1]:
3383 l[1] = size
3386 l[1] = size
3384 l[2] += size
3387 l[2] += size
3385
3388
3386 numrevs = len(r)
3389 numrevs = len(r)
3387 for rev in xrange(numrevs):
3390 for rev in xrange(numrevs):
3388 p1, p2 = r.parentrevs(rev)
3391 p1, p2 = r.parentrevs(rev)
3389 delta = r.deltaparent(rev)
3392 delta = r.deltaparent(rev)
3390 if format > 0:
3393 if format > 0:
3391 addsize(r.rawsize(rev), datasize)
3394 addsize(r.rawsize(rev), datasize)
3392 if p2 != nullrev:
3395 if p2 != nullrev:
3393 nummerges += 1
3396 nummerges += 1
3394 size = r.length(rev)
3397 size = r.length(rev)
3395 if delta == nullrev:
3398 if delta == nullrev:
3396 chainlengths.append(0)
3399 chainlengths.append(0)
3397 numfull += 1
3400 numfull += 1
3398 addsize(size, fullsize)
3401 addsize(size, fullsize)
3399 else:
3402 else:
3400 chainlengths.append(chainlengths[delta] + 1)
3403 chainlengths.append(chainlengths[delta] + 1)
3401 addsize(size, deltasize)
3404 addsize(size, deltasize)
3402 if delta == rev - 1:
3405 if delta == rev - 1:
3403 numprev += 1
3406 numprev += 1
3404 if delta == p1:
3407 if delta == p1:
3405 nump1prev += 1
3408 nump1prev += 1
3406 elif delta == p2:
3409 elif delta == p2:
3407 nump2prev += 1
3410 nump2prev += 1
3408 elif delta == p1:
3411 elif delta == p1:
3409 nump1 += 1
3412 nump1 += 1
3410 elif delta == p2:
3413 elif delta == p2:
3411 nump2 += 1
3414 nump2 += 1
3412 elif delta != nullrev:
3415 elif delta != nullrev:
3413 numother += 1
3416 numother += 1
3414
3417
3415 # Adjust size min value for empty cases
3418 # Adjust size min value for empty cases
3416 for size in (datasize, fullsize, deltasize):
3419 for size in (datasize, fullsize, deltasize):
3417 if size[0] is None:
3420 if size[0] is None:
3418 size[0] = 0
3421 size[0] = 0
3419
3422
3420 numdeltas = numrevs - numfull
3423 numdeltas = numrevs - numfull
3421 numoprev = numprev - nump1prev - nump2prev
3424 numoprev = numprev - nump1prev - nump2prev
3422 totalrawsize = datasize[2]
3425 totalrawsize = datasize[2]
3423 datasize[2] /= numrevs
3426 datasize[2] /= numrevs
3424 fulltotal = fullsize[2]
3427 fulltotal = fullsize[2]
3425 fullsize[2] /= numfull
3428 fullsize[2] /= numfull
3426 deltatotal = deltasize[2]
3429 deltatotal = deltasize[2]
3427 if numrevs - numfull > 0:
3430 if numrevs - numfull > 0:
3428 deltasize[2] /= numrevs - numfull
3431 deltasize[2] /= numrevs - numfull
3429 totalsize = fulltotal + deltatotal
3432 totalsize = fulltotal + deltatotal
3430 avgchainlen = sum(chainlengths) / numrevs
3433 avgchainlen = sum(chainlengths) / numrevs
3431 maxchainlen = max(chainlengths)
3434 maxchainlen = max(chainlengths)
3432 compratio = 1
3435 compratio = 1
3433 if totalsize:
3436 if totalsize:
3434 compratio = totalrawsize / totalsize
3437 compratio = totalrawsize / totalsize
3435
3438
3436 basedfmtstr = '%%%dd\n'
3439 basedfmtstr = '%%%dd\n'
3437 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
3440 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
3438
3441
3439 def dfmtstr(max):
3442 def dfmtstr(max):
3440 return basedfmtstr % len(str(max))
3443 return basedfmtstr % len(str(max))
3441 def pcfmtstr(max, padding=0):
3444 def pcfmtstr(max, padding=0):
3442 return basepcfmtstr % (len(str(max)), ' ' * padding)
3445 return basepcfmtstr % (len(str(max)), ' ' * padding)
3443
3446
3444 def pcfmt(value, total):
3447 def pcfmt(value, total):
3445 if total:
3448 if total:
3446 return (value, 100 * float(value) / total)
3449 return (value, 100 * float(value) / total)
3447 else:
3450 else:
3448 return value, 100.0
3451 return value, 100.0
3449
3452
3450 ui.write(('format : %d\n') % format)
3453 ui.write(('format : %d\n') % format)
3451 ui.write(('flags : %s\n') % ', '.join(flags))
3454 ui.write(('flags : %s\n') % ', '.join(flags))
3452
3455
3453 ui.write('\n')
3456 ui.write('\n')
3454 fmt = pcfmtstr(totalsize)
3457 fmt = pcfmtstr(totalsize)
3455 fmt2 = dfmtstr(totalsize)
3458 fmt2 = dfmtstr(totalsize)
3456 ui.write(('revisions : ') + fmt2 % numrevs)
3459 ui.write(('revisions : ') + fmt2 % numrevs)
3457 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
3460 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
3458 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
3461 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
3459 ui.write(('revisions : ') + fmt2 % numrevs)
3462 ui.write(('revisions : ') + fmt2 % numrevs)
3460 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
3463 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
3461 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
3464 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
3462 ui.write(('revision size : ') + fmt2 % totalsize)
3465 ui.write(('revision size : ') + fmt2 % totalsize)
3463 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
3466 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
3464 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
3467 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
3465
3468
3466 ui.write('\n')
3469 ui.write('\n')
3467 fmt = dfmtstr(max(avgchainlen, compratio))
3470 fmt = dfmtstr(max(avgchainlen, compratio))
3468 ui.write(('avg chain length : ') + fmt % avgchainlen)
3471 ui.write(('avg chain length : ') + fmt % avgchainlen)
3469 ui.write(('max chain length : ') + fmt % maxchainlen)
3472 ui.write(('max chain length : ') + fmt % maxchainlen)
3470 ui.write(('compression ratio : ') + fmt % compratio)
3473 ui.write(('compression ratio : ') + fmt % compratio)
3471
3474
3472 if format > 0:
3475 if format > 0:
3473 ui.write('\n')
3476 ui.write('\n')
3474 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
3477 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
3475 % tuple(datasize))
3478 % tuple(datasize))
3476 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
3479 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
3477 % tuple(fullsize))
3480 % tuple(fullsize))
3478 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
3481 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
3479 % tuple(deltasize))
3482 % tuple(deltasize))
3480
3483
3481 if numdeltas > 0:
3484 if numdeltas > 0:
3482 ui.write('\n')
3485 ui.write('\n')
3483 fmt = pcfmtstr(numdeltas)
3486 fmt = pcfmtstr(numdeltas)
3484 fmt2 = pcfmtstr(numdeltas, 4)
3487 fmt2 = pcfmtstr(numdeltas, 4)
3485 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
3488 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
3486 if numprev > 0:
3489 if numprev > 0:
3487 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
3490 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
3488 numprev))
3491 numprev))
3489 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
3492 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
3490 numprev))
3493 numprev))
3491 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
3494 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
3492 numprev))
3495 numprev))
3493 if gdelta:
3496 if gdelta:
3494 ui.write(('deltas against p1 : ')
3497 ui.write(('deltas against p1 : ')
3495 + fmt % pcfmt(nump1, numdeltas))
3498 + fmt % pcfmt(nump1, numdeltas))
3496 ui.write(('deltas against p2 : ')
3499 ui.write(('deltas against p2 : ')
3497 + fmt % pcfmt(nump2, numdeltas))
3500 + fmt % pcfmt(nump2, numdeltas))
3498 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
3501 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
3499 numdeltas))
3502 numdeltas))
3500
3503
3501 @command('debugrevspec',
3504 @command('debugrevspec',
3502 [('', 'optimize', None, _('print parsed tree after optimizing'))],
3505 [('', 'optimize', None, _('print parsed tree after optimizing'))],
3503 ('REVSPEC'))
3506 ('REVSPEC'))
3504 def debugrevspec(ui, repo, expr, **opts):
3507 def debugrevspec(ui, repo, expr, **opts):
3505 """parse and apply a revision specification
3508 """parse and apply a revision specification
3506
3509
3507 Use --verbose to print the parsed tree before and after aliases
3510 Use --verbose to print the parsed tree before and after aliases
3508 expansion.
3511 expansion.
3509 """
3512 """
3510 if ui.verbose:
3513 if ui.verbose:
3511 tree = revset.parse(expr, lookup=repo.__contains__)
3514 tree = revset.parse(expr, lookup=repo.__contains__)
3512 ui.note(revset.prettyformat(tree), "\n")
3515 ui.note(revset.prettyformat(tree), "\n")
3513 newtree = revset.expandaliases(ui, tree)
3516 newtree = revset.expandaliases(ui, tree)
3514 if newtree != tree:
3517 if newtree != tree:
3515 ui.note(("* expanded:\n"), revset.prettyformat(newtree), "\n")
3518 ui.note(("* expanded:\n"), revset.prettyformat(newtree), "\n")
3516 tree = newtree
3519 tree = newtree
3517 newtree = revset.foldconcat(tree)
3520 newtree = revset.foldconcat(tree)
3518 if newtree != tree:
3521 if newtree != tree:
3519 ui.note(("* concatenated:\n"), revset.prettyformat(newtree), "\n")
3522 ui.note(("* concatenated:\n"), revset.prettyformat(newtree), "\n")
3520 if opts["optimize"]:
3523 if opts["optimize"]:
3521 optimizedtree = revset.optimize(newtree)
3524 optimizedtree = revset.optimize(newtree)
3522 ui.note(("* optimized:\n"),
3525 ui.note(("* optimized:\n"),
3523 revset.prettyformat(optimizedtree), "\n")
3526 revset.prettyformat(optimizedtree), "\n")
3524 func = revset.match(ui, expr, repo)
3527 func = revset.match(ui, expr, repo)
3525 revs = func(repo)
3528 revs = func(repo)
3526 if ui.verbose:
3529 if ui.verbose:
3527 ui.note(("* set:\n"), revset.prettyformatset(revs), "\n")
3530 ui.note(("* set:\n"), revset.prettyformatset(revs), "\n")
3528 for c in revs:
3531 for c in revs:
3529 ui.write("%s\n" % c)
3532 ui.write("%s\n" % c)
3530
3533
3531 @command('debugsetparents', [], _('REV1 [REV2]'))
3534 @command('debugsetparents', [], _('REV1 [REV2]'))
3532 def debugsetparents(ui, repo, rev1, rev2=None):
3535 def debugsetparents(ui, repo, rev1, rev2=None):
3533 """manually set the parents of the current working directory
3536 """manually set the parents of the current working directory
3534
3537
3535 This is useful for writing repository conversion tools, but should
3538 This is useful for writing repository conversion tools, but should
3536 be used with care. For example, neither the working directory nor the
3539 be used with care. For example, neither the working directory nor the
3537 dirstate is updated, so file status may be incorrect after running this
3540 dirstate is updated, so file status may be incorrect after running this
3538 command.
3541 command.
3539
3542
3540 Returns 0 on success.
3543 Returns 0 on success.
3541 """
3544 """
3542
3545
3543 r1 = scmutil.revsingle(repo, rev1).node()
3546 r1 = scmutil.revsingle(repo, rev1).node()
3544 r2 = scmutil.revsingle(repo, rev2, 'null').node()
3547 r2 = scmutil.revsingle(repo, rev2, 'null').node()
3545
3548
3546 with repo.wlock():
3549 with repo.wlock():
3547 repo.setparents(r1, r2)
3550 repo.setparents(r1, r2)
3548
3551
3549 @command('debugdirstate|debugstate',
3552 @command('debugdirstate|debugstate',
3550 [('', 'nodates', None, _('do not display the saved mtime')),
3553 [('', 'nodates', None, _('do not display the saved mtime')),
3551 ('', 'datesort', None, _('sort by saved mtime'))],
3554 ('', 'datesort', None, _('sort by saved mtime'))],
3552 _('[OPTION]...'))
3555 _('[OPTION]...'))
3553 def debugstate(ui, repo, **opts):
3556 def debugstate(ui, repo, **opts):
3554 """show the contents of the current dirstate"""
3557 """show the contents of the current dirstate"""
3555
3558
3556 nodates = opts.get('nodates')
3559 nodates = opts.get('nodates')
3557 datesort = opts.get('datesort')
3560 datesort = opts.get('datesort')
3558
3561
3559 timestr = ""
3562 timestr = ""
3560 if datesort:
3563 if datesort:
3561 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
3564 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
3562 else:
3565 else:
3563 keyfunc = None # sort by filename
3566 keyfunc = None # sort by filename
3564 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
3567 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
3565 if ent[3] == -1:
3568 if ent[3] == -1:
3566 timestr = 'unset '
3569 timestr = 'unset '
3567 elif nodates:
3570 elif nodates:
3568 timestr = 'set '
3571 timestr = 'set '
3569 else:
3572 else:
3570 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
3573 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
3571 time.localtime(ent[3]))
3574 time.localtime(ent[3]))
3572 if ent[1] & 0o20000:
3575 if ent[1] & 0o20000:
3573 mode = 'lnk'
3576 mode = 'lnk'
3574 else:
3577 else:
3575 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
3578 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
3576 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
3579 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
3577 for f in repo.dirstate.copies():
3580 for f in repo.dirstate.copies():
3578 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
3581 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
3579
3582
3580 @command('debugsub',
3583 @command('debugsub',
3581 [('r', 'rev', '',
3584 [('r', 'rev', '',
3582 _('revision to check'), _('REV'))],
3585 _('revision to check'), _('REV'))],
3583 _('[-r REV] [REV]'))
3586 _('[-r REV] [REV]'))
3584 def debugsub(ui, repo, rev=None):
3587 def debugsub(ui, repo, rev=None):
3585 ctx = scmutil.revsingle(repo, rev, None)
3588 ctx = scmutil.revsingle(repo, rev, None)
3586 for k, v in sorted(ctx.substate.items()):
3589 for k, v in sorted(ctx.substate.items()):
3587 ui.write(('path %s\n') % k)
3590 ui.write(('path %s\n') % k)
3588 ui.write((' source %s\n') % v[0])
3591 ui.write((' source %s\n') % v[0])
3589 ui.write((' revision %s\n') % v[1])
3592 ui.write((' revision %s\n') % v[1])
3590
3593
3591 @command('debugsuccessorssets',
3594 @command('debugsuccessorssets',
3592 [],
3595 [],
3593 _('[REV]'))
3596 _('[REV]'))
3594 def debugsuccessorssets(ui, repo, *revs):
3597 def debugsuccessorssets(ui, repo, *revs):
3595 """show set of successors for revision
3598 """show set of successors for revision
3596
3599
3597 A successors set of changeset A is a consistent group of revisions that
3600 A successors set of changeset A is a consistent group of revisions that
3598 succeed A. It contains non-obsolete changesets only.
3601 succeed A. It contains non-obsolete changesets only.
3599
3602
3600 In most cases a changeset A has a single successors set containing a single
3603 In most cases a changeset A has a single successors set containing a single
3601 successor (changeset A replaced by A').
3604 successor (changeset A replaced by A').
3602
3605
3603 A changeset that is made obsolete with no successors are called "pruned".
3606 A changeset that is made obsolete with no successors are called "pruned".
3604 Such changesets have no successors sets at all.
3607 Such changesets have no successors sets at all.
3605
3608
3606 A changeset that has been "split" will have a successors set containing
3609 A changeset that has been "split" will have a successors set containing
3607 more than one successor.
3610 more than one successor.
3608
3611
3609 A changeset that has been rewritten in multiple different ways is called
3612 A changeset that has been rewritten in multiple different ways is called
3610 "divergent". Such changesets have multiple successor sets (each of which
3613 "divergent". Such changesets have multiple successor sets (each of which
3611 may also be split, i.e. have multiple successors).
3614 may also be split, i.e. have multiple successors).
3612
3615
3613 Results are displayed as follows::
3616 Results are displayed as follows::
3614
3617
3615 <rev1>
3618 <rev1>
3616 <successors-1A>
3619 <successors-1A>
3617 <rev2>
3620 <rev2>
3618 <successors-2A>
3621 <successors-2A>
3619 <successors-2B1> <successors-2B2> <successors-2B3>
3622 <successors-2B1> <successors-2B2> <successors-2B3>
3620
3623
3621 Here rev2 has two possible (i.e. divergent) successors sets. The first
3624 Here rev2 has two possible (i.e. divergent) successors sets. The first
3622 holds one element, whereas the second holds three (i.e. the changeset has
3625 holds one element, whereas the second holds three (i.e. the changeset has
3623 been split).
3626 been split).
3624 """
3627 """
3625 # passed to successorssets caching computation from one call to another
3628 # passed to successorssets caching computation from one call to another
3626 cache = {}
3629 cache = {}
3627 ctx2str = str
3630 ctx2str = str
3628 node2str = short
3631 node2str = short
3629 if ui.debug():
3632 if ui.debug():
3630 def ctx2str(ctx):
3633 def ctx2str(ctx):
3631 return ctx.hex()
3634 return ctx.hex()
3632 node2str = hex
3635 node2str = hex
3633 for rev in scmutil.revrange(repo, revs):
3636 for rev in scmutil.revrange(repo, revs):
3634 ctx = repo[rev]
3637 ctx = repo[rev]
3635 ui.write('%s\n'% ctx2str(ctx))
3638 ui.write('%s\n'% ctx2str(ctx))
3636 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
3639 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
3637 if succsset:
3640 if succsset:
3638 ui.write(' ')
3641 ui.write(' ')
3639 ui.write(node2str(succsset[0]))
3642 ui.write(node2str(succsset[0]))
3640 for node in succsset[1:]:
3643 for node in succsset[1:]:
3641 ui.write(' ')
3644 ui.write(' ')
3642 ui.write(node2str(node))
3645 ui.write(node2str(node))
3643 ui.write('\n')
3646 ui.write('\n')
3644
3647
3645 @command('debugtemplate',
3648 @command('debugtemplate',
3646 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
3649 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
3647 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
3650 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
3648 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
3651 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
3649 optionalrepo=True)
3652 optionalrepo=True)
3650 def debugtemplate(ui, repo, tmpl, **opts):
3653 def debugtemplate(ui, repo, tmpl, **opts):
3651 """parse and apply a template
3654 """parse and apply a template
3652
3655
3653 If -r/--rev is given, the template is processed as a log template and
3656 If -r/--rev is given, the template is processed as a log template and
3654 applied to the given changesets. Otherwise, it is processed as a generic
3657 applied to the given changesets. Otherwise, it is processed as a generic
3655 template.
3658 template.
3656
3659
3657 Use --verbose to print the parsed tree.
3660 Use --verbose to print the parsed tree.
3658 """
3661 """
3659 revs = None
3662 revs = None
3660 if opts['rev']:
3663 if opts['rev']:
3661 if repo is None:
3664 if repo is None:
3662 raise error.RepoError(_('there is no Mercurial repository here '
3665 raise error.RepoError(_('there is no Mercurial repository here '
3663 '(.hg not found)'))
3666 '(.hg not found)'))
3664 revs = scmutil.revrange(repo, opts['rev'])
3667 revs = scmutil.revrange(repo, opts['rev'])
3665
3668
3666 props = {}
3669 props = {}
3667 for d in opts['define']:
3670 for d in opts['define']:
3668 try:
3671 try:
3669 k, v = (e.strip() for e in d.split('=', 1))
3672 k, v = (e.strip() for e in d.split('=', 1))
3670 if not k:
3673 if not k:
3671 raise ValueError
3674 raise ValueError
3672 props[k] = v
3675 props[k] = v
3673 except ValueError:
3676 except ValueError:
3674 raise error.Abort(_('malformed keyword definition: %s') % d)
3677 raise error.Abort(_('malformed keyword definition: %s') % d)
3675
3678
3676 if ui.verbose:
3679 if ui.verbose:
3677 aliases = ui.configitems('templatealias')
3680 aliases = ui.configitems('templatealias')
3678 tree = templater.parse(tmpl)
3681 tree = templater.parse(tmpl)
3679 ui.note(templater.prettyformat(tree), '\n')
3682 ui.note(templater.prettyformat(tree), '\n')
3680 newtree = templater.expandaliases(tree, aliases)
3683 newtree = templater.expandaliases(tree, aliases)
3681 if newtree != tree:
3684 if newtree != tree:
3682 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
3685 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
3683
3686
3684 mapfile = None
3687 mapfile = None
3685 if revs is None:
3688 if revs is None:
3686 k = 'debugtemplate'
3689 k = 'debugtemplate'
3687 t = formatter.maketemplater(ui, k, tmpl)
3690 t = formatter.maketemplater(ui, k, tmpl)
3688 ui.write(templater.stringify(t(k, **props)))
3691 ui.write(templater.stringify(t(k, **props)))
3689 else:
3692 else:
3690 displayer = cmdutil.changeset_templater(ui, repo, None, opts, tmpl,
3693 displayer = cmdutil.changeset_templater(ui, repo, None, opts, tmpl,
3691 mapfile, buffered=False)
3694 mapfile, buffered=False)
3692 for r in revs:
3695 for r in revs:
3693 displayer.show(repo[r], **props)
3696 displayer.show(repo[r], **props)
3694 displayer.close()
3697 displayer.close()
3695
3698
3696 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
3699 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
3697 def debugwalk(ui, repo, *pats, **opts):
3700 def debugwalk(ui, repo, *pats, **opts):
3698 """show how files match on given patterns"""
3701 """show how files match on given patterns"""
3699 m = scmutil.match(repo[None], pats, opts)
3702 m = scmutil.match(repo[None], pats, opts)
3700 items = list(repo.walk(m))
3703 items = list(repo.walk(m))
3701 if not items:
3704 if not items:
3702 return
3705 return
3703 f = lambda fn: fn
3706 f = lambda fn: fn
3704 if ui.configbool('ui', 'slash') and os.sep != '/':
3707 if ui.configbool('ui', 'slash') and os.sep != '/':
3705 f = lambda fn: util.normpath(fn)
3708 f = lambda fn: util.normpath(fn)
3706 fmt = 'f %%-%ds %%-%ds %%s' % (
3709 fmt = 'f %%-%ds %%-%ds %%s' % (
3707 max([len(abs) for abs in items]),
3710 max([len(abs) for abs in items]),
3708 max([len(m.rel(abs)) for abs in items]))
3711 max([len(m.rel(abs)) for abs in items]))
3709 for abs in items:
3712 for abs in items:
3710 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3713 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3711 ui.write("%s\n" % line.rstrip())
3714 ui.write("%s\n" % line.rstrip())
3712
3715
3713 @command('debugwireargs',
3716 @command('debugwireargs',
3714 [('', 'three', '', 'three'),
3717 [('', 'three', '', 'three'),
3715 ('', 'four', '', 'four'),
3718 ('', 'four', '', 'four'),
3716 ('', 'five', '', 'five'),
3719 ('', 'five', '', 'five'),
3717 ] + remoteopts,
3720 ] + remoteopts,
3718 _('REPO [OPTIONS]... [ONE [TWO]]'),
3721 _('REPO [OPTIONS]... [ONE [TWO]]'),
3719 norepo=True)
3722 norepo=True)
3720 def debugwireargs(ui, repopath, *vals, **opts):
3723 def debugwireargs(ui, repopath, *vals, **opts):
3721 repo = hg.peer(ui, opts, repopath)
3724 repo = hg.peer(ui, opts, repopath)
3722 for opt in remoteopts:
3725 for opt in remoteopts:
3723 del opts[opt[1]]
3726 del opts[opt[1]]
3724 args = {}
3727 args = {}
3725 for k, v in opts.iteritems():
3728 for k, v in opts.iteritems():
3726 if v:
3729 if v:
3727 args[k] = v
3730 args[k] = v
3728 # run twice to check that we don't mess up the stream for the next command
3731 # run twice to check that we don't mess up the stream for the next command
3729 res1 = repo.debugwireargs(*vals, **args)
3732 res1 = repo.debugwireargs(*vals, **args)
3730 res2 = repo.debugwireargs(*vals, **args)
3733 res2 = repo.debugwireargs(*vals, **args)
3731 ui.write("%s\n" % res1)
3734 ui.write("%s\n" % res1)
3732 if res1 != res2:
3735 if res1 != res2:
3733 ui.warn("%s\n" % res2)
3736 ui.warn("%s\n" % res2)
3734
3737
3735 @command('^diff',
3738 @command('^diff',
3736 [('r', 'rev', [], _('revision'), _('REV')),
3739 [('r', 'rev', [], _('revision'), _('REV')),
3737 ('c', 'change', '', _('change made by revision'), _('REV'))
3740 ('c', 'change', '', _('change made by revision'), _('REV'))
3738 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3741 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3739 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3742 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3740 inferrepo=True)
3743 inferrepo=True)
3741 def diff(ui, repo, *pats, **opts):
3744 def diff(ui, repo, *pats, **opts):
3742 """diff repository (or selected files)
3745 """diff repository (or selected files)
3743
3746
3744 Show differences between revisions for the specified files.
3747 Show differences between revisions for the specified files.
3745
3748
3746 Differences between files are shown using the unified diff format.
3749 Differences between files are shown using the unified diff format.
3747
3750
3748 .. note::
3751 .. note::
3749
3752
3750 :hg:`diff` may generate unexpected results for merges, as it will
3753 :hg:`diff` may generate unexpected results for merges, as it will
3751 default to comparing against the working directory's first
3754 default to comparing against the working directory's first
3752 parent changeset if no revisions are specified.
3755 parent changeset if no revisions are specified.
3753
3756
3754 When two revision arguments are given, then changes are shown
3757 When two revision arguments are given, then changes are shown
3755 between those revisions. If only one revision is specified then
3758 between those revisions. If only one revision is specified then
3756 that revision is compared to the working directory, and, when no
3759 that revision is compared to the working directory, and, when no
3757 revisions are specified, the working directory files are compared
3760 revisions are specified, the working directory files are compared
3758 to its first parent.
3761 to its first parent.
3759
3762
3760 Alternatively you can specify -c/--change with a revision to see
3763 Alternatively you can specify -c/--change with a revision to see
3761 the changes in that changeset relative to its first parent.
3764 the changes in that changeset relative to its first parent.
3762
3765
3763 Without the -a/--text option, diff will avoid generating diffs of
3766 Without the -a/--text option, diff will avoid generating diffs of
3764 files it detects as binary. With -a, diff will generate a diff
3767 files it detects as binary. With -a, diff will generate a diff
3765 anyway, probably with undesirable results.
3768 anyway, probably with undesirable results.
3766
3769
3767 Use the -g/--git option to generate diffs in the git extended diff
3770 Use the -g/--git option to generate diffs in the git extended diff
3768 format. For more information, read :hg:`help diffs`.
3771 format. For more information, read :hg:`help diffs`.
3769
3772
3770 .. container:: verbose
3773 .. container:: verbose
3771
3774
3772 Examples:
3775 Examples:
3773
3776
3774 - compare a file in the current working directory to its parent::
3777 - compare a file in the current working directory to its parent::
3775
3778
3776 hg diff foo.c
3779 hg diff foo.c
3777
3780
3778 - compare two historical versions of a directory, with rename info::
3781 - compare two historical versions of a directory, with rename info::
3779
3782
3780 hg diff --git -r 1.0:1.2 lib/
3783 hg diff --git -r 1.0:1.2 lib/
3781
3784
3782 - get change stats relative to the last change on some date::
3785 - get change stats relative to the last change on some date::
3783
3786
3784 hg diff --stat -r "date('may 2')"
3787 hg diff --stat -r "date('may 2')"
3785
3788
3786 - diff all newly-added files that contain a keyword::
3789 - diff all newly-added files that contain a keyword::
3787
3790
3788 hg diff "set:added() and grep(GNU)"
3791 hg diff "set:added() and grep(GNU)"
3789
3792
3790 - compare a revision and its parents::
3793 - compare a revision and its parents::
3791
3794
3792 hg diff -c 9353 # compare against first parent
3795 hg diff -c 9353 # compare against first parent
3793 hg diff -r 9353^:9353 # same using revset syntax
3796 hg diff -r 9353^:9353 # same using revset syntax
3794 hg diff -r 9353^2:9353 # compare against the second parent
3797 hg diff -r 9353^2:9353 # compare against the second parent
3795
3798
3796 Returns 0 on success.
3799 Returns 0 on success.
3797 """
3800 """
3798
3801
3799 revs = opts.get('rev')
3802 revs = opts.get('rev')
3800 change = opts.get('change')
3803 change = opts.get('change')
3801 stat = opts.get('stat')
3804 stat = opts.get('stat')
3802 reverse = opts.get('reverse')
3805 reverse = opts.get('reverse')
3803
3806
3804 if revs and change:
3807 if revs and change:
3805 msg = _('cannot specify --rev and --change at the same time')
3808 msg = _('cannot specify --rev and --change at the same time')
3806 raise error.Abort(msg)
3809 raise error.Abort(msg)
3807 elif change:
3810 elif change:
3808 node2 = scmutil.revsingle(repo, change, None).node()
3811 node2 = scmutil.revsingle(repo, change, None).node()
3809 node1 = repo[node2].p1().node()
3812 node1 = repo[node2].p1().node()
3810 else:
3813 else:
3811 node1, node2 = scmutil.revpair(repo, revs)
3814 node1, node2 = scmutil.revpair(repo, revs)
3812
3815
3813 if reverse:
3816 if reverse:
3814 node1, node2 = node2, node1
3817 node1, node2 = node2, node1
3815
3818
3816 diffopts = patch.diffallopts(ui, opts)
3819 diffopts = patch.diffallopts(ui, opts)
3817 m = scmutil.match(repo[node2], pats, opts)
3820 m = scmutil.match(repo[node2], pats, opts)
3818 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3821 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3819 listsubrepos=opts.get('subrepos'),
3822 listsubrepos=opts.get('subrepos'),
3820 root=opts.get('root'))
3823 root=opts.get('root'))
3821
3824
3822 @command('^export',
3825 @command('^export',
3823 [('o', 'output', '',
3826 [('o', 'output', '',
3824 _('print output to file with formatted name'), _('FORMAT')),
3827 _('print output to file with formatted name'), _('FORMAT')),
3825 ('', 'switch-parent', None, _('diff against the second parent')),
3828 ('', 'switch-parent', None, _('diff against the second parent')),
3826 ('r', 'rev', [], _('revisions to export'), _('REV')),
3829 ('r', 'rev', [], _('revisions to export'), _('REV')),
3827 ] + diffopts,
3830 ] + diffopts,
3828 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3831 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3829 def export(ui, repo, *changesets, **opts):
3832 def export(ui, repo, *changesets, **opts):
3830 """dump the header and diffs for one or more changesets
3833 """dump the header and diffs for one or more changesets
3831
3834
3832 Print the changeset header and diffs for one or more revisions.
3835 Print the changeset header and diffs for one or more revisions.
3833 If no revision is given, the parent of the working directory is used.
3836 If no revision is given, the parent of the working directory is used.
3834
3837
3835 The information shown in the changeset header is: author, date,
3838 The information shown in the changeset header is: author, date,
3836 branch name (if non-default), changeset hash, parent(s) and commit
3839 branch name (if non-default), changeset hash, parent(s) and commit
3837 comment.
3840 comment.
3838
3841
3839 .. note::
3842 .. note::
3840
3843
3841 :hg:`export` may generate unexpected diff output for merge
3844 :hg:`export` may generate unexpected diff output for merge
3842 changesets, as it will compare the merge changeset against its
3845 changesets, as it will compare the merge changeset against its
3843 first parent only.
3846 first parent only.
3844
3847
3845 Output may be to a file, in which case the name of the file is
3848 Output may be to a file, in which case the name of the file is
3846 given using a format string. The formatting rules are as follows:
3849 given using a format string. The formatting rules are as follows:
3847
3850
3848 :``%%``: literal "%" character
3851 :``%%``: literal "%" character
3849 :``%H``: changeset hash (40 hexadecimal digits)
3852 :``%H``: changeset hash (40 hexadecimal digits)
3850 :``%N``: number of patches being generated
3853 :``%N``: number of patches being generated
3851 :``%R``: changeset revision number
3854 :``%R``: changeset revision number
3852 :``%b``: basename of the exporting repository
3855 :``%b``: basename of the exporting repository
3853 :``%h``: short-form changeset hash (12 hexadecimal digits)
3856 :``%h``: short-form changeset hash (12 hexadecimal digits)
3854 :``%m``: first line of the commit message (only alphanumeric characters)
3857 :``%m``: first line of the commit message (only alphanumeric characters)
3855 :``%n``: zero-padded sequence number, starting at 1
3858 :``%n``: zero-padded sequence number, starting at 1
3856 :``%r``: zero-padded changeset revision number
3859 :``%r``: zero-padded changeset revision number
3857
3860
3858 Without the -a/--text option, export will avoid generating diffs
3861 Without the -a/--text option, export will avoid generating diffs
3859 of files it detects as binary. With -a, export will generate a
3862 of files it detects as binary. With -a, export will generate a
3860 diff anyway, probably with undesirable results.
3863 diff anyway, probably with undesirable results.
3861
3864
3862 Use the -g/--git option to generate diffs in the git extended diff
3865 Use the -g/--git option to generate diffs in the git extended diff
3863 format. See :hg:`help diffs` for more information.
3866 format. See :hg:`help diffs` for more information.
3864
3867
3865 With the --switch-parent option, the diff will be against the
3868 With the --switch-parent option, the diff will be against the
3866 second parent. It can be useful to review a merge.
3869 second parent. It can be useful to review a merge.
3867
3870
3868 .. container:: verbose
3871 .. container:: verbose
3869
3872
3870 Examples:
3873 Examples:
3871
3874
3872 - use export and import to transplant a bugfix to the current
3875 - use export and import to transplant a bugfix to the current
3873 branch::
3876 branch::
3874
3877
3875 hg export -r 9353 | hg import -
3878 hg export -r 9353 | hg import -
3876
3879
3877 - export all the changesets between two revisions to a file with
3880 - export all the changesets between two revisions to a file with
3878 rename information::
3881 rename information::
3879
3882
3880 hg export --git -r 123:150 > changes.txt
3883 hg export --git -r 123:150 > changes.txt
3881
3884
3882 - split outgoing changes into a series of patches with
3885 - split outgoing changes into a series of patches with
3883 descriptive names::
3886 descriptive names::
3884
3887
3885 hg export -r "outgoing()" -o "%n-%m.patch"
3888 hg export -r "outgoing()" -o "%n-%m.patch"
3886
3889
3887 Returns 0 on success.
3890 Returns 0 on success.
3888 """
3891 """
3889 changesets += tuple(opts.get('rev', []))
3892 changesets += tuple(opts.get('rev', []))
3890 if not changesets:
3893 if not changesets:
3891 changesets = ['.']
3894 changesets = ['.']
3892 revs = scmutil.revrange(repo, changesets)
3895 revs = scmutil.revrange(repo, changesets)
3893 if not revs:
3896 if not revs:
3894 raise error.Abort(_("export requires at least one changeset"))
3897 raise error.Abort(_("export requires at least one changeset"))
3895 if len(revs) > 1:
3898 if len(revs) > 1:
3896 ui.note(_('exporting patches:\n'))
3899 ui.note(_('exporting patches:\n'))
3897 else:
3900 else:
3898 ui.note(_('exporting patch:\n'))
3901 ui.note(_('exporting patch:\n'))
3899 cmdutil.export(repo, revs, template=opts.get('output'),
3902 cmdutil.export(repo, revs, template=opts.get('output'),
3900 switch_parent=opts.get('switch_parent'),
3903 switch_parent=opts.get('switch_parent'),
3901 opts=patch.diffallopts(ui, opts))
3904 opts=patch.diffallopts(ui, opts))
3902
3905
3903 @command('files',
3906 @command('files',
3904 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3907 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3905 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3908 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3906 ] + walkopts + formatteropts + subrepoopts,
3909 ] + walkopts + formatteropts + subrepoopts,
3907 _('[OPTION]... [PATTERN]...'))
3910 _('[OPTION]... [PATTERN]...'))
3908 def files(ui, repo, *pats, **opts):
3911 def files(ui, repo, *pats, **opts):
3909 """list tracked files
3912 """list tracked files
3910
3913
3911 Print files under Mercurial control in the working directory or
3914 Print files under Mercurial control in the working directory or
3912 specified revision whose names match the given patterns (excluding
3915 specified revision whose names match the given patterns (excluding
3913 removed files).
3916 removed files).
3914
3917
3915 If no patterns are given to match, this command prints the names
3918 If no patterns are given to match, this command prints the names
3916 of all files under Mercurial control in the working directory.
3919 of all files under Mercurial control in the working directory.
3917
3920
3918 .. container:: verbose
3921 .. container:: verbose
3919
3922
3920 Examples:
3923 Examples:
3921
3924
3922 - list all files under the current directory::
3925 - list all files under the current directory::
3923
3926
3924 hg files .
3927 hg files .
3925
3928
3926 - shows sizes and flags for current revision::
3929 - shows sizes and flags for current revision::
3927
3930
3928 hg files -vr .
3931 hg files -vr .
3929
3932
3930 - list all files named README::
3933 - list all files named README::
3931
3934
3932 hg files -I "**/README"
3935 hg files -I "**/README"
3933
3936
3934 - list all binary files::
3937 - list all binary files::
3935
3938
3936 hg files "set:binary()"
3939 hg files "set:binary()"
3937
3940
3938 - find files containing a regular expression::
3941 - find files containing a regular expression::
3939
3942
3940 hg files "set:grep('bob')"
3943 hg files "set:grep('bob')"
3941
3944
3942 - search tracked file contents with xargs and grep::
3945 - search tracked file contents with xargs and grep::
3943
3946
3944 hg files -0 | xargs -0 grep foo
3947 hg files -0 | xargs -0 grep foo
3945
3948
3946 See :hg:`help patterns` and :hg:`help filesets` for more information
3949 See :hg:`help patterns` and :hg:`help filesets` for more information
3947 on specifying file patterns.
3950 on specifying file patterns.
3948
3951
3949 Returns 0 if a match is found, 1 otherwise.
3952 Returns 0 if a match is found, 1 otherwise.
3950
3953
3951 """
3954 """
3952 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3955 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3953
3956
3954 end = '\n'
3957 end = '\n'
3955 if opts.get('print0'):
3958 if opts.get('print0'):
3956 end = '\0'
3959 end = '\0'
3957 fm = ui.formatter('files', opts)
3960 fm = ui.formatter('files', opts)
3958 fmt = '%s' + end
3961 fmt = '%s' + end
3959
3962
3960 m = scmutil.match(ctx, pats, opts)
3963 m = scmutil.match(ctx, pats, opts)
3961 ret = cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
3964 ret = cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
3962
3965
3963 fm.end()
3966 fm.end()
3964
3967
3965 return ret
3968 return ret
3966
3969
3967 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3970 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3968 def forget(ui, repo, *pats, **opts):
3971 def forget(ui, repo, *pats, **opts):
3969 """forget the specified files on the next commit
3972 """forget the specified files on the next commit
3970
3973
3971 Mark the specified files so they will no longer be tracked
3974 Mark the specified files so they will no longer be tracked
3972 after the next commit.
3975 after the next commit.
3973
3976
3974 This only removes files from the current branch, not from the
3977 This only removes files from the current branch, not from the
3975 entire project history, and it does not delete them from the
3978 entire project history, and it does not delete them from the
3976 working directory.
3979 working directory.
3977
3980
3978 To delete the file from the working directory, see :hg:`remove`.
3981 To delete the file from the working directory, see :hg:`remove`.
3979
3982
3980 To undo a forget before the next commit, see :hg:`add`.
3983 To undo a forget before the next commit, see :hg:`add`.
3981
3984
3982 .. container:: verbose
3985 .. container:: verbose
3983
3986
3984 Examples:
3987 Examples:
3985
3988
3986 - forget newly-added binary files::
3989 - forget newly-added binary files::
3987
3990
3988 hg forget "set:added() and binary()"
3991 hg forget "set:added() and binary()"
3989
3992
3990 - forget files that would be excluded by .hgignore::
3993 - forget files that would be excluded by .hgignore::
3991
3994
3992 hg forget "set:hgignore()"
3995 hg forget "set:hgignore()"
3993
3996
3994 Returns 0 on success.
3997 Returns 0 on success.
3995 """
3998 """
3996
3999
3997 if not pats:
4000 if not pats:
3998 raise error.Abort(_('no files specified'))
4001 raise error.Abort(_('no files specified'))
3999
4002
4000 m = scmutil.match(repo[None], pats, opts)
4003 m = scmutil.match(repo[None], pats, opts)
4001 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
4004 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
4002 return rejected and 1 or 0
4005 return rejected and 1 or 0
4003
4006
4004 @command(
4007 @command(
4005 'graft',
4008 'graft',
4006 [('r', 'rev', [], _('revisions to graft'), _('REV')),
4009 [('r', 'rev', [], _('revisions to graft'), _('REV')),
4007 ('c', 'continue', False, _('resume interrupted graft')),
4010 ('c', 'continue', False, _('resume interrupted graft')),
4008 ('e', 'edit', False, _('invoke editor on commit messages')),
4011 ('e', 'edit', False, _('invoke editor on commit messages')),
4009 ('', 'log', None, _('append graft info to log message')),
4012 ('', 'log', None, _('append graft info to log message')),
4010 ('f', 'force', False, _('force graft')),
4013 ('f', 'force', False, _('force graft')),
4011 ('D', 'currentdate', False,
4014 ('D', 'currentdate', False,
4012 _('record the current date as commit date')),
4015 _('record the current date as commit date')),
4013 ('U', 'currentuser', False,
4016 ('U', 'currentuser', False,
4014 _('record the current user as committer'), _('DATE'))]
4017 _('record the current user as committer'), _('DATE'))]
4015 + commitopts2 + mergetoolopts + dryrunopts,
4018 + commitopts2 + mergetoolopts + dryrunopts,
4016 _('[OPTION]... [-r REV]... REV...'))
4019 _('[OPTION]... [-r REV]... REV...'))
4017 def graft(ui, repo, *revs, **opts):
4020 def graft(ui, repo, *revs, **opts):
4018 '''copy changes from other branches onto the current branch
4021 '''copy changes from other branches onto the current branch
4019
4022
4020 This command uses Mercurial's merge logic to copy individual
4023 This command uses Mercurial's merge logic to copy individual
4021 changes from other branches without merging branches in the
4024 changes from other branches without merging branches in the
4022 history graph. This is sometimes known as 'backporting' or
4025 history graph. This is sometimes known as 'backporting' or
4023 'cherry-picking'. By default, graft will copy user, date, and
4026 'cherry-picking'. By default, graft will copy user, date, and
4024 description from the source changesets.
4027 description from the source changesets.
4025
4028
4026 Changesets that are ancestors of the current revision, that have
4029 Changesets that are ancestors of the current revision, that have
4027 already been grafted, or that are merges will be skipped.
4030 already been grafted, or that are merges will be skipped.
4028
4031
4029 If --log is specified, log messages will have a comment appended
4032 If --log is specified, log messages will have a comment appended
4030 of the form::
4033 of the form::
4031
4034
4032 (grafted from CHANGESETHASH)
4035 (grafted from CHANGESETHASH)
4033
4036
4034 If --force is specified, revisions will be grafted even if they
4037 If --force is specified, revisions will be grafted even if they
4035 are already ancestors of or have been grafted to the destination.
4038 are already ancestors of or have been grafted to the destination.
4036 This is useful when the revisions have since been backed out.
4039 This is useful when the revisions have since been backed out.
4037
4040
4038 If a graft merge results in conflicts, the graft process is
4041 If a graft merge results in conflicts, the graft process is
4039 interrupted so that the current merge can be manually resolved.
4042 interrupted so that the current merge can be manually resolved.
4040 Once all conflicts are addressed, the graft process can be
4043 Once all conflicts are addressed, the graft process can be
4041 continued with the -c/--continue option.
4044 continued with the -c/--continue option.
4042
4045
4043 .. note::
4046 .. note::
4044
4047
4045 The -c/--continue option does not reapply earlier options, except
4048 The -c/--continue option does not reapply earlier options, except
4046 for --force.
4049 for --force.
4047
4050
4048 .. container:: verbose
4051 .. container:: verbose
4049
4052
4050 Examples:
4053 Examples:
4051
4054
4052 - copy a single change to the stable branch and edit its description::
4055 - copy a single change to the stable branch and edit its description::
4053
4056
4054 hg update stable
4057 hg update stable
4055 hg graft --edit 9393
4058 hg graft --edit 9393
4056
4059
4057 - graft a range of changesets with one exception, updating dates::
4060 - graft a range of changesets with one exception, updating dates::
4058
4061
4059 hg graft -D "2085::2093 and not 2091"
4062 hg graft -D "2085::2093 and not 2091"
4060
4063
4061 - continue a graft after resolving conflicts::
4064 - continue a graft after resolving conflicts::
4062
4065
4063 hg graft -c
4066 hg graft -c
4064
4067
4065 - show the source of a grafted changeset::
4068 - show the source of a grafted changeset::
4066
4069
4067 hg log --debug -r .
4070 hg log --debug -r .
4068
4071
4069 - show revisions sorted by date::
4072 - show revisions sorted by date::
4070
4073
4071 hg log -r "sort(all(), date)"
4074 hg log -r "sort(all(), date)"
4072
4075
4073 See :hg:`help revisions` and :hg:`help revsets` for more about
4076 See :hg:`help revisions` and :hg:`help revsets` for more about
4074 specifying revisions.
4077 specifying revisions.
4075
4078
4076 Returns 0 on successful completion.
4079 Returns 0 on successful completion.
4077 '''
4080 '''
4078 with repo.wlock():
4081 with repo.wlock():
4079 return _dograft(ui, repo, *revs, **opts)
4082 return _dograft(ui, repo, *revs, **opts)
4080
4083
4081 def _dograft(ui, repo, *revs, **opts):
4084 def _dograft(ui, repo, *revs, **opts):
4082 if revs and opts.get('rev'):
4085 if revs and opts.get('rev'):
4083 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
4086 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
4084 'revision ordering!\n'))
4087 'revision ordering!\n'))
4085
4088
4086 revs = list(revs)
4089 revs = list(revs)
4087 revs.extend(opts.get('rev'))
4090 revs.extend(opts.get('rev'))
4088
4091
4089 if not opts.get('user') and opts.get('currentuser'):
4092 if not opts.get('user') and opts.get('currentuser'):
4090 opts['user'] = ui.username()
4093 opts['user'] = ui.username()
4091 if not opts.get('date') and opts.get('currentdate'):
4094 if not opts.get('date') and opts.get('currentdate'):
4092 opts['date'] = "%d %d" % util.makedate()
4095 opts['date'] = "%d %d" % util.makedate()
4093
4096
4094 editor = cmdutil.getcommiteditor(editform='graft', **opts)
4097 editor = cmdutil.getcommiteditor(editform='graft', **opts)
4095
4098
4096 cont = False
4099 cont = False
4097 if opts.get('continue'):
4100 if opts.get('continue'):
4098 cont = True
4101 cont = True
4099 if revs:
4102 if revs:
4100 raise error.Abort(_("can't specify --continue and revisions"))
4103 raise error.Abort(_("can't specify --continue and revisions"))
4101 # read in unfinished revisions
4104 # read in unfinished revisions
4102 try:
4105 try:
4103 nodes = repo.vfs.read('graftstate').splitlines()
4106 nodes = repo.vfs.read('graftstate').splitlines()
4104 revs = [repo[node].rev() for node in nodes]
4107 revs = [repo[node].rev() for node in nodes]
4105 except IOError as inst:
4108 except IOError as inst:
4106 if inst.errno != errno.ENOENT:
4109 if inst.errno != errno.ENOENT:
4107 raise
4110 raise
4108 cmdutil.wrongtooltocontinue(repo, _('graft'))
4111 cmdutil.wrongtooltocontinue(repo, _('graft'))
4109 else:
4112 else:
4110 cmdutil.checkunfinished(repo)
4113 cmdutil.checkunfinished(repo)
4111 cmdutil.bailifchanged(repo)
4114 cmdutil.bailifchanged(repo)
4112 if not revs:
4115 if not revs:
4113 raise error.Abort(_('no revisions specified'))
4116 raise error.Abort(_('no revisions specified'))
4114 revs = scmutil.revrange(repo, revs)
4117 revs = scmutil.revrange(repo, revs)
4115
4118
4116 skipped = set()
4119 skipped = set()
4117 # check for merges
4120 # check for merges
4118 for rev in repo.revs('%ld and merge()', revs):
4121 for rev in repo.revs('%ld and merge()', revs):
4119 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
4122 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
4120 skipped.add(rev)
4123 skipped.add(rev)
4121 revs = [r for r in revs if r not in skipped]
4124 revs = [r for r in revs if r not in skipped]
4122 if not revs:
4125 if not revs:
4123 return -1
4126 return -1
4124
4127
4125 # Don't check in the --continue case, in effect retaining --force across
4128 # Don't check in the --continue case, in effect retaining --force across
4126 # --continues. That's because without --force, any revisions we decided to
4129 # --continues. That's because without --force, any revisions we decided to
4127 # skip would have been filtered out here, so they wouldn't have made their
4130 # skip would have been filtered out here, so they wouldn't have made their
4128 # way to the graftstate. With --force, any revisions we would have otherwise
4131 # way to the graftstate. With --force, any revisions we would have otherwise
4129 # skipped would not have been filtered out, and if they hadn't been applied
4132 # skipped would not have been filtered out, and if they hadn't been applied
4130 # already, they'd have been in the graftstate.
4133 # already, they'd have been in the graftstate.
4131 if not (cont or opts.get('force')):
4134 if not (cont or opts.get('force')):
4132 # check for ancestors of dest branch
4135 # check for ancestors of dest branch
4133 crev = repo['.'].rev()
4136 crev = repo['.'].rev()
4134 ancestors = repo.changelog.ancestors([crev], inclusive=True)
4137 ancestors = repo.changelog.ancestors([crev], inclusive=True)
4135 # Cannot use x.remove(y) on smart set, this has to be a list.
4138 # Cannot use x.remove(y) on smart set, this has to be a list.
4136 # XXX make this lazy in the future
4139 # XXX make this lazy in the future
4137 revs = list(revs)
4140 revs = list(revs)
4138 # don't mutate while iterating, create a copy
4141 # don't mutate while iterating, create a copy
4139 for rev in list(revs):
4142 for rev in list(revs):
4140 if rev in ancestors:
4143 if rev in ancestors:
4141 ui.warn(_('skipping ancestor revision %d:%s\n') %
4144 ui.warn(_('skipping ancestor revision %d:%s\n') %
4142 (rev, repo[rev]))
4145 (rev, repo[rev]))
4143 # XXX remove on list is slow
4146 # XXX remove on list is slow
4144 revs.remove(rev)
4147 revs.remove(rev)
4145 if not revs:
4148 if not revs:
4146 return -1
4149 return -1
4147
4150
4148 # analyze revs for earlier grafts
4151 # analyze revs for earlier grafts
4149 ids = {}
4152 ids = {}
4150 for ctx in repo.set("%ld", revs):
4153 for ctx in repo.set("%ld", revs):
4151 ids[ctx.hex()] = ctx.rev()
4154 ids[ctx.hex()] = ctx.rev()
4152 n = ctx.extra().get('source')
4155 n = ctx.extra().get('source')
4153 if n:
4156 if n:
4154 ids[n] = ctx.rev()
4157 ids[n] = ctx.rev()
4155
4158
4156 # check ancestors for earlier grafts
4159 # check ancestors for earlier grafts
4157 ui.debug('scanning for duplicate grafts\n')
4160 ui.debug('scanning for duplicate grafts\n')
4158
4161
4159 for rev in repo.changelog.findmissingrevs(revs, [crev]):
4162 for rev in repo.changelog.findmissingrevs(revs, [crev]):
4160 ctx = repo[rev]
4163 ctx = repo[rev]
4161 n = ctx.extra().get('source')
4164 n = ctx.extra().get('source')
4162 if n in ids:
4165 if n in ids:
4163 try:
4166 try:
4164 r = repo[n].rev()
4167 r = repo[n].rev()
4165 except error.RepoLookupError:
4168 except error.RepoLookupError:
4166 r = None
4169 r = None
4167 if r in revs:
4170 if r in revs:
4168 ui.warn(_('skipping revision %d:%s '
4171 ui.warn(_('skipping revision %d:%s '
4169 '(already grafted to %d:%s)\n')
4172 '(already grafted to %d:%s)\n')
4170 % (r, repo[r], rev, ctx))
4173 % (r, repo[r], rev, ctx))
4171 revs.remove(r)
4174 revs.remove(r)
4172 elif ids[n] in revs:
4175 elif ids[n] in revs:
4173 if r is None:
4176 if r is None:
4174 ui.warn(_('skipping already grafted revision %d:%s '
4177 ui.warn(_('skipping already grafted revision %d:%s '
4175 '(%d:%s also has unknown origin %s)\n')
4178 '(%d:%s also has unknown origin %s)\n')
4176 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
4179 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
4177 else:
4180 else:
4178 ui.warn(_('skipping already grafted revision %d:%s '
4181 ui.warn(_('skipping already grafted revision %d:%s '
4179 '(%d:%s also has origin %d:%s)\n')
4182 '(%d:%s also has origin %d:%s)\n')
4180 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
4183 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
4181 revs.remove(ids[n])
4184 revs.remove(ids[n])
4182 elif ctx.hex() in ids:
4185 elif ctx.hex() in ids:
4183 r = ids[ctx.hex()]
4186 r = ids[ctx.hex()]
4184 ui.warn(_('skipping already grafted revision %d:%s '
4187 ui.warn(_('skipping already grafted revision %d:%s '
4185 '(was grafted from %d:%s)\n') %
4188 '(was grafted from %d:%s)\n') %
4186 (r, repo[r], rev, ctx))
4189 (r, repo[r], rev, ctx))
4187 revs.remove(r)
4190 revs.remove(r)
4188 if not revs:
4191 if not revs:
4189 return -1
4192 return -1
4190
4193
4191 for pos, ctx in enumerate(repo.set("%ld", revs)):
4194 for pos, ctx in enumerate(repo.set("%ld", revs)):
4192 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
4195 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
4193 ctx.description().split('\n', 1)[0])
4196 ctx.description().split('\n', 1)[0])
4194 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
4197 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
4195 if names:
4198 if names:
4196 desc += ' (%s)' % ' '.join(names)
4199 desc += ' (%s)' % ' '.join(names)
4197 ui.status(_('grafting %s\n') % desc)
4200 ui.status(_('grafting %s\n') % desc)
4198 if opts.get('dry_run'):
4201 if opts.get('dry_run'):
4199 continue
4202 continue
4200
4203
4201 source = ctx.extra().get('source')
4204 source = ctx.extra().get('source')
4202 extra = {}
4205 extra = {}
4203 if source:
4206 if source:
4204 extra['source'] = source
4207 extra['source'] = source
4205 extra['intermediate-source'] = ctx.hex()
4208 extra['intermediate-source'] = ctx.hex()
4206 else:
4209 else:
4207 extra['source'] = ctx.hex()
4210 extra['source'] = ctx.hex()
4208 user = ctx.user()
4211 user = ctx.user()
4209 if opts.get('user'):
4212 if opts.get('user'):
4210 user = opts['user']
4213 user = opts['user']
4211 date = ctx.date()
4214 date = ctx.date()
4212 if opts.get('date'):
4215 if opts.get('date'):
4213 date = opts['date']
4216 date = opts['date']
4214 message = ctx.description()
4217 message = ctx.description()
4215 if opts.get('log'):
4218 if opts.get('log'):
4216 message += '\n(grafted from %s)' % ctx.hex()
4219 message += '\n(grafted from %s)' % ctx.hex()
4217
4220
4218 # we don't merge the first commit when continuing
4221 # we don't merge the first commit when continuing
4219 if not cont:
4222 if not cont:
4220 # perform the graft merge with p1(rev) as 'ancestor'
4223 # perform the graft merge with p1(rev) as 'ancestor'
4221 try:
4224 try:
4222 # ui.forcemerge is an internal variable, do not document
4225 # ui.forcemerge is an internal variable, do not document
4223 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4226 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4224 'graft')
4227 'graft')
4225 stats = mergemod.graft(repo, ctx, ctx.p1(),
4228 stats = mergemod.graft(repo, ctx, ctx.p1(),
4226 ['local', 'graft'])
4229 ['local', 'graft'])
4227 finally:
4230 finally:
4228 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
4231 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
4229 # report any conflicts
4232 # report any conflicts
4230 if stats and stats[3] > 0:
4233 if stats and stats[3] > 0:
4231 # write out state for --continue
4234 # write out state for --continue
4232 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
4235 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
4233 repo.vfs.write('graftstate', ''.join(nodelines))
4236 repo.vfs.write('graftstate', ''.join(nodelines))
4234 extra = ''
4237 extra = ''
4235 if opts.get('user'):
4238 if opts.get('user'):
4236 extra += ' --user %s' % util.shellquote(opts['user'])
4239 extra += ' --user %s' % util.shellquote(opts['user'])
4237 if opts.get('date'):
4240 if opts.get('date'):
4238 extra += ' --date %s' % util.shellquote(opts['date'])
4241 extra += ' --date %s' % util.shellquote(opts['date'])
4239 if opts.get('log'):
4242 if opts.get('log'):
4240 extra += ' --log'
4243 extra += ' --log'
4241 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
4244 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
4242 raise error.Abort(
4245 raise error.Abort(
4243 _("unresolved conflicts, can't continue"),
4246 _("unresolved conflicts, can't continue"),
4244 hint=hint)
4247 hint=hint)
4245 else:
4248 else:
4246 cont = False
4249 cont = False
4247
4250
4248 # commit
4251 # commit
4249 node = repo.commit(text=message, user=user,
4252 node = repo.commit(text=message, user=user,
4250 date=date, extra=extra, editor=editor)
4253 date=date, extra=extra, editor=editor)
4251 if node is None:
4254 if node is None:
4252 ui.warn(
4255 ui.warn(
4253 _('note: graft of %d:%s created no changes to commit\n') %
4256 _('note: graft of %d:%s created no changes to commit\n') %
4254 (ctx.rev(), ctx))
4257 (ctx.rev(), ctx))
4255
4258
4256 # remove state when we complete successfully
4259 # remove state when we complete successfully
4257 if not opts.get('dry_run'):
4260 if not opts.get('dry_run'):
4258 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
4261 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
4259
4262
4260 return 0
4263 return 0
4261
4264
4262 @command('grep',
4265 @command('grep',
4263 [('0', 'print0', None, _('end fields with NUL')),
4266 [('0', 'print0', None, _('end fields with NUL')),
4264 ('', 'all', None, _('print all revisions that match')),
4267 ('', 'all', None, _('print all revisions that match')),
4265 ('a', 'text', None, _('treat all files as text')),
4268 ('a', 'text', None, _('treat all files as text')),
4266 ('f', 'follow', None,
4269 ('f', 'follow', None,
4267 _('follow changeset history,'
4270 _('follow changeset history,'
4268 ' or file history across copies and renames')),
4271 ' or file history across copies and renames')),
4269 ('i', 'ignore-case', None, _('ignore case when matching')),
4272 ('i', 'ignore-case', None, _('ignore case when matching')),
4270 ('l', 'files-with-matches', None,
4273 ('l', 'files-with-matches', None,
4271 _('print only filenames and revisions that match')),
4274 _('print only filenames and revisions that match')),
4272 ('n', 'line-number', None, _('print matching line numbers')),
4275 ('n', 'line-number', None, _('print matching line numbers')),
4273 ('r', 'rev', [],
4276 ('r', 'rev', [],
4274 _('only search files changed within revision range'), _('REV')),
4277 _('only search files changed within revision range'), _('REV')),
4275 ('u', 'user', None, _('list the author (long with -v)')),
4278 ('u', 'user', None, _('list the author (long with -v)')),
4276 ('d', 'date', None, _('list the date (short with -q)')),
4279 ('d', 'date', None, _('list the date (short with -q)')),
4277 ] + walkopts,
4280 ] + walkopts,
4278 _('[OPTION]... PATTERN [FILE]...'),
4281 _('[OPTION]... PATTERN [FILE]...'),
4279 inferrepo=True)
4282 inferrepo=True)
4280 def grep(ui, repo, pattern, *pats, **opts):
4283 def grep(ui, repo, pattern, *pats, **opts):
4281 """search for a pattern in specified files and revisions
4284 """search for a pattern in specified files and revisions
4282
4285
4283 Search revisions of files for a regular expression.
4286 Search revisions of files for a regular expression.
4284
4287
4285 This command behaves differently than Unix grep. It only accepts
4288 This command behaves differently than Unix grep. It only accepts
4286 Python/Perl regexps. It searches repository history, not the
4289 Python/Perl regexps. It searches repository history, not the
4287 working directory. It always prints the revision number in which a
4290 working directory. It always prints the revision number in which a
4288 match appears.
4291 match appears.
4289
4292
4290 By default, grep only prints output for the first revision of a
4293 By default, grep only prints output for the first revision of a
4291 file in which it finds a match. To get it to print every revision
4294 file in which it finds a match. To get it to print every revision
4292 that contains a change in match status ("-" for a match that
4295 that contains a change in match status ("-" for a match that
4293 becomes a non-match, or "+" for a non-match that becomes a match),
4296 becomes a non-match, or "+" for a non-match that becomes a match),
4294 use the --all flag.
4297 use the --all flag.
4295
4298
4296 Returns 0 if a match is found, 1 otherwise.
4299 Returns 0 if a match is found, 1 otherwise.
4297 """
4300 """
4298 reflags = re.M
4301 reflags = re.M
4299 if opts.get('ignore_case'):
4302 if opts.get('ignore_case'):
4300 reflags |= re.I
4303 reflags |= re.I
4301 try:
4304 try:
4302 regexp = util.re.compile(pattern, reflags)
4305 regexp = util.re.compile(pattern, reflags)
4303 except re.error as inst:
4306 except re.error as inst:
4304 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
4307 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
4305 return 1
4308 return 1
4306 sep, eol = ':', '\n'
4309 sep, eol = ':', '\n'
4307 if opts.get('print0'):
4310 if opts.get('print0'):
4308 sep = eol = '\0'
4311 sep = eol = '\0'
4309
4312
4310 getfile = util.lrucachefunc(repo.file)
4313 getfile = util.lrucachefunc(repo.file)
4311
4314
4312 def matchlines(body):
4315 def matchlines(body):
4313 begin = 0
4316 begin = 0
4314 linenum = 0
4317 linenum = 0
4315 while begin < len(body):
4318 while begin < len(body):
4316 match = regexp.search(body, begin)
4319 match = regexp.search(body, begin)
4317 if not match:
4320 if not match:
4318 break
4321 break
4319 mstart, mend = match.span()
4322 mstart, mend = match.span()
4320 linenum += body.count('\n', begin, mstart) + 1
4323 linenum += body.count('\n', begin, mstart) + 1
4321 lstart = body.rfind('\n', begin, mstart) + 1 or begin
4324 lstart = body.rfind('\n', begin, mstart) + 1 or begin
4322 begin = body.find('\n', mend) + 1 or len(body) + 1
4325 begin = body.find('\n', mend) + 1 or len(body) + 1
4323 lend = begin - 1
4326 lend = begin - 1
4324 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
4327 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
4325
4328
4326 class linestate(object):
4329 class linestate(object):
4327 def __init__(self, line, linenum, colstart, colend):
4330 def __init__(self, line, linenum, colstart, colend):
4328 self.line = line
4331 self.line = line
4329 self.linenum = linenum
4332 self.linenum = linenum
4330 self.colstart = colstart
4333 self.colstart = colstart
4331 self.colend = colend
4334 self.colend = colend
4332
4335
4333 def __hash__(self):
4336 def __hash__(self):
4334 return hash((self.linenum, self.line))
4337 return hash((self.linenum, self.line))
4335
4338
4336 def __eq__(self, other):
4339 def __eq__(self, other):
4337 return self.line == other.line
4340 return self.line == other.line
4338
4341
4339 def __iter__(self):
4342 def __iter__(self):
4340 yield (self.line[:self.colstart], '')
4343 yield (self.line[:self.colstart], '')
4341 yield (self.line[self.colstart:self.colend], 'grep.match')
4344 yield (self.line[self.colstart:self.colend], 'grep.match')
4342 rest = self.line[self.colend:]
4345 rest = self.line[self.colend:]
4343 while rest != '':
4346 while rest != '':
4344 match = regexp.search(rest)
4347 match = regexp.search(rest)
4345 if not match:
4348 if not match:
4346 yield (rest, '')
4349 yield (rest, '')
4347 break
4350 break
4348 mstart, mend = match.span()
4351 mstart, mend = match.span()
4349 yield (rest[:mstart], '')
4352 yield (rest[:mstart], '')
4350 yield (rest[mstart:mend], 'grep.match')
4353 yield (rest[mstart:mend], 'grep.match')
4351 rest = rest[mend:]
4354 rest = rest[mend:]
4352
4355
4353 matches = {}
4356 matches = {}
4354 copies = {}
4357 copies = {}
4355 def grepbody(fn, rev, body):
4358 def grepbody(fn, rev, body):
4356 matches[rev].setdefault(fn, [])
4359 matches[rev].setdefault(fn, [])
4357 m = matches[rev][fn]
4360 m = matches[rev][fn]
4358 for lnum, cstart, cend, line in matchlines(body):
4361 for lnum, cstart, cend, line in matchlines(body):
4359 s = linestate(line, lnum, cstart, cend)
4362 s = linestate(line, lnum, cstart, cend)
4360 m.append(s)
4363 m.append(s)
4361
4364
4362 def difflinestates(a, b):
4365 def difflinestates(a, b):
4363 sm = difflib.SequenceMatcher(None, a, b)
4366 sm = difflib.SequenceMatcher(None, a, b)
4364 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
4367 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
4365 if tag == 'insert':
4368 if tag == 'insert':
4366 for i in xrange(blo, bhi):
4369 for i in xrange(blo, bhi):
4367 yield ('+', b[i])
4370 yield ('+', b[i])
4368 elif tag == 'delete':
4371 elif tag == 'delete':
4369 for i in xrange(alo, ahi):
4372 for i in xrange(alo, ahi):
4370 yield ('-', a[i])
4373 yield ('-', a[i])
4371 elif tag == 'replace':
4374 elif tag == 'replace':
4372 for i in xrange(alo, ahi):
4375 for i in xrange(alo, ahi):
4373 yield ('-', a[i])
4376 yield ('-', a[i])
4374 for i in xrange(blo, bhi):
4377 for i in xrange(blo, bhi):
4375 yield ('+', b[i])
4378 yield ('+', b[i])
4376
4379
4377 def display(fn, ctx, pstates, states):
4380 def display(fn, ctx, pstates, states):
4378 rev = ctx.rev()
4381 rev = ctx.rev()
4379 if ui.quiet:
4382 if ui.quiet:
4380 datefunc = util.shortdate
4383 datefunc = util.shortdate
4381 else:
4384 else:
4382 datefunc = util.datestr
4385 datefunc = util.datestr
4383 found = False
4386 found = False
4384 @util.cachefunc
4387 @util.cachefunc
4385 def binary():
4388 def binary():
4386 flog = getfile(fn)
4389 flog = getfile(fn)
4387 return util.binary(flog.read(ctx.filenode(fn)))
4390 return util.binary(flog.read(ctx.filenode(fn)))
4388
4391
4389 if opts.get('all'):
4392 if opts.get('all'):
4390 iter = difflinestates(pstates, states)
4393 iter = difflinestates(pstates, states)
4391 else:
4394 else:
4392 iter = [('', l) for l in states]
4395 iter = [('', l) for l in states]
4393 for change, l in iter:
4396 for change, l in iter:
4394 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
4397 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
4395
4398
4396 if opts.get('line_number'):
4399 if opts.get('line_number'):
4397 cols.append((str(l.linenum), 'grep.linenumber'))
4400 cols.append((str(l.linenum), 'grep.linenumber'))
4398 if opts.get('all'):
4401 if opts.get('all'):
4399 cols.append((change, 'grep.change'))
4402 cols.append((change, 'grep.change'))
4400 if opts.get('user'):
4403 if opts.get('user'):
4401 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
4404 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
4402 if opts.get('date'):
4405 if opts.get('date'):
4403 cols.append((datefunc(ctx.date()), 'grep.date'))
4406 cols.append((datefunc(ctx.date()), 'grep.date'))
4404 for col, label in cols[:-1]:
4407 for col, label in cols[:-1]:
4405 ui.write(col, label=label)
4408 ui.write(col, label=label)
4406 ui.write(sep, label='grep.sep')
4409 ui.write(sep, label='grep.sep')
4407 ui.write(cols[-1][0], label=cols[-1][1])
4410 ui.write(cols[-1][0], label=cols[-1][1])
4408 if not opts.get('files_with_matches'):
4411 if not opts.get('files_with_matches'):
4409 ui.write(sep, label='grep.sep')
4412 ui.write(sep, label='grep.sep')
4410 if not opts.get('text') and binary():
4413 if not opts.get('text') and binary():
4411 ui.write(_(" Binary file matches"))
4414 ui.write(_(" Binary file matches"))
4412 else:
4415 else:
4413 for s, label in l:
4416 for s, label in l:
4414 ui.write(s, label=label)
4417 ui.write(s, label=label)
4415 ui.write(eol)
4418 ui.write(eol)
4416 found = True
4419 found = True
4417 if opts.get('files_with_matches'):
4420 if opts.get('files_with_matches'):
4418 break
4421 break
4419 return found
4422 return found
4420
4423
4421 skip = {}
4424 skip = {}
4422 revfiles = {}
4425 revfiles = {}
4423 matchfn = scmutil.match(repo[None], pats, opts)
4426 matchfn = scmutil.match(repo[None], pats, opts)
4424 found = False
4427 found = False
4425 follow = opts.get('follow')
4428 follow = opts.get('follow')
4426
4429
4427 def prep(ctx, fns):
4430 def prep(ctx, fns):
4428 rev = ctx.rev()
4431 rev = ctx.rev()
4429 pctx = ctx.p1()
4432 pctx = ctx.p1()
4430 parent = pctx.rev()
4433 parent = pctx.rev()
4431 matches.setdefault(rev, {})
4434 matches.setdefault(rev, {})
4432 matches.setdefault(parent, {})
4435 matches.setdefault(parent, {})
4433 files = revfiles.setdefault(rev, [])
4436 files = revfiles.setdefault(rev, [])
4434 for fn in fns:
4437 for fn in fns:
4435 flog = getfile(fn)
4438 flog = getfile(fn)
4436 try:
4439 try:
4437 fnode = ctx.filenode(fn)
4440 fnode = ctx.filenode(fn)
4438 except error.LookupError:
4441 except error.LookupError:
4439 continue
4442 continue
4440
4443
4441 copied = flog.renamed(fnode)
4444 copied = flog.renamed(fnode)
4442 copy = follow and copied and copied[0]
4445 copy = follow and copied and copied[0]
4443 if copy:
4446 if copy:
4444 copies.setdefault(rev, {})[fn] = copy
4447 copies.setdefault(rev, {})[fn] = copy
4445 if fn in skip:
4448 if fn in skip:
4446 if copy:
4449 if copy:
4447 skip[copy] = True
4450 skip[copy] = True
4448 continue
4451 continue
4449 files.append(fn)
4452 files.append(fn)
4450
4453
4451 if fn not in matches[rev]:
4454 if fn not in matches[rev]:
4452 grepbody(fn, rev, flog.read(fnode))
4455 grepbody(fn, rev, flog.read(fnode))
4453
4456
4454 pfn = copy or fn
4457 pfn = copy or fn
4455 if pfn not in matches[parent]:
4458 if pfn not in matches[parent]:
4456 try:
4459 try:
4457 fnode = pctx.filenode(pfn)
4460 fnode = pctx.filenode(pfn)
4458 grepbody(pfn, parent, flog.read(fnode))
4461 grepbody(pfn, parent, flog.read(fnode))
4459 except error.LookupError:
4462 except error.LookupError:
4460 pass
4463 pass
4461
4464
4462 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
4465 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
4463 rev = ctx.rev()
4466 rev = ctx.rev()
4464 parent = ctx.p1().rev()
4467 parent = ctx.p1().rev()
4465 for fn in sorted(revfiles.get(rev, [])):
4468 for fn in sorted(revfiles.get(rev, [])):
4466 states = matches[rev][fn]
4469 states = matches[rev][fn]
4467 copy = copies.get(rev, {}).get(fn)
4470 copy = copies.get(rev, {}).get(fn)
4468 if fn in skip:
4471 if fn in skip:
4469 if copy:
4472 if copy:
4470 skip[copy] = True
4473 skip[copy] = True
4471 continue
4474 continue
4472 pstates = matches.get(parent, {}).get(copy or fn, [])
4475 pstates = matches.get(parent, {}).get(copy or fn, [])
4473 if pstates or states:
4476 if pstates or states:
4474 r = display(fn, ctx, pstates, states)
4477 r = display(fn, ctx, pstates, states)
4475 found = found or r
4478 found = found or r
4476 if r and not opts.get('all'):
4479 if r and not opts.get('all'):
4477 skip[fn] = True
4480 skip[fn] = True
4478 if copy:
4481 if copy:
4479 skip[copy] = True
4482 skip[copy] = True
4480 del matches[rev]
4483 del matches[rev]
4481 del revfiles[rev]
4484 del revfiles[rev]
4482
4485
4483 return not found
4486 return not found
4484
4487
4485 @command('heads',
4488 @command('heads',
4486 [('r', 'rev', '',
4489 [('r', 'rev', '',
4487 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
4490 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
4488 ('t', 'topo', False, _('show topological heads only')),
4491 ('t', 'topo', False, _('show topological heads only')),
4489 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
4492 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
4490 ('c', 'closed', False, _('show normal and closed branch heads')),
4493 ('c', 'closed', False, _('show normal and closed branch heads')),
4491 ] + templateopts,
4494 ] + templateopts,
4492 _('[-ct] [-r STARTREV] [REV]...'))
4495 _('[-ct] [-r STARTREV] [REV]...'))
4493 def heads(ui, repo, *branchrevs, **opts):
4496 def heads(ui, repo, *branchrevs, **opts):
4494 """show branch heads
4497 """show branch heads
4495
4498
4496 With no arguments, show all open branch heads in the repository.
4499 With no arguments, show all open branch heads in the repository.
4497 Branch heads are changesets that have no descendants on the
4500 Branch heads are changesets that have no descendants on the
4498 same branch. They are where development generally takes place and
4501 same branch. They are where development generally takes place and
4499 are the usual targets for update and merge operations.
4502 are the usual targets for update and merge operations.
4500
4503
4501 If one or more REVs are given, only open branch heads on the
4504 If one or more REVs are given, only open branch heads on the
4502 branches associated with the specified changesets are shown. This
4505 branches associated with the specified changesets are shown. This
4503 means that you can use :hg:`heads .` to see the heads on the
4506 means that you can use :hg:`heads .` to see the heads on the
4504 currently checked-out branch.
4507 currently checked-out branch.
4505
4508
4506 If -c/--closed is specified, also show branch heads marked closed
4509 If -c/--closed is specified, also show branch heads marked closed
4507 (see :hg:`commit --close-branch`).
4510 (see :hg:`commit --close-branch`).
4508
4511
4509 If STARTREV is specified, only those heads that are descendants of
4512 If STARTREV is specified, only those heads that are descendants of
4510 STARTREV will be displayed.
4513 STARTREV will be displayed.
4511
4514
4512 If -t/--topo is specified, named branch mechanics will be ignored and only
4515 If -t/--topo is specified, named branch mechanics will be ignored and only
4513 topological heads (changesets with no children) will be shown.
4516 topological heads (changesets with no children) will be shown.
4514
4517
4515 Returns 0 if matching heads are found, 1 if not.
4518 Returns 0 if matching heads are found, 1 if not.
4516 """
4519 """
4517
4520
4518 start = None
4521 start = None
4519 if 'rev' in opts:
4522 if 'rev' in opts:
4520 start = scmutil.revsingle(repo, opts['rev'], None).node()
4523 start = scmutil.revsingle(repo, opts['rev'], None).node()
4521
4524
4522 if opts.get('topo'):
4525 if opts.get('topo'):
4523 heads = [repo[h] for h in repo.heads(start)]
4526 heads = [repo[h] for h in repo.heads(start)]
4524 else:
4527 else:
4525 heads = []
4528 heads = []
4526 for branch in repo.branchmap():
4529 for branch in repo.branchmap():
4527 heads += repo.branchheads(branch, start, opts.get('closed'))
4530 heads += repo.branchheads(branch, start, opts.get('closed'))
4528 heads = [repo[h] for h in heads]
4531 heads = [repo[h] for h in heads]
4529
4532
4530 if branchrevs:
4533 if branchrevs:
4531 branches = set(repo[br].branch() for br in branchrevs)
4534 branches = set(repo[br].branch() for br in branchrevs)
4532 heads = [h for h in heads if h.branch() in branches]
4535 heads = [h for h in heads if h.branch() in branches]
4533
4536
4534 if opts.get('active') and branchrevs:
4537 if opts.get('active') and branchrevs:
4535 dagheads = repo.heads(start)
4538 dagheads = repo.heads(start)
4536 heads = [h for h in heads if h.node() in dagheads]
4539 heads = [h for h in heads if h.node() in dagheads]
4537
4540
4538 if branchrevs:
4541 if branchrevs:
4539 haveheads = set(h.branch() for h in heads)
4542 haveheads = set(h.branch() for h in heads)
4540 if branches - haveheads:
4543 if branches - haveheads:
4541 headless = ', '.join(b for b in branches - haveheads)
4544 headless = ', '.join(b for b in branches - haveheads)
4542 msg = _('no open branch heads found on branches %s')
4545 msg = _('no open branch heads found on branches %s')
4543 if opts.get('rev'):
4546 if opts.get('rev'):
4544 msg += _(' (started at %s)') % opts['rev']
4547 msg += _(' (started at %s)') % opts['rev']
4545 ui.warn((msg + '\n') % headless)
4548 ui.warn((msg + '\n') % headless)
4546
4549
4547 if not heads:
4550 if not heads:
4548 return 1
4551 return 1
4549
4552
4550 heads = sorted(heads, key=lambda x: -x.rev())
4553 heads = sorted(heads, key=lambda x: -x.rev())
4551 displayer = cmdutil.show_changeset(ui, repo, opts)
4554 displayer = cmdutil.show_changeset(ui, repo, opts)
4552 for ctx in heads:
4555 for ctx in heads:
4553 displayer.show(ctx)
4556 displayer.show(ctx)
4554 displayer.close()
4557 displayer.close()
4555
4558
4556 @command('help',
4559 @command('help',
4557 [('e', 'extension', None, _('show only help for extensions')),
4560 [('e', 'extension', None, _('show only help for extensions')),
4558 ('c', 'command', None, _('show only help for commands')),
4561 ('c', 'command', None, _('show only help for commands')),
4559 ('k', 'keyword', None, _('show topics matching keyword')),
4562 ('k', 'keyword', None, _('show topics matching keyword')),
4560 ('s', 'system', [], _('show help for specific platform(s)')),
4563 ('s', 'system', [], _('show help for specific platform(s)')),
4561 ],
4564 ],
4562 _('[-ecks] [TOPIC]'),
4565 _('[-ecks] [TOPIC]'),
4563 norepo=True)
4566 norepo=True)
4564 def help_(ui, name=None, **opts):
4567 def help_(ui, name=None, **opts):
4565 """show help for a given topic or a help overview
4568 """show help for a given topic or a help overview
4566
4569
4567 With no arguments, print a list of commands with short help messages.
4570 With no arguments, print a list of commands with short help messages.
4568
4571
4569 Given a topic, extension, or command name, print help for that
4572 Given a topic, extension, or command name, print help for that
4570 topic.
4573 topic.
4571
4574
4572 Returns 0 if successful.
4575 Returns 0 if successful.
4573 """
4576 """
4574
4577
4575 textwidth = ui.configint('ui', 'textwidth', 78)
4578 textwidth = ui.configint('ui', 'textwidth', 78)
4576 termwidth = ui.termwidth() - 2
4579 termwidth = ui.termwidth() - 2
4577 if textwidth <= 0 or termwidth < textwidth:
4580 if textwidth <= 0 or termwidth < textwidth:
4578 textwidth = termwidth
4581 textwidth = termwidth
4579
4582
4580 keep = opts.get('system') or []
4583 keep = opts.get('system') or []
4581 if len(keep) == 0:
4584 if len(keep) == 0:
4582 if sys.platform.startswith('win'):
4585 if sys.platform.startswith('win'):
4583 keep.append('windows')
4586 keep.append('windows')
4584 elif sys.platform == 'OpenVMS':
4587 elif sys.platform == 'OpenVMS':
4585 keep.append('vms')
4588 keep.append('vms')
4586 elif sys.platform == 'plan9':
4589 elif sys.platform == 'plan9':
4587 keep.append('plan9')
4590 keep.append('plan9')
4588 else:
4591 else:
4589 keep.append('unix')
4592 keep.append('unix')
4590 keep.append(sys.platform.lower())
4593 keep.append(sys.platform.lower())
4591 if ui.verbose:
4594 if ui.verbose:
4592 keep.append('verbose')
4595 keep.append('verbose')
4593
4596
4594 section = None
4597 section = None
4595 subtopic = None
4598 subtopic = None
4596 if name and '.' in name:
4599 if name and '.' in name:
4597 name, remaining = name.split('.', 1)
4600 name, remaining = name.split('.', 1)
4598 remaining = encoding.lower(remaining)
4601 remaining = encoding.lower(remaining)
4599 if '.' in remaining:
4602 if '.' in remaining:
4600 subtopic, section = remaining.split('.', 1)
4603 subtopic, section = remaining.split('.', 1)
4601 else:
4604 else:
4602 if name in help.subtopics:
4605 if name in help.subtopics:
4603 subtopic = remaining
4606 subtopic = remaining
4604 else:
4607 else:
4605 section = remaining
4608 section = remaining
4606
4609
4607 text = help.help_(ui, name, subtopic=subtopic, **opts)
4610 text = help.help_(ui, name, subtopic=subtopic, **opts)
4608
4611
4609 formatted, pruned = minirst.format(text, textwidth, keep=keep,
4612 formatted, pruned = minirst.format(text, textwidth, keep=keep,
4610 section=section)
4613 section=section)
4611
4614
4612 # We could have been given a weird ".foo" section without a name
4615 # We could have been given a weird ".foo" section without a name
4613 # to look for, or we could have simply failed to found "foo.bar"
4616 # to look for, or we could have simply failed to found "foo.bar"
4614 # because bar isn't a section of foo
4617 # because bar isn't a section of foo
4615 if section and not (formatted and name):
4618 if section and not (formatted and name):
4616 raise error.Abort(_("help section not found"))
4619 raise error.Abort(_("help section not found"))
4617
4620
4618 if 'verbose' in pruned:
4621 if 'verbose' in pruned:
4619 keep.append('omitted')
4622 keep.append('omitted')
4620 else:
4623 else:
4621 keep.append('notomitted')
4624 keep.append('notomitted')
4622 formatted, pruned = minirst.format(text, textwidth, keep=keep,
4625 formatted, pruned = minirst.format(text, textwidth, keep=keep,
4623 section=section)
4626 section=section)
4624 ui.write(formatted)
4627 ui.write(formatted)
4625
4628
4626
4629
4627 @command('identify|id',
4630 @command('identify|id',
4628 [('r', 'rev', '',
4631 [('r', 'rev', '',
4629 _('identify the specified revision'), _('REV')),
4632 _('identify the specified revision'), _('REV')),
4630 ('n', 'num', None, _('show local revision number')),
4633 ('n', 'num', None, _('show local revision number')),
4631 ('i', 'id', None, _('show global revision id')),
4634 ('i', 'id', None, _('show global revision id')),
4632 ('b', 'branch', None, _('show branch')),
4635 ('b', 'branch', None, _('show branch')),
4633 ('t', 'tags', None, _('show tags')),
4636 ('t', 'tags', None, _('show tags')),
4634 ('B', 'bookmarks', None, _('show bookmarks')),
4637 ('B', 'bookmarks', None, _('show bookmarks')),
4635 ] + remoteopts,
4638 ] + remoteopts,
4636 _('[-nibtB] [-r REV] [SOURCE]'),
4639 _('[-nibtB] [-r REV] [SOURCE]'),
4637 optionalrepo=True)
4640 optionalrepo=True)
4638 def identify(ui, repo, source=None, rev=None,
4641 def identify(ui, repo, source=None, rev=None,
4639 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
4642 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
4640 """identify the working directory or specified revision
4643 """identify the working directory or specified revision
4641
4644
4642 Print a summary identifying the repository state at REV using one or
4645 Print a summary identifying the repository state at REV using one or
4643 two parent hash identifiers, followed by a "+" if the working
4646 two parent hash identifiers, followed by a "+" if the working
4644 directory has uncommitted changes, the branch name (if not default),
4647 directory has uncommitted changes, the branch name (if not default),
4645 a list of tags, and a list of bookmarks.
4648 a list of tags, and a list of bookmarks.
4646
4649
4647 When REV is not given, print a summary of the current state of the
4650 When REV is not given, print a summary of the current state of the
4648 repository.
4651 repository.
4649
4652
4650 Specifying a path to a repository root or Mercurial bundle will
4653 Specifying a path to a repository root or Mercurial bundle will
4651 cause lookup to operate on that repository/bundle.
4654 cause lookup to operate on that repository/bundle.
4652
4655
4653 .. container:: verbose
4656 .. container:: verbose
4654
4657
4655 Examples:
4658 Examples:
4656
4659
4657 - generate a build identifier for the working directory::
4660 - generate a build identifier for the working directory::
4658
4661
4659 hg id --id > build-id.dat
4662 hg id --id > build-id.dat
4660
4663
4661 - find the revision corresponding to a tag::
4664 - find the revision corresponding to a tag::
4662
4665
4663 hg id -n -r 1.3
4666 hg id -n -r 1.3
4664
4667
4665 - check the most recent revision of a remote repository::
4668 - check the most recent revision of a remote repository::
4666
4669
4667 hg id -r tip http://selenic.com/hg/
4670 hg id -r tip http://selenic.com/hg/
4668
4671
4669 See :hg:`log` for generating more information about specific revisions,
4672 See :hg:`log` for generating more information about specific revisions,
4670 including full hash identifiers.
4673 including full hash identifiers.
4671
4674
4672 Returns 0 if successful.
4675 Returns 0 if successful.
4673 """
4676 """
4674
4677
4675 if not repo and not source:
4678 if not repo and not source:
4676 raise error.Abort(_("there is no Mercurial repository here "
4679 raise error.Abort(_("there is no Mercurial repository here "
4677 "(.hg not found)"))
4680 "(.hg not found)"))
4678
4681
4679 if ui.debugflag:
4682 if ui.debugflag:
4680 hexfunc = hex
4683 hexfunc = hex
4681 else:
4684 else:
4682 hexfunc = short
4685 hexfunc = short
4683 default = not (num or id or branch or tags or bookmarks)
4686 default = not (num or id or branch or tags or bookmarks)
4684 output = []
4687 output = []
4685 revs = []
4688 revs = []
4686
4689
4687 if source:
4690 if source:
4688 source, branches = hg.parseurl(ui.expandpath(source))
4691 source, branches = hg.parseurl(ui.expandpath(source))
4689 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
4692 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
4690 repo = peer.local()
4693 repo = peer.local()
4691 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
4694 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
4692
4695
4693 if not repo:
4696 if not repo:
4694 if num or branch or tags:
4697 if num or branch or tags:
4695 raise error.Abort(
4698 raise error.Abort(
4696 _("can't query remote revision number, branch, or tags"))
4699 _("can't query remote revision number, branch, or tags"))
4697 if not rev and revs:
4700 if not rev and revs:
4698 rev = revs[0]
4701 rev = revs[0]
4699 if not rev:
4702 if not rev:
4700 rev = "tip"
4703 rev = "tip"
4701
4704
4702 remoterev = peer.lookup(rev)
4705 remoterev = peer.lookup(rev)
4703 if default or id:
4706 if default or id:
4704 output = [hexfunc(remoterev)]
4707 output = [hexfunc(remoterev)]
4705
4708
4706 def getbms():
4709 def getbms():
4707 bms = []
4710 bms = []
4708
4711
4709 if 'bookmarks' in peer.listkeys('namespaces'):
4712 if 'bookmarks' in peer.listkeys('namespaces'):
4710 hexremoterev = hex(remoterev)
4713 hexremoterev = hex(remoterev)
4711 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
4714 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
4712 if bmr == hexremoterev]
4715 if bmr == hexremoterev]
4713
4716
4714 return sorted(bms)
4717 return sorted(bms)
4715
4718
4716 if bookmarks:
4719 if bookmarks:
4717 output.extend(getbms())
4720 output.extend(getbms())
4718 elif default and not ui.quiet:
4721 elif default and not ui.quiet:
4719 # multiple bookmarks for a single parent separated by '/'
4722 # multiple bookmarks for a single parent separated by '/'
4720 bm = '/'.join(getbms())
4723 bm = '/'.join(getbms())
4721 if bm:
4724 if bm:
4722 output.append(bm)
4725 output.append(bm)
4723 else:
4726 else:
4724 ctx = scmutil.revsingle(repo, rev, None)
4727 ctx = scmutil.revsingle(repo, rev, None)
4725
4728
4726 if ctx.rev() is None:
4729 if ctx.rev() is None:
4727 ctx = repo[None]
4730 ctx = repo[None]
4728 parents = ctx.parents()
4731 parents = ctx.parents()
4729 taglist = []
4732 taglist = []
4730 for p in parents:
4733 for p in parents:
4731 taglist.extend(p.tags())
4734 taglist.extend(p.tags())
4732
4735
4733 changed = ""
4736 changed = ""
4734 if default or id or num:
4737 if default or id or num:
4735 if (any(repo.status())
4738 if (any(repo.status())
4736 or any(ctx.sub(s).dirty() for s in ctx.substate)):
4739 or any(ctx.sub(s).dirty() for s in ctx.substate)):
4737 changed = '+'
4740 changed = '+'
4738 if default or id:
4741 if default or id:
4739 output = ["%s%s" %
4742 output = ["%s%s" %
4740 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4743 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4741 if num:
4744 if num:
4742 output.append("%s%s" %
4745 output.append("%s%s" %
4743 ('+'.join([str(p.rev()) for p in parents]), changed))
4746 ('+'.join([str(p.rev()) for p in parents]), changed))
4744 else:
4747 else:
4745 if default or id:
4748 if default or id:
4746 output = [hexfunc(ctx.node())]
4749 output = [hexfunc(ctx.node())]
4747 if num:
4750 if num:
4748 output.append(str(ctx.rev()))
4751 output.append(str(ctx.rev()))
4749 taglist = ctx.tags()
4752 taglist = ctx.tags()
4750
4753
4751 if default and not ui.quiet:
4754 if default and not ui.quiet:
4752 b = ctx.branch()
4755 b = ctx.branch()
4753 if b != 'default':
4756 if b != 'default':
4754 output.append("(%s)" % b)
4757 output.append("(%s)" % b)
4755
4758
4756 # multiple tags for a single parent separated by '/'
4759 # multiple tags for a single parent separated by '/'
4757 t = '/'.join(taglist)
4760 t = '/'.join(taglist)
4758 if t:
4761 if t:
4759 output.append(t)
4762 output.append(t)
4760
4763
4761 # multiple bookmarks for a single parent separated by '/'
4764 # multiple bookmarks for a single parent separated by '/'
4762 bm = '/'.join(ctx.bookmarks())
4765 bm = '/'.join(ctx.bookmarks())
4763 if bm:
4766 if bm:
4764 output.append(bm)
4767 output.append(bm)
4765 else:
4768 else:
4766 if branch:
4769 if branch:
4767 output.append(ctx.branch())
4770 output.append(ctx.branch())
4768
4771
4769 if tags:
4772 if tags:
4770 output.extend(taglist)
4773 output.extend(taglist)
4771
4774
4772 if bookmarks:
4775 if bookmarks:
4773 output.extend(ctx.bookmarks())
4776 output.extend(ctx.bookmarks())
4774
4777
4775 ui.write("%s\n" % ' '.join(output))
4778 ui.write("%s\n" % ' '.join(output))
4776
4779
4777 @command('import|patch',
4780 @command('import|patch',
4778 [('p', 'strip', 1,
4781 [('p', 'strip', 1,
4779 _('directory strip option for patch. This has the same '
4782 _('directory strip option for patch. This has the same '
4780 'meaning as the corresponding patch option'), _('NUM')),
4783 'meaning as the corresponding patch option'), _('NUM')),
4781 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4784 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4782 ('e', 'edit', False, _('invoke editor on commit messages')),
4785 ('e', 'edit', False, _('invoke editor on commit messages')),
4783 ('f', 'force', None,
4786 ('f', 'force', None,
4784 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4787 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4785 ('', 'no-commit', None,
4788 ('', 'no-commit', None,
4786 _("don't commit, just update the working directory")),
4789 _("don't commit, just update the working directory")),
4787 ('', 'bypass', None,
4790 ('', 'bypass', None,
4788 _("apply patch without touching the working directory")),
4791 _("apply patch without touching the working directory")),
4789 ('', 'partial', None,
4792 ('', 'partial', None,
4790 _('commit even if some hunks fail')),
4793 _('commit even if some hunks fail')),
4791 ('', 'exact', None,
4794 ('', 'exact', None,
4792 _('abort if patch would apply lossily')),
4795 _('abort if patch would apply lossily')),
4793 ('', 'prefix', '',
4796 ('', 'prefix', '',
4794 _('apply patch to subdirectory'), _('DIR')),
4797 _('apply patch to subdirectory'), _('DIR')),
4795 ('', 'import-branch', None,
4798 ('', 'import-branch', None,
4796 _('use any branch information in patch (implied by --exact)'))] +
4799 _('use any branch information in patch (implied by --exact)'))] +
4797 commitopts + commitopts2 + similarityopts,
4800 commitopts + commitopts2 + similarityopts,
4798 _('[OPTION]... PATCH...'))
4801 _('[OPTION]... PATCH...'))
4799 def import_(ui, repo, patch1=None, *patches, **opts):
4802 def import_(ui, repo, patch1=None, *patches, **opts):
4800 """import an ordered set of patches
4803 """import an ordered set of patches
4801
4804
4802 Import a list of patches and commit them individually (unless
4805 Import a list of patches and commit them individually (unless
4803 --no-commit is specified).
4806 --no-commit is specified).
4804
4807
4805 To read a patch from standard input, use "-" as the patch name. If
4808 To read a patch from standard input, use "-" as the patch name. If
4806 a URL is specified, the patch will be downloaded from there.
4809 a URL is specified, the patch will be downloaded from there.
4807
4810
4808 Import first applies changes to the working directory (unless
4811 Import first applies changes to the working directory (unless
4809 --bypass is specified), import will abort if there are outstanding
4812 --bypass is specified), import will abort if there are outstanding
4810 changes.
4813 changes.
4811
4814
4812 Use --bypass to apply and commit patches directly to the
4815 Use --bypass to apply and commit patches directly to the
4813 repository, without affecting the working directory. Without
4816 repository, without affecting the working directory. Without
4814 --exact, patches will be applied on top of the working directory
4817 --exact, patches will be applied on top of the working directory
4815 parent revision.
4818 parent revision.
4816
4819
4817 You can import a patch straight from a mail message. Even patches
4820 You can import a patch straight from a mail message. Even patches
4818 as attachments work (to use the body part, it must have type
4821 as attachments work (to use the body part, it must have type
4819 text/plain or text/x-patch). From and Subject headers of email
4822 text/plain or text/x-patch). From and Subject headers of email
4820 message are used as default committer and commit message. All
4823 message are used as default committer and commit message. All
4821 text/plain body parts before first diff are added to the commit
4824 text/plain body parts before first diff are added to the commit
4822 message.
4825 message.
4823
4826
4824 If the imported patch was generated by :hg:`export`, user and
4827 If the imported patch was generated by :hg:`export`, user and
4825 description from patch override values from message headers and
4828 description from patch override values from message headers and
4826 body. Values given on command line with -m/--message and -u/--user
4829 body. Values given on command line with -m/--message and -u/--user
4827 override these.
4830 override these.
4828
4831
4829 If --exact is specified, import will set the working directory to
4832 If --exact is specified, import will set the working directory to
4830 the parent of each patch before applying it, and will abort if the
4833 the parent of each patch before applying it, and will abort if the
4831 resulting changeset has a different ID than the one recorded in
4834 resulting changeset has a different ID than the one recorded in
4832 the patch. This will guard against various ways that portable
4835 the patch. This will guard against various ways that portable
4833 patch formats and mail systems might fail to transfer Mercurial
4836 patch formats and mail systems might fail to transfer Mercurial
4834 data or metadata. See :hg:`bundle` for lossless transmission.
4837 data or metadata. See :hg:`bundle` for lossless transmission.
4835
4838
4836 Use --partial to ensure a changeset will be created from the patch
4839 Use --partial to ensure a changeset will be created from the patch
4837 even if some hunks fail to apply. Hunks that fail to apply will be
4840 even if some hunks fail to apply. Hunks that fail to apply will be
4838 written to a <target-file>.rej file. Conflicts can then be resolved
4841 written to a <target-file>.rej file. Conflicts can then be resolved
4839 by hand before :hg:`commit --amend` is run to update the created
4842 by hand before :hg:`commit --amend` is run to update the created
4840 changeset. This flag exists to let people import patches that
4843 changeset. This flag exists to let people import patches that
4841 partially apply without losing the associated metadata (author,
4844 partially apply without losing the associated metadata (author,
4842 date, description, ...).
4845 date, description, ...).
4843
4846
4844 .. note::
4847 .. note::
4845
4848
4846 When no hunks apply cleanly, :hg:`import --partial` will create
4849 When no hunks apply cleanly, :hg:`import --partial` will create
4847 an empty changeset, importing only the patch metadata.
4850 an empty changeset, importing only the patch metadata.
4848
4851
4849 With -s/--similarity, hg will attempt to discover renames and
4852 With -s/--similarity, hg will attempt to discover renames and
4850 copies in the patch in the same way as :hg:`addremove`.
4853 copies in the patch in the same way as :hg:`addremove`.
4851
4854
4852 It is possible to use external patch programs to perform the patch
4855 It is possible to use external patch programs to perform the patch
4853 by setting the ``ui.patch`` configuration option. For the default
4856 by setting the ``ui.patch`` configuration option. For the default
4854 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4857 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4855 See :hg:`help config` for more information about configuration
4858 See :hg:`help config` for more information about configuration
4856 files and how to use these options.
4859 files and how to use these options.
4857
4860
4858 See :hg:`help dates` for a list of formats valid for -d/--date.
4861 See :hg:`help dates` for a list of formats valid for -d/--date.
4859
4862
4860 .. container:: verbose
4863 .. container:: verbose
4861
4864
4862 Examples:
4865 Examples:
4863
4866
4864 - import a traditional patch from a website and detect renames::
4867 - import a traditional patch from a website and detect renames::
4865
4868
4866 hg import -s 80 http://example.com/bugfix.patch
4869 hg import -s 80 http://example.com/bugfix.patch
4867
4870
4868 - import a changeset from an hgweb server::
4871 - import a changeset from an hgweb server::
4869
4872
4870 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
4873 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
4871
4874
4872 - import all the patches in an Unix-style mbox::
4875 - import all the patches in an Unix-style mbox::
4873
4876
4874 hg import incoming-patches.mbox
4877 hg import incoming-patches.mbox
4875
4878
4876 - attempt to exactly restore an exported changeset (not always
4879 - attempt to exactly restore an exported changeset (not always
4877 possible)::
4880 possible)::
4878
4881
4879 hg import --exact proposed-fix.patch
4882 hg import --exact proposed-fix.patch
4880
4883
4881 - use an external tool to apply a patch which is too fuzzy for
4884 - use an external tool to apply a patch which is too fuzzy for
4882 the default internal tool.
4885 the default internal tool.
4883
4886
4884 hg import --config ui.patch="patch --merge" fuzzy.patch
4887 hg import --config ui.patch="patch --merge" fuzzy.patch
4885
4888
4886 - change the default fuzzing from 2 to a less strict 7
4889 - change the default fuzzing from 2 to a less strict 7
4887
4890
4888 hg import --config ui.fuzz=7 fuzz.patch
4891 hg import --config ui.fuzz=7 fuzz.patch
4889
4892
4890 Returns 0 on success, 1 on partial success (see --partial).
4893 Returns 0 on success, 1 on partial success (see --partial).
4891 """
4894 """
4892
4895
4893 if not patch1:
4896 if not patch1:
4894 raise error.Abort(_('need at least one patch to import'))
4897 raise error.Abort(_('need at least one patch to import'))
4895
4898
4896 patches = (patch1,) + patches
4899 patches = (patch1,) + patches
4897
4900
4898 date = opts.get('date')
4901 date = opts.get('date')
4899 if date:
4902 if date:
4900 opts['date'] = util.parsedate(date)
4903 opts['date'] = util.parsedate(date)
4901
4904
4902 exact = opts.get('exact')
4905 exact = opts.get('exact')
4903 update = not opts.get('bypass')
4906 update = not opts.get('bypass')
4904 if not update and opts.get('no_commit'):
4907 if not update and opts.get('no_commit'):
4905 raise error.Abort(_('cannot use --no-commit with --bypass'))
4908 raise error.Abort(_('cannot use --no-commit with --bypass'))
4906 try:
4909 try:
4907 sim = float(opts.get('similarity') or 0)
4910 sim = float(opts.get('similarity') or 0)
4908 except ValueError:
4911 except ValueError:
4909 raise error.Abort(_('similarity must be a number'))
4912 raise error.Abort(_('similarity must be a number'))
4910 if sim < 0 or sim > 100:
4913 if sim < 0 or sim > 100:
4911 raise error.Abort(_('similarity must be between 0 and 100'))
4914 raise error.Abort(_('similarity must be between 0 and 100'))
4912 if sim and not update:
4915 if sim and not update:
4913 raise error.Abort(_('cannot use --similarity with --bypass'))
4916 raise error.Abort(_('cannot use --similarity with --bypass'))
4914 if exact:
4917 if exact:
4915 if opts.get('edit'):
4918 if opts.get('edit'):
4916 raise error.Abort(_('cannot use --exact with --edit'))
4919 raise error.Abort(_('cannot use --exact with --edit'))
4917 if opts.get('prefix'):
4920 if opts.get('prefix'):
4918 raise error.Abort(_('cannot use --exact with --prefix'))
4921 raise error.Abort(_('cannot use --exact with --prefix'))
4919
4922
4920 base = opts["base"]
4923 base = opts["base"]
4921 wlock = dsguard = lock = tr = None
4924 wlock = dsguard = lock = tr = None
4922 msgs = []
4925 msgs = []
4923 ret = 0
4926 ret = 0
4924
4927
4925
4928
4926 try:
4929 try:
4927 wlock = repo.wlock()
4930 wlock = repo.wlock()
4928
4931
4929 if update:
4932 if update:
4930 cmdutil.checkunfinished(repo)
4933 cmdutil.checkunfinished(repo)
4931 if (exact or not opts.get('force')):
4934 if (exact or not opts.get('force')):
4932 cmdutil.bailifchanged(repo)
4935 cmdutil.bailifchanged(repo)
4933
4936
4934 if not opts.get('no_commit'):
4937 if not opts.get('no_commit'):
4935 lock = repo.lock()
4938 lock = repo.lock()
4936 tr = repo.transaction('import')
4939 tr = repo.transaction('import')
4937 else:
4940 else:
4938 dsguard = cmdutil.dirstateguard(repo, 'import')
4941 dsguard = cmdutil.dirstateguard(repo, 'import')
4939 parents = repo[None].parents()
4942 parents = repo[None].parents()
4940 for patchurl in patches:
4943 for patchurl in patches:
4941 if patchurl == '-':
4944 if patchurl == '-':
4942 ui.status(_('applying patch from stdin\n'))
4945 ui.status(_('applying patch from stdin\n'))
4943 patchfile = ui.fin
4946 patchfile = ui.fin
4944 patchurl = 'stdin' # for error message
4947 patchurl = 'stdin' # for error message
4945 else:
4948 else:
4946 patchurl = os.path.join(base, patchurl)
4949 patchurl = os.path.join(base, patchurl)
4947 ui.status(_('applying %s\n') % patchurl)
4950 ui.status(_('applying %s\n') % patchurl)
4948 patchfile = hg.openpath(ui, patchurl)
4951 patchfile = hg.openpath(ui, patchurl)
4949
4952
4950 haspatch = False
4953 haspatch = False
4951 for hunk in patch.split(patchfile):
4954 for hunk in patch.split(patchfile):
4952 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4955 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4953 parents, opts,
4956 parents, opts,
4954 msgs, hg.clean)
4957 msgs, hg.clean)
4955 if msg:
4958 if msg:
4956 haspatch = True
4959 haspatch = True
4957 ui.note(msg + '\n')
4960 ui.note(msg + '\n')
4958 if update or exact:
4961 if update or exact:
4959 parents = repo[None].parents()
4962 parents = repo[None].parents()
4960 else:
4963 else:
4961 parents = [repo[node]]
4964 parents = [repo[node]]
4962 if rej:
4965 if rej:
4963 ui.write_err(_("patch applied partially\n"))
4966 ui.write_err(_("patch applied partially\n"))
4964 ui.write_err(_("(fix the .rej files and run "
4967 ui.write_err(_("(fix the .rej files and run "
4965 "`hg commit --amend`)\n"))
4968 "`hg commit --amend`)\n"))
4966 ret = 1
4969 ret = 1
4967 break
4970 break
4968
4971
4969 if not haspatch:
4972 if not haspatch:
4970 raise error.Abort(_('%s: no diffs found') % patchurl)
4973 raise error.Abort(_('%s: no diffs found') % patchurl)
4971
4974
4972 if tr:
4975 if tr:
4973 tr.close()
4976 tr.close()
4974 if msgs:
4977 if msgs:
4975 repo.savecommitmessage('\n* * *\n'.join(msgs))
4978 repo.savecommitmessage('\n* * *\n'.join(msgs))
4976 if dsguard:
4979 if dsguard:
4977 dsguard.close()
4980 dsguard.close()
4978 return ret
4981 return ret
4979 finally:
4982 finally:
4980 if tr:
4983 if tr:
4981 tr.release()
4984 tr.release()
4982 release(lock, dsguard, wlock)
4985 release(lock, dsguard, wlock)
4983
4986
4984 @command('incoming|in',
4987 @command('incoming|in',
4985 [('f', 'force', None,
4988 [('f', 'force', None,
4986 _('run even if remote repository is unrelated')),
4989 _('run even if remote repository is unrelated')),
4987 ('n', 'newest-first', None, _('show newest record first')),
4990 ('n', 'newest-first', None, _('show newest record first')),
4988 ('', 'bundle', '',
4991 ('', 'bundle', '',
4989 _('file to store the bundles into'), _('FILE')),
4992 _('file to store the bundles into'), _('FILE')),
4990 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4993 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4991 ('B', 'bookmarks', False, _("compare bookmarks")),
4994 ('B', 'bookmarks', False, _("compare bookmarks")),
4992 ('b', 'branch', [],
4995 ('b', 'branch', [],
4993 _('a specific branch you would like to pull'), _('BRANCH')),
4996 _('a specific branch you would like to pull'), _('BRANCH')),
4994 ] + logopts + remoteopts + subrepoopts,
4997 ] + logopts + remoteopts + subrepoopts,
4995 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4998 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4996 def incoming(ui, repo, source="default", **opts):
4999 def incoming(ui, repo, source="default", **opts):
4997 """show new changesets found in source
5000 """show new changesets found in source
4998
5001
4999 Show new changesets found in the specified path/URL or the default
5002 Show new changesets found in the specified path/URL or the default
5000 pull location. These are the changesets that would have been pulled
5003 pull location. These are the changesets that would have been pulled
5001 if a pull at the time you issued this command.
5004 if a pull at the time you issued this command.
5002
5005
5003 See pull for valid source format details.
5006 See pull for valid source format details.
5004
5007
5005 .. container:: verbose
5008 .. container:: verbose
5006
5009
5007 With -B/--bookmarks, the result of bookmark comparison between
5010 With -B/--bookmarks, the result of bookmark comparison between
5008 local and remote repositories is displayed. With -v/--verbose,
5011 local and remote repositories is displayed. With -v/--verbose,
5009 status is also displayed for each bookmark like below::
5012 status is also displayed for each bookmark like below::
5010
5013
5011 BM1 01234567890a added
5014 BM1 01234567890a added
5012 BM2 1234567890ab advanced
5015 BM2 1234567890ab advanced
5013 BM3 234567890abc diverged
5016 BM3 234567890abc diverged
5014 BM4 34567890abcd changed
5017 BM4 34567890abcd changed
5015
5018
5016 The action taken locally when pulling depends on the
5019 The action taken locally when pulling depends on the
5017 status of each bookmark:
5020 status of each bookmark:
5018
5021
5019 :``added``: pull will create it
5022 :``added``: pull will create it
5020 :``advanced``: pull will update it
5023 :``advanced``: pull will update it
5021 :``diverged``: pull will create a divergent bookmark
5024 :``diverged``: pull will create a divergent bookmark
5022 :``changed``: result depends on remote changesets
5025 :``changed``: result depends on remote changesets
5023
5026
5024 From the point of view of pulling behavior, bookmark
5027 From the point of view of pulling behavior, bookmark
5025 existing only in the remote repository are treated as ``added``,
5028 existing only in the remote repository are treated as ``added``,
5026 even if it is in fact locally deleted.
5029 even if it is in fact locally deleted.
5027
5030
5028 .. container:: verbose
5031 .. container:: verbose
5029
5032
5030 For remote repository, using --bundle avoids downloading the
5033 For remote repository, using --bundle avoids downloading the
5031 changesets twice if the incoming is followed by a pull.
5034 changesets twice if the incoming is followed by a pull.
5032
5035
5033 Examples:
5036 Examples:
5034
5037
5035 - show incoming changes with patches and full description::
5038 - show incoming changes with patches and full description::
5036
5039
5037 hg incoming -vp
5040 hg incoming -vp
5038
5041
5039 - show incoming changes excluding merges, store a bundle::
5042 - show incoming changes excluding merges, store a bundle::
5040
5043
5041 hg in -vpM --bundle incoming.hg
5044 hg in -vpM --bundle incoming.hg
5042 hg pull incoming.hg
5045 hg pull incoming.hg
5043
5046
5044 - briefly list changes inside a bundle::
5047 - briefly list changes inside a bundle::
5045
5048
5046 hg in changes.hg -T "{desc|firstline}\\n"
5049 hg in changes.hg -T "{desc|firstline}\\n"
5047
5050
5048 Returns 0 if there are incoming changes, 1 otherwise.
5051 Returns 0 if there are incoming changes, 1 otherwise.
5049 """
5052 """
5050 if opts.get('graph'):
5053 if opts.get('graph'):
5051 cmdutil.checkunsupportedgraphflags([], opts)
5054 cmdutil.checkunsupportedgraphflags([], opts)
5052 def display(other, chlist, displayer):
5055 def display(other, chlist, displayer):
5053 revdag = cmdutil.graphrevs(other, chlist, opts)
5056 revdag = cmdutil.graphrevs(other, chlist, opts)
5054 cmdutil.displaygraph(ui, repo, revdag, displayer,
5057 cmdutil.displaygraph(ui, repo, revdag, displayer,
5055 graphmod.asciiedges)
5058 graphmod.asciiedges)
5056
5059
5057 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
5060 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
5058 return 0
5061 return 0
5059
5062
5060 if opts.get('bundle') and opts.get('subrepos'):
5063 if opts.get('bundle') and opts.get('subrepos'):
5061 raise error.Abort(_('cannot combine --bundle and --subrepos'))
5064 raise error.Abort(_('cannot combine --bundle and --subrepos'))
5062
5065
5063 if opts.get('bookmarks'):
5066 if opts.get('bookmarks'):
5064 source, branches = hg.parseurl(ui.expandpath(source),
5067 source, branches = hg.parseurl(ui.expandpath(source),
5065 opts.get('branch'))
5068 opts.get('branch'))
5066 other = hg.peer(repo, opts, source)
5069 other = hg.peer(repo, opts, source)
5067 if 'bookmarks' not in other.listkeys('namespaces'):
5070 if 'bookmarks' not in other.listkeys('namespaces'):
5068 ui.warn(_("remote doesn't support bookmarks\n"))
5071 ui.warn(_("remote doesn't support bookmarks\n"))
5069 return 0
5072 return 0
5070 ui.status(_('comparing with %s\n') % util.hidepassword(source))
5073 ui.status(_('comparing with %s\n') % util.hidepassword(source))
5071 return bookmarks.incoming(ui, repo, other)
5074 return bookmarks.incoming(ui, repo, other)
5072
5075
5073 repo._subtoppath = ui.expandpath(source)
5076 repo._subtoppath = ui.expandpath(source)
5074 try:
5077 try:
5075 return hg.incoming(ui, repo, source, opts)
5078 return hg.incoming(ui, repo, source, opts)
5076 finally:
5079 finally:
5077 del repo._subtoppath
5080 del repo._subtoppath
5078
5081
5079
5082
5080 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
5083 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
5081 norepo=True)
5084 norepo=True)
5082 def init(ui, dest=".", **opts):
5085 def init(ui, dest=".", **opts):
5083 """create a new repository in the given directory
5086 """create a new repository in the given directory
5084
5087
5085 Initialize a new repository in the given directory. If the given
5088 Initialize a new repository in the given directory. If the given
5086 directory does not exist, it will be created.
5089 directory does not exist, it will be created.
5087
5090
5088 If no directory is given, the current directory is used.
5091 If no directory is given, the current directory is used.
5089
5092
5090 It is possible to specify an ``ssh://`` URL as the destination.
5093 It is possible to specify an ``ssh://`` URL as the destination.
5091 See :hg:`help urls` for more information.
5094 See :hg:`help urls` for more information.
5092
5095
5093 Returns 0 on success.
5096 Returns 0 on success.
5094 """
5097 """
5095 hg.peer(ui, opts, ui.expandpath(dest), create=True)
5098 hg.peer(ui, opts, ui.expandpath(dest), create=True)
5096
5099
5097 @command('locate',
5100 @command('locate',
5098 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
5101 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
5099 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5102 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5100 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
5103 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
5101 ] + walkopts,
5104 ] + walkopts,
5102 _('[OPTION]... [PATTERN]...'))
5105 _('[OPTION]... [PATTERN]...'))
5103 def locate(ui, repo, *pats, **opts):
5106 def locate(ui, repo, *pats, **opts):
5104 """locate files matching specific patterns (DEPRECATED)
5107 """locate files matching specific patterns (DEPRECATED)
5105
5108
5106 Print files under Mercurial control in the working directory whose
5109 Print files under Mercurial control in the working directory whose
5107 names match the given patterns.
5110 names match the given patterns.
5108
5111
5109 By default, this command searches all directories in the working
5112 By default, this command searches all directories in the working
5110 directory. To search just the current directory and its
5113 directory. To search just the current directory and its
5111 subdirectories, use "--include .".
5114 subdirectories, use "--include .".
5112
5115
5113 If no patterns are given to match, this command prints the names
5116 If no patterns are given to match, this command prints the names
5114 of all files under Mercurial control in the working directory.
5117 of all files under Mercurial control in the working directory.
5115
5118
5116 If you want to feed the output of this command into the "xargs"
5119 If you want to feed the output of this command into the "xargs"
5117 command, use the -0 option to both this command and "xargs". This
5120 command, use the -0 option to both this command and "xargs". This
5118 will avoid the problem of "xargs" treating single filenames that
5121 will avoid the problem of "xargs" treating single filenames that
5119 contain whitespace as multiple filenames.
5122 contain whitespace as multiple filenames.
5120
5123
5121 See :hg:`help files` for a more versatile command.
5124 See :hg:`help files` for a more versatile command.
5122
5125
5123 Returns 0 if a match is found, 1 otherwise.
5126 Returns 0 if a match is found, 1 otherwise.
5124 """
5127 """
5125 if opts.get('print0'):
5128 if opts.get('print0'):
5126 end = '\0'
5129 end = '\0'
5127 else:
5130 else:
5128 end = '\n'
5131 end = '\n'
5129 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
5132 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
5130
5133
5131 ret = 1
5134 ret = 1
5132 ctx = repo[rev]
5135 ctx = repo[rev]
5133 m = scmutil.match(ctx, pats, opts, default='relglob',
5136 m = scmutil.match(ctx, pats, opts, default='relglob',
5134 badfn=lambda x, y: False)
5137 badfn=lambda x, y: False)
5135
5138
5136 for abs in ctx.matches(m):
5139 for abs in ctx.matches(m):
5137 if opts.get('fullpath'):
5140 if opts.get('fullpath'):
5138 ui.write(repo.wjoin(abs), end)
5141 ui.write(repo.wjoin(abs), end)
5139 else:
5142 else:
5140 ui.write(((pats and m.rel(abs)) or abs), end)
5143 ui.write(((pats and m.rel(abs)) or abs), end)
5141 ret = 0
5144 ret = 0
5142
5145
5143 return ret
5146 return ret
5144
5147
5145 @command('^log|history',
5148 @command('^log|history',
5146 [('f', 'follow', None,
5149 [('f', 'follow', None,
5147 _('follow changeset history, or file history across copies and renames')),
5150 _('follow changeset history, or file history across copies and renames')),
5148 ('', 'follow-first', None,
5151 ('', 'follow-first', None,
5149 _('only follow the first parent of merge changesets (DEPRECATED)')),
5152 _('only follow the first parent of merge changesets (DEPRECATED)')),
5150 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
5153 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
5151 ('C', 'copies', None, _('show copied files')),
5154 ('C', 'copies', None, _('show copied files')),
5152 ('k', 'keyword', [],
5155 ('k', 'keyword', [],
5153 _('do case-insensitive search for a given text'), _('TEXT')),
5156 _('do case-insensitive search for a given text'), _('TEXT')),
5154 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
5157 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
5155 ('', 'removed', None, _('include revisions where files were removed')),
5158 ('', 'removed', None, _('include revisions where files were removed')),
5156 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
5159 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
5157 ('u', 'user', [], _('revisions committed by user'), _('USER')),
5160 ('u', 'user', [], _('revisions committed by user'), _('USER')),
5158 ('', 'only-branch', [],
5161 ('', 'only-branch', [],
5159 _('show only changesets within the given named branch (DEPRECATED)'),
5162 _('show only changesets within the given named branch (DEPRECATED)'),
5160 _('BRANCH')),
5163 _('BRANCH')),
5161 ('b', 'branch', [],
5164 ('b', 'branch', [],
5162 _('show changesets within the given named branch'), _('BRANCH')),
5165 _('show changesets within the given named branch'), _('BRANCH')),
5163 ('P', 'prune', [],
5166 ('P', 'prune', [],
5164 _('do not display revision or any of its ancestors'), _('REV')),
5167 _('do not display revision or any of its ancestors'), _('REV')),
5165 ] + logopts + walkopts,
5168 ] + logopts + walkopts,
5166 _('[OPTION]... [FILE]'),
5169 _('[OPTION]... [FILE]'),
5167 inferrepo=True)
5170 inferrepo=True)
5168 def log(ui, repo, *pats, **opts):
5171 def log(ui, repo, *pats, **opts):
5169 """show revision history of entire repository or files
5172 """show revision history of entire repository or files
5170
5173
5171 Print the revision history of the specified files or the entire
5174 Print the revision history of the specified files or the entire
5172 project.
5175 project.
5173
5176
5174 If no revision range is specified, the default is ``tip:0`` unless
5177 If no revision range is specified, the default is ``tip:0`` unless
5175 --follow is set, in which case the working directory parent is
5178 --follow is set, in which case the working directory parent is
5176 used as the starting revision.
5179 used as the starting revision.
5177
5180
5178 File history is shown without following rename or copy history of
5181 File history is shown without following rename or copy history of
5179 files. Use -f/--follow with a filename to follow history across
5182 files. Use -f/--follow with a filename to follow history across
5180 renames and copies. --follow without a filename will only show
5183 renames and copies. --follow without a filename will only show
5181 ancestors or descendants of the starting revision.
5184 ancestors or descendants of the starting revision.
5182
5185
5183 By default this command prints revision number and changeset id,
5186 By default this command prints revision number and changeset id,
5184 tags, non-trivial parents, user, date and time, and a summary for
5187 tags, non-trivial parents, user, date and time, and a summary for
5185 each commit. When the -v/--verbose switch is used, the list of
5188 each commit. When the -v/--verbose switch is used, the list of
5186 changed files and full commit message are shown.
5189 changed files and full commit message are shown.
5187
5190
5188 With --graph the revisions are shown as an ASCII art DAG with the most
5191 With --graph the revisions are shown as an ASCII art DAG with the most
5189 recent changeset at the top.
5192 recent changeset at the top.
5190 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
5193 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
5191 and '+' represents a fork where the changeset from the lines below is a
5194 and '+' represents a fork where the changeset from the lines below is a
5192 parent of the 'o' merge on the same line.
5195 parent of the 'o' merge on the same line.
5193
5196
5194 .. note::
5197 .. note::
5195
5198
5196 :hg:`log --patch` may generate unexpected diff output for merge
5199 :hg:`log --patch` may generate unexpected diff output for merge
5197 changesets, as it will only compare the merge changeset against
5200 changesets, as it will only compare the merge changeset against
5198 its first parent. Also, only files different from BOTH parents
5201 its first parent. Also, only files different from BOTH parents
5199 will appear in files:.
5202 will appear in files:.
5200
5203
5201 .. note::
5204 .. note::
5202
5205
5203 For performance reasons, :hg:`log FILE` may omit duplicate changes
5206 For performance reasons, :hg:`log FILE` may omit duplicate changes
5204 made on branches and will not show removals or mode changes. To
5207 made on branches and will not show removals or mode changes. To
5205 see all such changes, use the --removed switch.
5208 see all such changes, use the --removed switch.
5206
5209
5207 .. container:: verbose
5210 .. container:: verbose
5208
5211
5209 Some examples:
5212 Some examples:
5210
5213
5211 - changesets with full descriptions and file lists::
5214 - changesets with full descriptions and file lists::
5212
5215
5213 hg log -v
5216 hg log -v
5214
5217
5215 - changesets ancestral to the working directory::
5218 - changesets ancestral to the working directory::
5216
5219
5217 hg log -f
5220 hg log -f
5218
5221
5219 - last 10 commits on the current branch::
5222 - last 10 commits on the current branch::
5220
5223
5221 hg log -l 10 -b .
5224 hg log -l 10 -b .
5222
5225
5223 - changesets showing all modifications of a file, including removals::
5226 - changesets showing all modifications of a file, including removals::
5224
5227
5225 hg log --removed file.c
5228 hg log --removed file.c
5226
5229
5227 - all changesets that touch a directory, with diffs, excluding merges::
5230 - all changesets that touch a directory, with diffs, excluding merges::
5228
5231
5229 hg log -Mp lib/
5232 hg log -Mp lib/
5230
5233
5231 - all revision numbers that match a keyword::
5234 - all revision numbers that match a keyword::
5232
5235
5233 hg log -k bug --template "{rev}\\n"
5236 hg log -k bug --template "{rev}\\n"
5234
5237
5235 - the full hash identifier of the working directory parent::
5238 - the full hash identifier of the working directory parent::
5236
5239
5237 hg log -r . --template "{node}\\n"
5240 hg log -r . --template "{node}\\n"
5238
5241
5239 - list available log templates::
5242 - list available log templates::
5240
5243
5241 hg log -T list
5244 hg log -T list
5242
5245
5243 - check if a given changeset is included in a tagged release::
5246 - check if a given changeset is included in a tagged release::
5244
5247
5245 hg log -r "a21ccf and ancestor(1.9)"
5248 hg log -r "a21ccf and ancestor(1.9)"
5246
5249
5247 - find all changesets by some user in a date range::
5250 - find all changesets by some user in a date range::
5248
5251
5249 hg log -k alice -d "may 2008 to jul 2008"
5252 hg log -k alice -d "may 2008 to jul 2008"
5250
5253
5251 - summary of all changesets after the last tag::
5254 - summary of all changesets after the last tag::
5252
5255
5253 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
5256 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
5254
5257
5255 See :hg:`help dates` for a list of formats valid for -d/--date.
5258 See :hg:`help dates` for a list of formats valid for -d/--date.
5256
5259
5257 See :hg:`help revisions` and :hg:`help revsets` for more about
5260 See :hg:`help revisions` and :hg:`help revsets` for more about
5258 specifying and ordering revisions.
5261 specifying and ordering revisions.
5259
5262
5260 See :hg:`help templates` for more about pre-packaged styles and
5263 See :hg:`help templates` for more about pre-packaged styles and
5261 specifying custom templates.
5264 specifying custom templates.
5262
5265
5263 Returns 0 on success.
5266 Returns 0 on success.
5264
5267
5265 """
5268 """
5266 if opts.get('follow') and opts.get('rev'):
5269 if opts.get('follow') and opts.get('rev'):
5267 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
5270 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
5268 del opts['follow']
5271 del opts['follow']
5269
5272
5270 if opts.get('graph'):
5273 if opts.get('graph'):
5271 return cmdutil.graphlog(ui, repo, *pats, **opts)
5274 return cmdutil.graphlog(ui, repo, *pats, **opts)
5272
5275
5273 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
5276 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
5274 limit = cmdutil.loglimit(opts)
5277 limit = cmdutil.loglimit(opts)
5275 count = 0
5278 count = 0
5276
5279
5277 getrenamed = None
5280 getrenamed = None
5278 if opts.get('copies'):
5281 if opts.get('copies'):
5279 endrev = None
5282 endrev = None
5280 if opts.get('rev'):
5283 if opts.get('rev'):
5281 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
5284 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
5282 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
5285 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
5283
5286
5284 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
5287 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
5285 for rev in revs:
5288 for rev in revs:
5286 if count == limit:
5289 if count == limit:
5287 break
5290 break
5288 ctx = repo[rev]
5291 ctx = repo[rev]
5289 copies = None
5292 copies = None
5290 if getrenamed is not None and rev:
5293 if getrenamed is not None and rev:
5291 copies = []
5294 copies = []
5292 for fn in ctx.files():
5295 for fn in ctx.files():
5293 rename = getrenamed(fn, rev)
5296 rename = getrenamed(fn, rev)
5294 if rename:
5297 if rename:
5295 copies.append((fn, rename[0]))
5298 copies.append((fn, rename[0]))
5296 if filematcher:
5299 if filematcher:
5297 revmatchfn = filematcher(ctx.rev())
5300 revmatchfn = filematcher(ctx.rev())
5298 else:
5301 else:
5299 revmatchfn = None
5302 revmatchfn = None
5300 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
5303 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
5301 if displayer.flush(ctx):
5304 if displayer.flush(ctx):
5302 count += 1
5305 count += 1
5303
5306
5304 displayer.close()
5307 displayer.close()
5305
5308
5306 @command('manifest',
5309 @command('manifest',
5307 [('r', 'rev', '', _('revision to display'), _('REV')),
5310 [('r', 'rev', '', _('revision to display'), _('REV')),
5308 ('', 'all', False, _("list files from all revisions"))]
5311 ('', 'all', False, _("list files from all revisions"))]
5309 + formatteropts,
5312 + formatteropts,
5310 _('[-r REV]'))
5313 _('[-r REV]'))
5311 def manifest(ui, repo, node=None, rev=None, **opts):
5314 def manifest(ui, repo, node=None, rev=None, **opts):
5312 """output the current or given revision of the project manifest
5315 """output the current or given revision of the project manifest
5313
5316
5314 Print a list of version controlled files for the given revision.
5317 Print a list of version controlled files for the given revision.
5315 If no revision is given, the first parent of the working directory
5318 If no revision is given, the first parent of the working directory
5316 is used, or the null revision if no revision is checked out.
5319 is used, or the null revision if no revision is checked out.
5317
5320
5318 With -v, print file permissions, symlink and executable bits.
5321 With -v, print file permissions, symlink and executable bits.
5319 With --debug, print file revision hashes.
5322 With --debug, print file revision hashes.
5320
5323
5321 If option --all is specified, the list of all files from all revisions
5324 If option --all is specified, the list of all files from all revisions
5322 is printed. This includes deleted and renamed files.
5325 is printed. This includes deleted and renamed files.
5323
5326
5324 Returns 0 on success.
5327 Returns 0 on success.
5325 """
5328 """
5326
5329
5327 fm = ui.formatter('manifest', opts)
5330 fm = ui.formatter('manifest', opts)
5328
5331
5329 if opts.get('all'):
5332 if opts.get('all'):
5330 if rev or node:
5333 if rev or node:
5331 raise error.Abort(_("can't specify a revision with --all"))
5334 raise error.Abort(_("can't specify a revision with --all"))
5332
5335
5333 res = []
5336 res = []
5334 prefix = "data/"
5337 prefix = "data/"
5335 suffix = ".i"
5338 suffix = ".i"
5336 plen = len(prefix)
5339 plen = len(prefix)
5337 slen = len(suffix)
5340 slen = len(suffix)
5338 with repo.lock():
5341 with repo.lock():
5339 for fn, b, size in repo.store.datafiles():
5342 for fn, b, size in repo.store.datafiles():
5340 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
5343 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
5341 res.append(fn[plen:-slen])
5344 res.append(fn[plen:-slen])
5342 for f in res:
5345 for f in res:
5343 fm.startitem()
5346 fm.startitem()
5344 fm.write("path", '%s\n', f)
5347 fm.write("path", '%s\n', f)
5345 fm.end()
5348 fm.end()
5346 return
5349 return
5347
5350
5348 if rev and node:
5351 if rev and node:
5349 raise error.Abort(_("please specify just one revision"))
5352 raise error.Abort(_("please specify just one revision"))
5350
5353
5351 if not node:
5354 if not node:
5352 node = rev
5355 node = rev
5353
5356
5354 char = {'l': '@', 'x': '*', '': ''}
5357 char = {'l': '@', 'x': '*', '': ''}
5355 mode = {'l': '644', 'x': '755', '': '644'}
5358 mode = {'l': '644', 'x': '755', '': '644'}
5356 ctx = scmutil.revsingle(repo, node)
5359 ctx = scmutil.revsingle(repo, node)
5357 mf = ctx.manifest()
5360 mf = ctx.manifest()
5358 for f in ctx:
5361 for f in ctx:
5359 fm.startitem()
5362 fm.startitem()
5360 fl = ctx[f].flags()
5363 fl = ctx[f].flags()
5361 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
5364 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
5362 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
5365 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
5363 fm.write('path', '%s\n', f)
5366 fm.write('path', '%s\n', f)
5364 fm.end()
5367 fm.end()
5365
5368
5366 @command('^merge',
5369 @command('^merge',
5367 [('f', 'force', None,
5370 [('f', 'force', None,
5368 _('force a merge including outstanding changes (DEPRECATED)')),
5371 _('force a merge including outstanding changes (DEPRECATED)')),
5369 ('r', 'rev', '', _('revision to merge'), _('REV')),
5372 ('r', 'rev', '', _('revision to merge'), _('REV')),
5370 ('P', 'preview', None,
5373 ('P', 'preview', None,
5371 _('review revisions to merge (no merge is performed)'))
5374 _('review revisions to merge (no merge is performed)'))
5372 ] + mergetoolopts,
5375 ] + mergetoolopts,
5373 _('[-P] [[-r] REV]'))
5376 _('[-P] [[-r] REV]'))
5374 def merge(ui, repo, node=None, **opts):
5377 def merge(ui, repo, node=None, **opts):
5375 """merge another revision into working directory
5378 """merge another revision into working directory
5376
5379
5377 The current working directory is updated with all changes made in
5380 The current working directory is updated with all changes made in
5378 the requested revision since the last common predecessor revision.
5381 the requested revision since the last common predecessor revision.
5379
5382
5380 Files that changed between either parent are marked as changed for
5383 Files that changed between either parent are marked as changed for
5381 the next commit and a commit must be performed before any further
5384 the next commit and a commit must be performed before any further
5382 updates to the repository are allowed. The next commit will have
5385 updates to the repository are allowed. The next commit will have
5383 two parents.
5386 two parents.
5384
5387
5385 ``--tool`` can be used to specify the merge tool used for file
5388 ``--tool`` can be used to specify the merge tool used for file
5386 merges. It overrides the HGMERGE environment variable and your
5389 merges. It overrides the HGMERGE environment variable and your
5387 configuration files. See :hg:`help merge-tools` for options.
5390 configuration files. See :hg:`help merge-tools` for options.
5388
5391
5389 If no revision is specified, the working directory's parent is a
5392 If no revision is specified, the working directory's parent is a
5390 head revision, and the current branch contains exactly one other
5393 head revision, and the current branch contains exactly one other
5391 head, the other head is merged with by default. Otherwise, an
5394 head, the other head is merged with by default. Otherwise, an
5392 explicit revision with which to merge with must be provided.
5395 explicit revision with which to merge with must be provided.
5393
5396
5394 See :hg:`help resolve` for information on handling file conflicts.
5397 See :hg:`help resolve` for information on handling file conflicts.
5395
5398
5396 To undo an uncommitted merge, use :hg:`update --clean .` which
5399 To undo an uncommitted merge, use :hg:`update --clean .` which
5397 will check out a clean copy of the original merge parent, losing
5400 will check out a clean copy of the original merge parent, losing
5398 all changes.
5401 all changes.
5399
5402
5400 Returns 0 on success, 1 if there are unresolved files.
5403 Returns 0 on success, 1 if there are unresolved files.
5401 """
5404 """
5402
5405
5403 if opts.get('rev') and node:
5406 if opts.get('rev') and node:
5404 raise error.Abort(_("please specify just one revision"))
5407 raise error.Abort(_("please specify just one revision"))
5405 if not node:
5408 if not node:
5406 node = opts.get('rev')
5409 node = opts.get('rev')
5407
5410
5408 if node:
5411 if node:
5409 node = scmutil.revsingle(repo, node).node()
5412 node = scmutil.revsingle(repo, node).node()
5410
5413
5411 if not node:
5414 if not node:
5412 node = repo[destutil.destmerge(repo)].node()
5415 node = repo[destutil.destmerge(repo)].node()
5413
5416
5414 if opts.get('preview'):
5417 if opts.get('preview'):
5415 # find nodes that are ancestors of p2 but not of p1
5418 # find nodes that are ancestors of p2 but not of p1
5416 p1 = repo.lookup('.')
5419 p1 = repo.lookup('.')
5417 p2 = repo.lookup(node)
5420 p2 = repo.lookup(node)
5418 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
5421 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
5419
5422
5420 displayer = cmdutil.show_changeset(ui, repo, opts)
5423 displayer = cmdutil.show_changeset(ui, repo, opts)
5421 for node in nodes:
5424 for node in nodes:
5422 displayer.show(repo[node])
5425 displayer.show(repo[node])
5423 displayer.close()
5426 displayer.close()
5424 return 0
5427 return 0
5425
5428
5426 try:
5429 try:
5427 # ui.forcemerge is an internal variable, do not document
5430 # ui.forcemerge is an internal variable, do not document
5428 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
5431 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
5429 force = opts.get('force')
5432 force = opts.get('force')
5430 return hg.merge(repo, node, force=force, mergeforce=force)
5433 return hg.merge(repo, node, force=force, mergeforce=force)
5431 finally:
5434 finally:
5432 ui.setconfig('ui', 'forcemerge', '', 'merge')
5435 ui.setconfig('ui', 'forcemerge', '', 'merge')
5433
5436
5434 @command('outgoing|out',
5437 @command('outgoing|out',
5435 [('f', 'force', None, _('run even when the destination is unrelated')),
5438 [('f', 'force', None, _('run even when the destination is unrelated')),
5436 ('r', 'rev', [],
5439 ('r', 'rev', [],
5437 _('a changeset intended to be included in the destination'), _('REV')),
5440 _('a changeset intended to be included in the destination'), _('REV')),
5438 ('n', 'newest-first', None, _('show newest record first')),
5441 ('n', 'newest-first', None, _('show newest record first')),
5439 ('B', 'bookmarks', False, _('compare bookmarks')),
5442 ('B', 'bookmarks', False, _('compare bookmarks')),
5440 ('b', 'branch', [], _('a specific branch you would like to push'),
5443 ('b', 'branch', [], _('a specific branch you would like to push'),
5441 _('BRANCH')),
5444 _('BRANCH')),
5442 ] + logopts + remoteopts + subrepoopts,
5445 ] + logopts + remoteopts + subrepoopts,
5443 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
5446 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
5444 def outgoing(ui, repo, dest=None, **opts):
5447 def outgoing(ui, repo, dest=None, **opts):
5445 """show changesets not found in the destination
5448 """show changesets not found in the destination
5446
5449
5447 Show changesets not found in the specified destination repository
5450 Show changesets not found in the specified destination repository
5448 or the default push location. These are the changesets that would
5451 or the default push location. These are the changesets that would
5449 be pushed if a push was requested.
5452 be pushed if a push was requested.
5450
5453
5451 See pull for details of valid destination formats.
5454 See pull for details of valid destination formats.
5452
5455
5453 .. container:: verbose
5456 .. container:: verbose
5454
5457
5455 With -B/--bookmarks, the result of bookmark comparison between
5458 With -B/--bookmarks, the result of bookmark comparison between
5456 local and remote repositories is displayed. With -v/--verbose,
5459 local and remote repositories is displayed. With -v/--verbose,
5457 status is also displayed for each bookmark like below::
5460 status is also displayed for each bookmark like below::
5458
5461
5459 BM1 01234567890a added
5462 BM1 01234567890a added
5460 BM2 deleted
5463 BM2 deleted
5461 BM3 234567890abc advanced
5464 BM3 234567890abc advanced
5462 BM4 34567890abcd diverged
5465 BM4 34567890abcd diverged
5463 BM5 4567890abcde changed
5466 BM5 4567890abcde changed
5464
5467
5465 The action taken when pushing depends on the
5468 The action taken when pushing depends on the
5466 status of each bookmark:
5469 status of each bookmark:
5467
5470
5468 :``added``: push with ``-B`` will create it
5471 :``added``: push with ``-B`` will create it
5469 :``deleted``: push with ``-B`` will delete it
5472 :``deleted``: push with ``-B`` will delete it
5470 :``advanced``: push will update it
5473 :``advanced``: push will update it
5471 :``diverged``: push with ``-B`` will update it
5474 :``diverged``: push with ``-B`` will update it
5472 :``changed``: push with ``-B`` will update it
5475 :``changed``: push with ``-B`` will update it
5473
5476
5474 From the point of view of pushing behavior, bookmarks
5477 From the point of view of pushing behavior, bookmarks
5475 existing only in the remote repository are treated as
5478 existing only in the remote repository are treated as
5476 ``deleted``, even if it is in fact added remotely.
5479 ``deleted``, even if it is in fact added remotely.
5477
5480
5478 Returns 0 if there are outgoing changes, 1 otherwise.
5481 Returns 0 if there are outgoing changes, 1 otherwise.
5479 """
5482 """
5480 if opts.get('graph'):
5483 if opts.get('graph'):
5481 cmdutil.checkunsupportedgraphflags([], opts)
5484 cmdutil.checkunsupportedgraphflags([], opts)
5482 o, other = hg._outgoing(ui, repo, dest, opts)
5485 o, other = hg._outgoing(ui, repo, dest, opts)
5483 if not o:
5486 if not o:
5484 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5487 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5485 return
5488 return
5486
5489
5487 revdag = cmdutil.graphrevs(repo, o, opts)
5490 revdag = cmdutil.graphrevs(repo, o, opts)
5488 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
5491 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
5489 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
5492 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
5490 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5493 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5491 return 0
5494 return 0
5492
5495
5493 if opts.get('bookmarks'):
5496 if opts.get('bookmarks'):
5494 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5497 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5495 dest, branches = hg.parseurl(dest, opts.get('branch'))
5498 dest, branches = hg.parseurl(dest, opts.get('branch'))
5496 other = hg.peer(repo, opts, dest)
5499 other = hg.peer(repo, opts, dest)
5497 if 'bookmarks' not in other.listkeys('namespaces'):
5500 if 'bookmarks' not in other.listkeys('namespaces'):
5498 ui.warn(_("remote doesn't support bookmarks\n"))
5501 ui.warn(_("remote doesn't support bookmarks\n"))
5499 return 0
5502 return 0
5500 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
5503 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
5501 return bookmarks.outgoing(ui, repo, other)
5504 return bookmarks.outgoing(ui, repo, other)
5502
5505
5503 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
5506 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
5504 try:
5507 try:
5505 return hg.outgoing(ui, repo, dest, opts)
5508 return hg.outgoing(ui, repo, dest, opts)
5506 finally:
5509 finally:
5507 del repo._subtoppath
5510 del repo._subtoppath
5508
5511
5509 @command('parents',
5512 @command('parents',
5510 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
5513 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
5511 ] + templateopts,
5514 ] + templateopts,
5512 _('[-r REV] [FILE]'),
5515 _('[-r REV] [FILE]'),
5513 inferrepo=True)
5516 inferrepo=True)
5514 def parents(ui, repo, file_=None, **opts):
5517 def parents(ui, repo, file_=None, **opts):
5515 """show the parents of the working directory or revision (DEPRECATED)
5518 """show the parents of the working directory or revision (DEPRECATED)
5516
5519
5517 Print the working directory's parent revisions. If a revision is
5520 Print the working directory's parent revisions. If a revision is
5518 given via -r/--rev, the parent of that revision will be printed.
5521 given via -r/--rev, the parent of that revision will be printed.
5519 If a file argument is given, the revision in which the file was
5522 If a file argument is given, the revision in which the file was
5520 last changed (before the working directory revision or the
5523 last changed (before the working directory revision or the
5521 argument to --rev if given) is printed.
5524 argument to --rev if given) is printed.
5522
5525
5523 This command is equivalent to::
5526 This command is equivalent to::
5524
5527
5525 hg log -r "p1()+p2()" or
5528 hg log -r "p1()+p2()" or
5526 hg log -r "p1(REV)+p2(REV)" or
5529 hg log -r "p1(REV)+p2(REV)" or
5527 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5530 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5528 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5531 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5529
5532
5530 See :hg:`summary` and :hg:`help revsets` for related information.
5533 See :hg:`summary` and :hg:`help revsets` for related information.
5531
5534
5532 Returns 0 on success.
5535 Returns 0 on success.
5533 """
5536 """
5534
5537
5535 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
5538 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
5536
5539
5537 if file_:
5540 if file_:
5538 m = scmutil.match(ctx, (file_,), opts)
5541 m = scmutil.match(ctx, (file_,), opts)
5539 if m.anypats() or len(m.files()) != 1:
5542 if m.anypats() or len(m.files()) != 1:
5540 raise error.Abort(_('can only specify an explicit filename'))
5543 raise error.Abort(_('can only specify an explicit filename'))
5541 file_ = m.files()[0]
5544 file_ = m.files()[0]
5542 filenodes = []
5545 filenodes = []
5543 for cp in ctx.parents():
5546 for cp in ctx.parents():
5544 if not cp:
5547 if not cp:
5545 continue
5548 continue
5546 try:
5549 try:
5547 filenodes.append(cp.filenode(file_))
5550 filenodes.append(cp.filenode(file_))
5548 except error.LookupError:
5551 except error.LookupError:
5549 pass
5552 pass
5550 if not filenodes:
5553 if not filenodes:
5551 raise error.Abort(_("'%s' not found in manifest!") % file_)
5554 raise error.Abort(_("'%s' not found in manifest!") % file_)
5552 p = []
5555 p = []
5553 for fn in filenodes:
5556 for fn in filenodes:
5554 fctx = repo.filectx(file_, fileid=fn)
5557 fctx = repo.filectx(file_, fileid=fn)
5555 p.append(fctx.node())
5558 p.append(fctx.node())
5556 else:
5559 else:
5557 p = [cp.node() for cp in ctx.parents()]
5560 p = [cp.node() for cp in ctx.parents()]
5558
5561
5559 displayer = cmdutil.show_changeset(ui, repo, opts)
5562 displayer = cmdutil.show_changeset(ui, repo, opts)
5560 for n in p:
5563 for n in p:
5561 if n != nullid:
5564 if n != nullid:
5562 displayer.show(repo[n])
5565 displayer.show(repo[n])
5563 displayer.close()
5566 displayer.close()
5564
5567
5565 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
5568 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
5566 def paths(ui, repo, search=None, **opts):
5569 def paths(ui, repo, search=None, **opts):
5567 """show aliases for remote repositories
5570 """show aliases for remote repositories
5568
5571
5569 Show definition of symbolic path name NAME. If no name is given,
5572 Show definition of symbolic path name NAME. If no name is given,
5570 show definition of all available names.
5573 show definition of all available names.
5571
5574
5572 Option -q/--quiet suppresses all output when searching for NAME
5575 Option -q/--quiet suppresses all output when searching for NAME
5573 and shows only the path names when listing all definitions.
5576 and shows only the path names when listing all definitions.
5574
5577
5575 Path names are defined in the [paths] section of your
5578 Path names are defined in the [paths] section of your
5576 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5579 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5577 repository, ``.hg/hgrc`` is used, too.
5580 repository, ``.hg/hgrc`` is used, too.
5578
5581
5579 The path names ``default`` and ``default-push`` have a special
5582 The path names ``default`` and ``default-push`` have a special
5580 meaning. When performing a push or pull operation, they are used
5583 meaning. When performing a push or pull operation, they are used
5581 as fallbacks if no location is specified on the command-line.
5584 as fallbacks if no location is specified on the command-line.
5582 When ``default-push`` is set, it will be used for push and
5585 When ``default-push`` is set, it will be used for push and
5583 ``default`` will be used for pull; otherwise ``default`` is used
5586 ``default`` will be used for pull; otherwise ``default`` is used
5584 as the fallback for both. When cloning a repository, the clone
5587 as the fallback for both. When cloning a repository, the clone
5585 source is written as ``default`` in ``.hg/hgrc``.
5588 source is written as ``default`` in ``.hg/hgrc``.
5586
5589
5587 .. note::
5590 .. note::
5588
5591
5589 ``default`` and ``default-push`` apply to all inbound (e.g.
5592 ``default`` and ``default-push`` apply to all inbound (e.g.
5590 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5593 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5591 and :hg:`bundle`) operations.
5594 and :hg:`bundle`) operations.
5592
5595
5593 See :hg:`help urls` for more information.
5596 See :hg:`help urls` for more information.
5594
5597
5595 Returns 0 on success.
5598 Returns 0 on success.
5596 """
5599 """
5597 if search:
5600 if search:
5598 pathitems = [(name, path) for name, path in ui.paths.iteritems()
5601 pathitems = [(name, path) for name, path in ui.paths.iteritems()
5599 if name == search]
5602 if name == search]
5600 else:
5603 else:
5601 pathitems = sorted(ui.paths.iteritems())
5604 pathitems = sorted(ui.paths.iteritems())
5602
5605
5603 fm = ui.formatter('paths', opts)
5606 fm = ui.formatter('paths', opts)
5604 if fm:
5607 if fm:
5605 hidepassword = str
5608 hidepassword = str
5606 else:
5609 else:
5607 hidepassword = util.hidepassword
5610 hidepassword = util.hidepassword
5608 if ui.quiet:
5611 if ui.quiet:
5609 namefmt = '%s\n'
5612 namefmt = '%s\n'
5610 else:
5613 else:
5611 namefmt = '%s = '
5614 namefmt = '%s = '
5612 showsubopts = not search and not ui.quiet
5615 showsubopts = not search and not ui.quiet
5613
5616
5614 for name, path in pathitems:
5617 for name, path in pathitems:
5615 fm.startitem()
5618 fm.startitem()
5616 fm.condwrite(not search, 'name', namefmt, name)
5619 fm.condwrite(not search, 'name', namefmt, name)
5617 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
5620 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
5618 for subopt, value in sorted(path.suboptions.items()):
5621 for subopt, value in sorted(path.suboptions.items()):
5619 assert subopt not in ('name', 'url')
5622 assert subopt not in ('name', 'url')
5620 if showsubopts:
5623 if showsubopts:
5621 fm.plain('%s:%s = ' % (name, subopt))
5624 fm.plain('%s:%s = ' % (name, subopt))
5622 fm.condwrite(showsubopts, subopt, '%s\n', value)
5625 fm.condwrite(showsubopts, subopt, '%s\n', value)
5623
5626
5624 fm.end()
5627 fm.end()
5625
5628
5626 if search and not pathitems:
5629 if search and not pathitems:
5627 if not ui.quiet:
5630 if not ui.quiet:
5628 ui.warn(_("not found!\n"))
5631 ui.warn(_("not found!\n"))
5629 return 1
5632 return 1
5630 else:
5633 else:
5631 return 0
5634 return 0
5632
5635
5633 @command('phase',
5636 @command('phase',
5634 [('p', 'public', False, _('set changeset phase to public')),
5637 [('p', 'public', False, _('set changeset phase to public')),
5635 ('d', 'draft', False, _('set changeset phase to draft')),
5638 ('d', 'draft', False, _('set changeset phase to draft')),
5636 ('s', 'secret', False, _('set changeset phase to secret')),
5639 ('s', 'secret', False, _('set changeset phase to secret')),
5637 ('f', 'force', False, _('allow to move boundary backward')),
5640 ('f', 'force', False, _('allow to move boundary backward')),
5638 ('r', 'rev', [], _('target revision'), _('REV')),
5641 ('r', 'rev', [], _('target revision'), _('REV')),
5639 ],
5642 ],
5640 _('[-p|-d|-s] [-f] [-r] [REV...]'))
5643 _('[-p|-d|-s] [-f] [-r] [REV...]'))
5641 def phase(ui, repo, *revs, **opts):
5644 def phase(ui, repo, *revs, **opts):
5642 """set or show the current phase name
5645 """set or show the current phase name
5643
5646
5644 With no argument, show the phase name of the current revision(s).
5647 With no argument, show the phase name of the current revision(s).
5645
5648
5646 With one of -p/--public, -d/--draft or -s/--secret, change the
5649 With one of -p/--public, -d/--draft or -s/--secret, change the
5647 phase value of the specified revisions.
5650 phase value of the specified revisions.
5648
5651
5649 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
5652 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
5650 lower phase to an higher phase. Phases are ordered as follows::
5653 lower phase to an higher phase. Phases are ordered as follows::
5651
5654
5652 public < draft < secret
5655 public < draft < secret
5653
5656
5654 Returns 0 on success, 1 if some phases could not be changed.
5657 Returns 0 on success, 1 if some phases could not be changed.
5655
5658
5656 (For more information about the phases concept, see :hg:`help phases`.)
5659 (For more information about the phases concept, see :hg:`help phases`.)
5657 """
5660 """
5658 # search for a unique phase argument
5661 # search for a unique phase argument
5659 targetphase = None
5662 targetphase = None
5660 for idx, name in enumerate(phases.phasenames):
5663 for idx, name in enumerate(phases.phasenames):
5661 if opts[name]:
5664 if opts[name]:
5662 if targetphase is not None:
5665 if targetphase is not None:
5663 raise error.Abort(_('only one phase can be specified'))
5666 raise error.Abort(_('only one phase can be specified'))
5664 targetphase = idx
5667 targetphase = idx
5665
5668
5666 # look for specified revision
5669 # look for specified revision
5667 revs = list(revs)
5670 revs = list(revs)
5668 revs.extend(opts['rev'])
5671 revs.extend(opts['rev'])
5669 if not revs:
5672 if not revs:
5670 # display both parents as the second parent phase can influence
5673 # display both parents as the second parent phase can influence
5671 # the phase of a merge commit
5674 # the phase of a merge commit
5672 revs = [c.rev() for c in repo[None].parents()]
5675 revs = [c.rev() for c in repo[None].parents()]
5673
5676
5674 revs = scmutil.revrange(repo, revs)
5677 revs = scmutil.revrange(repo, revs)
5675
5678
5676 lock = None
5679 lock = None
5677 ret = 0
5680 ret = 0
5678 if targetphase is None:
5681 if targetphase is None:
5679 # display
5682 # display
5680 for r in revs:
5683 for r in revs:
5681 ctx = repo[r]
5684 ctx = repo[r]
5682 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5685 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5683 else:
5686 else:
5684 tr = None
5687 tr = None
5685 lock = repo.lock()
5688 lock = repo.lock()
5686 try:
5689 try:
5687 tr = repo.transaction("phase")
5690 tr = repo.transaction("phase")
5688 # set phase
5691 # set phase
5689 if not revs:
5692 if not revs:
5690 raise error.Abort(_('empty revision set'))
5693 raise error.Abort(_('empty revision set'))
5691 nodes = [repo[r].node() for r in revs]
5694 nodes = [repo[r].node() for r in revs]
5692 # moving revision from public to draft may hide them
5695 # moving revision from public to draft may hide them
5693 # We have to check result on an unfiltered repository
5696 # We have to check result on an unfiltered repository
5694 unfi = repo.unfiltered()
5697 unfi = repo.unfiltered()
5695 getphase = unfi._phasecache.phase
5698 getphase = unfi._phasecache.phase
5696 olddata = [getphase(unfi, r) for r in unfi]
5699 olddata = [getphase(unfi, r) for r in unfi]
5697 phases.advanceboundary(repo, tr, targetphase, nodes)
5700 phases.advanceboundary(repo, tr, targetphase, nodes)
5698 if opts['force']:
5701 if opts['force']:
5699 phases.retractboundary(repo, tr, targetphase, nodes)
5702 phases.retractboundary(repo, tr, targetphase, nodes)
5700 tr.close()
5703 tr.close()
5701 finally:
5704 finally:
5702 if tr is not None:
5705 if tr is not None:
5703 tr.release()
5706 tr.release()
5704 lock.release()
5707 lock.release()
5705 getphase = unfi._phasecache.phase
5708 getphase = unfi._phasecache.phase
5706 newdata = [getphase(unfi, r) for r in unfi]
5709 newdata = [getphase(unfi, r) for r in unfi]
5707 changes = sum(newdata[r] != olddata[r] for r in unfi)
5710 changes = sum(newdata[r] != olddata[r] for r in unfi)
5708 cl = unfi.changelog
5711 cl = unfi.changelog
5709 rejected = [n for n in nodes
5712 rejected = [n for n in nodes
5710 if newdata[cl.rev(n)] < targetphase]
5713 if newdata[cl.rev(n)] < targetphase]
5711 if rejected:
5714 if rejected:
5712 ui.warn(_('cannot move %i changesets to a higher '
5715 ui.warn(_('cannot move %i changesets to a higher '
5713 'phase, use --force\n') % len(rejected))
5716 'phase, use --force\n') % len(rejected))
5714 ret = 1
5717 ret = 1
5715 if changes:
5718 if changes:
5716 msg = _('phase changed for %i changesets\n') % changes
5719 msg = _('phase changed for %i changesets\n') % changes
5717 if ret:
5720 if ret:
5718 ui.status(msg)
5721 ui.status(msg)
5719 else:
5722 else:
5720 ui.note(msg)
5723 ui.note(msg)
5721 else:
5724 else:
5722 ui.warn(_('no phases changed\n'))
5725 ui.warn(_('no phases changed\n'))
5723 return ret
5726 return ret
5724
5727
5725 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5728 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5726 """Run after a changegroup has been added via pull/unbundle
5729 """Run after a changegroup has been added via pull/unbundle
5727
5730
5728 This takes arguments below:
5731 This takes arguments below:
5729
5732
5730 :modheads: change of heads by pull/unbundle
5733 :modheads: change of heads by pull/unbundle
5731 :optupdate: updating working directory is needed or not
5734 :optupdate: updating working directory is needed or not
5732 :checkout: update destination revision (or None to default destination)
5735 :checkout: update destination revision (or None to default destination)
5733 :brev: a name, which might be a bookmark to be activated after updating
5736 :brev: a name, which might be a bookmark to be activated after updating
5734 """
5737 """
5735 if modheads == 0:
5738 if modheads == 0:
5736 return
5739 return
5737 if optupdate:
5740 if optupdate:
5738 try:
5741 try:
5739 return hg.updatetotally(ui, repo, checkout, brev)
5742 return hg.updatetotally(ui, repo, checkout, brev)
5740 except error.UpdateAbort as inst:
5743 except error.UpdateAbort as inst:
5741 msg = _("not updating: %s") % str(inst)
5744 msg = _("not updating: %s") % str(inst)
5742 hint = inst.hint
5745 hint = inst.hint
5743 raise error.UpdateAbort(msg, hint=hint)
5746 raise error.UpdateAbort(msg, hint=hint)
5744 if modheads > 1:
5747 if modheads > 1:
5745 currentbranchheads = len(repo.branchheads())
5748 currentbranchheads = len(repo.branchheads())
5746 if currentbranchheads == modheads:
5749 if currentbranchheads == modheads:
5747 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
5750 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
5748 elif currentbranchheads > 1:
5751 elif currentbranchheads > 1:
5749 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
5752 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
5750 "merge)\n"))
5753 "merge)\n"))
5751 else:
5754 else:
5752 ui.status(_("(run 'hg heads' to see heads)\n"))
5755 ui.status(_("(run 'hg heads' to see heads)\n"))
5753 else:
5756 else:
5754 ui.status(_("(run 'hg update' to get a working copy)\n"))
5757 ui.status(_("(run 'hg update' to get a working copy)\n"))
5755
5758
5756 @command('^pull',
5759 @command('^pull',
5757 [('u', 'update', None,
5760 [('u', 'update', None,
5758 _('update to new branch head if changesets were pulled')),
5761 _('update to new branch head if changesets were pulled')),
5759 ('f', 'force', None, _('run even when remote repository is unrelated')),
5762 ('f', 'force', None, _('run even when remote repository is unrelated')),
5760 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
5763 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
5761 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
5764 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
5762 ('b', 'branch', [], _('a specific branch you would like to pull'),
5765 ('b', 'branch', [], _('a specific branch you would like to pull'),
5763 _('BRANCH')),
5766 _('BRANCH')),
5764 ] + remoteopts,
5767 ] + remoteopts,
5765 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
5768 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
5766 def pull(ui, repo, source="default", **opts):
5769 def pull(ui, repo, source="default", **opts):
5767 """pull changes from the specified source
5770 """pull changes from the specified source
5768
5771
5769 Pull changes from a remote repository to a local one.
5772 Pull changes from a remote repository to a local one.
5770
5773
5771 This finds all changes from the repository at the specified path
5774 This finds all changes from the repository at the specified path
5772 or URL and adds them to a local repository (the current one unless
5775 or URL and adds them to a local repository (the current one unless
5773 -R is specified). By default, this does not update the copy of the
5776 -R is specified). By default, this does not update the copy of the
5774 project in the working directory.
5777 project in the working directory.
5775
5778
5776 Use :hg:`incoming` if you want to see what would have been added
5779 Use :hg:`incoming` if you want to see what would have been added
5777 by a pull at the time you issued this command. If you then decide
5780 by a pull at the time you issued this command. If you then decide
5778 to add those changes to the repository, you should use :hg:`pull
5781 to add those changes to the repository, you should use :hg:`pull
5779 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5782 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5780
5783
5781 If SOURCE is omitted, the 'default' path will be used.
5784 If SOURCE is omitted, the 'default' path will be used.
5782 See :hg:`help urls` for more information.
5785 See :hg:`help urls` for more information.
5783
5786
5784 Specifying bookmark as ``.`` is equivalent to specifying the active
5787 Specifying bookmark as ``.`` is equivalent to specifying the active
5785 bookmark's name.
5788 bookmark's name.
5786
5789
5787 Returns 0 on success, 1 if an update had unresolved files.
5790 Returns 0 on success, 1 if an update had unresolved files.
5788 """
5791 """
5789 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5792 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5790 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5793 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5791 other = hg.peer(repo, opts, source)
5794 other = hg.peer(repo, opts, source)
5792 try:
5795 try:
5793 revs, checkout = hg.addbranchrevs(repo, other, branches,
5796 revs, checkout = hg.addbranchrevs(repo, other, branches,
5794 opts.get('rev'))
5797 opts.get('rev'))
5795
5798
5796
5799
5797 pullopargs = {}
5800 pullopargs = {}
5798 if opts.get('bookmark'):
5801 if opts.get('bookmark'):
5799 if not revs:
5802 if not revs:
5800 revs = []
5803 revs = []
5801 # The list of bookmark used here is not the one used to actually
5804 # The list of bookmark used here is not the one used to actually
5802 # update the bookmark name. This can result in the revision pulled
5805 # update the bookmark name. This can result in the revision pulled
5803 # not ending up with the name of the bookmark because of a race
5806 # not ending up with the name of the bookmark because of a race
5804 # condition on the server. (See issue 4689 for details)
5807 # condition on the server. (See issue 4689 for details)
5805 remotebookmarks = other.listkeys('bookmarks')
5808 remotebookmarks = other.listkeys('bookmarks')
5806 pullopargs['remotebookmarks'] = remotebookmarks
5809 pullopargs['remotebookmarks'] = remotebookmarks
5807 for b in opts['bookmark']:
5810 for b in opts['bookmark']:
5808 b = repo._bookmarks.expandname(b)
5811 b = repo._bookmarks.expandname(b)
5809 if b not in remotebookmarks:
5812 if b not in remotebookmarks:
5810 raise error.Abort(_('remote bookmark %s not found!') % b)
5813 raise error.Abort(_('remote bookmark %s not found!') % b)
5811 revs.append(remotebookmarks[b])
5814 revs.append(remotebookmarks[b])
5812
5815
5813 if revs:
5816 if revs:
5814 try:
5817 try:
5815 # When 'rev' is a bookmark name, we cannot guarantee that it
5818 # When 'rev' is a bookmark name, we cannot guarantee that it
5816 # will be updated with that name because of a race condition
5819 # will be updated with that name because of a race condition
5817 # server side. (See issue 4689 for details)
5820 # server side. (See issue 4689 for details)
5818 oldrevs = revs
5821 oldrevs = revs
5819 revs = [] # actually, nodes
5822 revs = [] # actually, nodes
5820 for r in oldrevs:
5823 for r in oldrevs:
5821 node = other.lookup(r)
5824 node = other.lookup(r)
5822 revs.append(node)
5825 revs.append(node)
5823 if r == checkout:
5826 if r == checkout:
5824 checkout = node
5827 checkout = node
5825 except error.CapabilityError:
5828 except error.CapabilityError:
5826 err = _("other repository doesn't support revision lookup, "
5829 err = _("other repository doesn't support revision lookup, "
5827 "so a rev cannot be specified.")
5830 "so a rev cannot be specified.")
5828 raise error.Abort(err)
5831 raise error.Abort(err)
5829
5832
5830 pullopargs.update(opts.get('opargs', {}))
5833 pullopargs.update(opts.get('opargs', {}))
5831 modheads = exchange.pull(repo, other, heads=revs,
5834 modheads = exchange.pull(repo, other, heads=revs,
5832 force=opts.get('force'),
5835 force=opts.get('force'),
5833 bookmarks=opts.get('bookmark', ()),
5836 bookmarks=opts.get('bookmark', ()),
5834 opargs=pullopargs).cgresult
5837 opargs=pullopargs).cgresult
5835
5838
5836 # brev is a name, which might be a bookmark to be activated at
5839 # brev is a name, which might be a bookmark to be activated at
5837 # the end of the update. In other words, it is an explicit
5840 # the end of the update. In other words, it is an explicit
5838 # destination of the update
5841 # destination of the update
5839 brev = None
5842 brev = None
5840
5843
5841 if checkout:
5844 if checkout:
5842 checkout = str(repo.changelog.rev(checkout))
5845 checkout = str(repo.changelog.rev(checkout))
5843
5846
5844 # order below depends on implementation of
5847 # order below depends on implementation of
5845 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5848 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5846 # because 'checkout' is determined without it.
5849 # because 'checkout' is determined without it.
5847 if opts.get('rev'):
5850 if opts.get('rev'):
5848 brev = opts['rev'][0]
5851 brev = opts['rev'][0]
5849 elif opts.get('branch'):
5852 elif opts.get('branch'):
5850 brev = opts['branch'][0]
5853 brev = opts['branch'][0]
5851 else:
5854 else:
5852 brev = branches[0]
5855 brev = branches[0]
5853 repo._subtoppath = source
5856 repo._subtoppath = source
5854 try:
5857 try:
5855 ret = postincoming(ui, repo, modheads, opts.get('update'),
5858 ret = postincoming(ui, repo, modheads, opts.get('update'),
5856 checkout, brev)
5859 checkout, brev)
5857
5860
5858 finally:
5861 finally:
5859 del repo._subtoppath
5862 del repo._subtoppath
5860
5863
5861 finally:
5864 finally:
5862 other.close()
5865 other.close()
5863 return ret
5866 return ret
5864
5867
5865 @command('^push',
5868 @command('^push',
5866 [('f', 'force', None, _('force push')),
5869 [('f', 'force', None, _('force push')),
5867 ('r', 'rev', [],
5870 ('r', 'rev', [],
5868 _('a changeset intended to be included in the destination'),
5871 _('a changeset intended to be included in the destination'),
5869 _('REV')),
5872 _('REV')),
5870 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5873 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5871 ('b', 'branch', [],
5874 ('b', 'branch', [],
5872 _('a specific branch you would like to push'), _('BRANCH')),
5875 _('a specific branch you would like to push'), _('BRANCH')),
5873 ('', 'new-branch', False, _('allow pushing a new branch')),
5876 ('', 'new-branch', False, _('allow pushing a new branch')),
5874 ] + remoteopts,
5877 ] + remoteopts,
5875 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5878 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5876 def push(ui, repo, dest=None, **opts):
5879 def push(ui, repo, dest=None, **opts):
5877 """push changes to the specified destination
5880 """push changes to the specified destination
5878
5881
5879 Push changesets from the local repository to the specified
5882 Push changesets from the local repository to the specified
5880 destination.
5883 destination.
5881
5884
5882 This operation is symmetrical to pull: it is identical to a pull
5885 This operation is symmetrical to pull: it is identical to a pull
5883 in the destination repository from the current one.
5886 in the destination repository from the current one.
5884
5887
5885 By default, push will not allow creation of new heads at the
5888 By default, push will not allow creation of new heads at the
5886 destination, since multiple heads would make it unclear which head
5889 destination, since multiple heads would make it unclear which head
5887 to use. In this situation, it is recommended to pull and merge
5890 to use. In this situation, it is recommended to pull and merge
5888 before pushing.
5891 before pushing.
5889
5892
5890 Use --new-branch if you want to allow push to create a new named
5893 Use --new-branch if you want to allow push to create a new named
5891 branch that is not present at the destination. This allows you to
5894 branch that is not present at the destination. This allows you to
5892 only create a new branch without forcing other changes.
5895 only create a new branch without forcing other changes.
5893
5896
5894 .. note::
5897 .. note::
5895
5898
5896 Extra care should be taken with the -f/--force option,
5899 Extra care should be taken with the -f/--force option,
5897 which will push all new heads on all branches, an action which will
5900 which will push all new heads on all branches, an action which will
5898 almost always cause confusion for collaborators.
5901 almost always cause confusion for collaborators.
5899
5902
5900 If -r/--rev is used, the specified revision and all its ancestors
5903 If -r/--rev is used, the specified revision and all its ancestors
5901 will be pushed to the remote repository.
5904 will be pushed to the remote repository.
5902
5905
5903 If -B/--bookmark is used, the specified bookmarked revision, its
5906 If -B/--bookmark is used, the specified bookmarked revision, its
5904 ancestors, and the bookmark will be pushed to the remote
5907 ancestors, and the bookmark will be pushed to the remote
5905 repository. Specifying ``.`` is equivalent to specifying the active
5908 repository. Specifying ``.`` is equivalent to specifying the active
5906 bookmark's name.
5909 bookmark's name.
5907
5910
5908 Please see :hg:`help urls` for important details about ``ssh://``
5911 Please see :hg:`help urls` for important details about ``ssh://``
5909 URLs. If DESTINATION is omitted, a default path will be used.
5912 URLs. If DESTINATION is omitted, a default path will be used.
5910
5913
5911 Returns 0 if push was successful, 1 if nothing to push.
5914 Returns 0 if push was successful, 1 if nothing to push.
5912 """
5915 """
5913
5916
5914 if opts.get('bookmark'):
5917 if opts.get('bookmark'):
5915 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5918 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5916 for b in opts['bookmark']:
5919 for b in opts['bookmark']:
5917 # translate -B options to -r so changesets get pushed
5920 # translate -B options to -r so changesets get pushed
5918 b = repo._bookmarks.expandname(b)
5921 b = repo._bookmarks.expandname(b)
5919 if b in repo._bookmarks:
5922 if b in repo._bookmarks:
5920 opts.setdefault('rev', []).append(b)
5923 opts.setdefault('rev', []).append(b)
5921 else:
5924 else:
5922 # if we try to push a deleted bookmark, translate it to null
5925 # if we try to push a deleted bookmark, translate it to null
5923 # this lets simultaneous -r, -b options continue working
5926 # this lets simultaneous -r, -b options continue working
5924 opts.setdefault('rev', []).append("null")
5927 opts.setdefault('rev', []).append("null")
5925
5928
5926 path = ui.paths.getpath(dest, default=('default-push', 'default'))
5929 path = ui.paths.getpath(dest, default=('default-push', 'default'))
5927 if not path:
5930 if not path:
5928 raise error.Abort(_('default repository not configured!'),
5931 raise error.Abort(_('default repository not configured!'),
5929 hint=_('see the "path" section in "hg help config"'))
5932 hint=_('see the "path" section in "hg help config"'))
5930 dest = path.pushloc or path.loc
5933 dest = path.pushloc or path.loc
5931 branches = (path.branch, opts.get('branch') or [])
5934 branches = (path.branch, opts.get('branch') or [])
5932 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5935 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5933 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5936 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5934 other = hg.peer(repo, opts, dest)
5937 other = hg.peer(repo, opts, dest)
5935
5938
5936 if revs:
5939 if revs:
5937 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5940 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5938 if not revs:
5941 if not revs:
5939 raise error.Abort(_("specified revisions evaluate to an empty set"),
5942 raise error.Abort(_("specified revisions evaluate to an empty set"),
5940 hint=_("use different revision arguments"))
5943 hint=_("use different revision arguments"))
5941 elif path.pushrev:
5944 elif path.pushrev:
5942 # It doesn't make any sense to specify ancestor revisions. So limit
5945 # It doesn't make any sense to specify ancestor revisions. So limit
5943 # to DAG heads to make discovery simpler.
5946 # to DAG heads to make discovery simpler.
5944 expr = revset.formatspec('heads(%r)', path.pushrev)
5947 expr = revset.formatspec('heads(%r)', path.pushrev)
5945 revs = scmutil.revrange(repo, [expr])
5948 revs = scmutil.revrange(repo, [expr])
5946 revs = [repo[rev].node() for rev in revs]
5949 revs = [repo[rev].node() for rev in revs]
5947 if not revs:
5950 if not revs:
5948 raise error.Abort(_('default push revset for path evaluates to an '
5951 raise error.Abort(_('default push revset for path evaluates to an '
5949 'empty set'))
5952 'empty set'))
5950
5953
5951 repo._subtoppath = dest
5954 repo._subtoppath = dest
5952 try:
5955 try:
5953 # push subrepos depth-first for coherent ordering
5956 # push subrepos depth-first for coherent ordering
5954 c = repo['']
5957 c = repo['']
5955 subs = c.substate # only repos that are committed
5958 subs = c.substate # only repos that are committed
5956 for s in sorted(subs):
5959 for s in sorted(subs):
5957 result = c.sub(s).push(opts)
5960 result = c.sub(s).push(opts)
5958 if result == 0:
5961 if result == 0:
5959 return not result
5962 return not result
5960 finally:
5963 finally:
5961 del repo._subtoppath
5964 del repo._subtoppath
5962 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5965 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5963 newbranch=opts.get('new_branch'),
5966 newbranch=opts.get('new_branch'),
5964 bookmarks=opts.get('bookmark', ()),
5967 bookmarks=opts.get('bookmark', ()),
5965 opargs=opts.get('opargs'))
5968 opargs=opts.get('opargs'))
5966
5969
5967 result = not pushop.cgresult
5970 result = not pushop.cgresult
5968
5971
5969 if pushop.bkresult is not None:
5972 if pushop.bkresult is not None:
5970 if pushop.bkresult == 2:
5973 if pushop.bkresult == 2:
5971 result = 2
5974 result = 2
5972 elif not result and pushop.bkresult:
5975 elif not result and pushop.bkresult:
5973 result = 2
5976 result = 2
5974
5977
5975 return result
5978 return result
5976
5979
5977 @command('recover', [])
5980 @command('recover', [])
5978 def recover(ui, repo):
5981 def recover(ui, repo):
5979 """roll back an interrupted transaction
5982 """roll back an interrupted transaction
5980
5983
5981 Recover from an interrupted commit or pull.
5984 Recover from an interrupted commit or pull.
5982
5985
5983 This command tries to fix the repository status after an
5986 This command tries to fix the repository status after an
5984 interrupted operation. It should only be necessary when Mercurial
5987 interrupted operation. It should only be necessary when Mercurial
5985 suggests it.
5988 suggests it.
5986
5989
5987 Returns 0 if successful, 1 if nothing to recover or verify fails.
5990 Returns 0 if successful, 1 if nothing to recover or verify fails.
5988 """
5991 """
5989 if repo.recover():
5992 if repo.recover():
5990 return hg.verify(repo)
5993 return hg.verify(repo)
5991 return 1
5994 return 1
5992
5995
5993 @command('^remove|rm',
5996 @command('^remove|rm',
5994 [('A', 'after', None, _('record delete for missing files')),
5997 [('A', 'after', None, _('record delete for missing files')),
5995 ('f', 'force', None,
5998 ('f', 'force', None,
5996 _('forget added files, delete modified files')),
5999 _('forget added files, delete modified files')),
5997 ] + subrepoopts + walkopts,
6000 ] + subrepoopts + walkopts,
5998 _('[OPTION]... FILE...'),
6001 _('[OPTION]... FILE...'),
5999 inferrepo=True)
6002 inferrepo=True)
6000 def remove(ui, repo, *pats, **opts):
6003 def remove(ui, repo, *pats, **opts):
6001 """remove the specified files on the next commit
6004 """remove the specified files on the next commit
6002
6005
6003 Schedule the indicated files for removal from the current branch.
6006 Schedule the indicated files for removal from the current branch.
6004
6007
6005 This command schedules the files to be removed at the next commit.
6008 This command schedules the files to be removed at the next commit.
6006 To undo a remove before that, see :hg:`revert`. To undo added
6009 To undo a remove before that, see :hg:`revert`. To undo added
6007 files, see :hg:`forget`.
6010 files, see :hg:`forget`.
6008
6011
6009 .. container:: verbose
6012 .. container:: verbose
6010
6013
6011 -A/--after can be used to remove only files that have already
6014 -A/--after can be used to remove only files that have already
6012 been deleted, -f/--force can be used to force deletion, and -Af
6015 been deleted, -f/--force can be used to force deletion, and -Af
6013 can be used to remove files from the next revision without
6016 can be used to remove files from the next revision without
6014 deleting them from the working directory.
6017 deleting them from the working directory.
6015
6018
6016 The following table details the behavior of remove for different
6019 The following table details the behavior of remove for different
6017 file states (columns) and option combinations (rows). The file
6020 file states (columns) and option combinations (rows). The file
6018 states are Added [A], Clean [C], Modified [M] and Missing [!]
6021 states are Added [A], Clean [C], Modified [M] and Missing [!]
6019 (as reported by :hg:`status`). The actions are Warn, Remove
6022 (as reported by :hg:`status`). The actions are Warn, Remove
6020 (from branch) and Delete (from disk):
6023 (from branch) and Delete (from disk):
6021
6024
6022 ========= == == == ==
6025 ========= == == == ==
6023 opt/state A C M !
6026 opt/state A C M !
6024 ========= == == == ==
6027 ========= == == == ==
6025 none W RD W R
6028 none W RD W R
6026 -f R RD RD R
6029 -f R RD RD R
6027 -A W W W R
6030 -A W W W R
6028 -Af R R R R
6031 -Af R R R R
6029 ========= == == == ==
6032 ========= == == == ==
6030
6033
6031 .. note::
6034 .. note::
6032
6035
6033 :hg:`remove` never deletes files in Added [A] state from the
6036 :hg:`remove` never deletes files in Added [A] state from the
6034 working directory, not even if ``--force`` is specified.
6037 working directory, not even if ``--force`` is specified.
6035
6038
6036 Returns 0 on success, 1 if any warnings encountered.
6039 Returns 0 on success, 1 if any warnings encountered.
6037 """
6040 """
6038
6041
6039 after, force = opts.get('after'), opts.get('force')
6042 after, force = opts.get('after'), opts.get('force')
6040 if not pats and not after:
6043 if not pats and not after:
6041 raise error.Abort(_('no files specified'))
6044 raise error.Abort(_('no files specified'))
6042
6045
6043 m = scmutil.match(repo[None], pats, opts)
6046 m = scmutil.match(repo[None], pats, opts)
6044 subrepos = opts.get('subrepos')
6047 subrepos = opts.get('subrepos')
6045 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
6048 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
6046
6049
6047 @command('rename|move|mv',
6050 @command('rename|move|mv',
6048 [('A', 'after', None, _('record a rename that has already occurred')),
6051 [('A', 'after', None, _('record a rename that has already occurred')),
6049 ('f', 'force', None, _('forcibly copy over an existing managed file')),
6052 ('f', 'force', None, _('forcibly copy over an existing managed file')),
6050 ] + walkopts + dryrunopts,
6053 ] + walkopts + dryrunopts,
6051 _('[OPTION]... SOURCE... DEST'))
6054 _('[OPTION]... SOURCE... DEST'))
6052 def rename(ui, repo, *pats, **opts):
6055 def rename(ui, repo, *pats, **opts):
6053 """rename files; equivalent of copy + remove
6056 """rename files; equivalent of copy + remove
6054
6057
6055 Mark dest as copies of sources; mark sources for deletion. If dest
6058 Mark dest as copies of sources; mark sources for deletion. If dest
6056 is a directory, copies are put in that directory. If dest is a
6059 is a directory, copies are put in that directory. If dest is a
6057 file, there can only be one source.
6060 file, there can only be one source.
6058
6061
6059 By default, this command copies the contents of files as they
6062 By default, this command copies the contents of files as they
6060 exist in the working directory. If invoked with -A/--after, the
6063 exist in the working directory. If invoked with -A/--after, the
6061 operation is recorded, but no copying is performed.
6064 operation is recorded, but no copying is performed.
6062
6065
6063 This command takes effect at the next commit. To undo a rename
6066 This command takes effect at the next commit. To undo a rename
6064 before that, see :hg:`revert`.
6067 before that, see :hg:`revert`.
6065
6068
6066 Returns 0 on success, 1 if errors are encountered.
6069 Returns 0 on success, 1 if errors are encountered.
6067 """
6070 """
6068 with repo.wlock(False):
6071 with repo.wlock(False):
6069 return cmdutil.copy(ui, repo, pats, opts, rename=True)
6072 return cmdutil.copy(ui, repo, pats, opts, rename=True)
6070
6073
6071 @command('resolve',
6074 @command('resolve',
6072 [('a', 'all', None, _('select all unresolved files')),
6075 [('a', 'all', None, _('select all unresolved files')),
6073 ('l', 'list', None, _('list state of files needing merge')),
6076 ('l', 'list', None, _('list state of files needing merge')),
6074 ('m', 'mark', None, _('mark files as resolved')),
6077 ('m', 'mark', None, _('mark files as resolved')),
6075 ('u', 'unmark', None, _('mark files as unresolved')),
6078 ('u', 'unmark', None, _('mark files as unresolved')),
6076 ('n', 'no-status', None, _('hide status prefix'))]
6079 ('n', 'no-status', None, _('hide status prefix'))]
6077 + mergetoolopts + walkopts + formatteropts,
6080 + mergetoolopts + walkopts + formatteropts,
6078 _('[OPTION]... [FILE]...'),
6081 _('[OPTION]... [FILE]...'),
6079 inferrepo=True)
6082 inferrepo=True)
6080 def resolve(ui, repo, *pats, **opts):
6083 def resolve(ui, repo, *pats, **opts):
6081 """redo merges or set/view the merge status of files
6084 """redo merges or set/view the merge status of files
6082
6085
6083 Merges with unresolved conflicts are often the result of
6086 Merges with unresolved conflicts are often the result of
6084 non-interactive merging using the ``internal:merge`` configuration
6087 non-interactive merging using the ``internal:merge`` configuration
6085 setting, or a command-line merge tool like ``diff3``. The resolve
6088 setting, or a command-line merge tool like ``diff3``. The resolve
6086 command is used to manage the files involved in a merge, after
6089 command is used to manage the files involved in a merge, after
6087 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
6090 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
6088 working directory must have two parents). See :hg:`help
6091 working directory must have two parents). See :hg:`help
6089 merge-tools` for information on configuring merge tools.
6092 merge-tools` for information on configuring merge tools.
6090
6093
6091 The resolve command can be used in the following ways:
6094 The resolve command can be used in the following ways:
6092
6095
6093 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
6096 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
6094 files, discarding any previous merge attempts. Re-merging is not
6097 files, discarding any previous merge attempts. Re-merging is not
6095 performed for files already marked as resolved. Use ``--all/-a``
6098 performed for files already marked as resolved. Use ``--all/-a``
6096 to select all unresolved files. ``--tool`` can be used to specify
6099 to select all unresolved files. ``--tool`` can be used to specify
6097 the merge tool used for the given files. It overrides the HGMERGE
6100 the merge tool used for the given files. It overrides the HGMERGE
6098 environment variable and your configuration files. Previous file
6101 environment variable and your configuration files. Previous file
6099 contents are saved with a ``.orig`` suffix.
6102 contents are saved with a ``.orig`` suffix.
6100
6103
6101 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
6104 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
6102 (e.g. after having manually fixed-up the files). The default is
6105 (e.g. after having manually fixed-up the files). The default is
6103 to mark all unresolved files.
6106 to mark all unresolved files.
6104
6107
6105 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
6108 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
6106 default is to mark all resolved files.
6109 default is to mark all resolved files.
6107
6110
6108 - :hg:`resolve -l`: list files which had or still have conflicts.
6111 - :hg:`resolve -l`: list files which had or still have conflicts.
6109 In the printed list, ``U`` = unresolved and ``R`` = resolved.
6112 In the printed list, ``U`` = unresolved and ``R`` = resolved.
6110
6113
6111 .. note::
6114 .. note::
6112
6115
6113 Mercurial will not let you commit files with unresolved merge
6116 Mercurial will not let you commit files with unresolved merge
6114 conflicts. You must use :hg:`resolve -m ...` before you can
6117 conflicts. You must use :hg:`resolve -m ...` before you can
6115 commit after a conflicting merge.
6118 commit after a conflicting merge.
6116
6119
6117 Returns 0 on success, 1 if any files fail a resolve attempt.
6120 Returns 0 on success, 1 if any files fail a resolve attempt.
6118 """
6121 """
6119
6122
6120 flaglist = 'all mark unmark list no_status'.split()
6123 flaglist = 'all mark unmark list no_status'.split()
6121 all, mark, unmark, show, nostatus = \
6124 all, mark, unmark, show, nostatus = \
6122 [opts.get(o) for o in flaglist]
6125 [opts.get(o) for o in flaglist]
6123
6126
6124 if (show and (mark or unmark)) or (mark and unmark):
6127 if (show and (mark or unmark)) or (mark and unmark):
6125 raise error.Abort(_("too many options specified"))
6128 raise error.Abort(_("too many options specified"))
6126 if pats and all:
6129 if pats and all:
6127 raise error.Abort(_("can't specify --all and patterns"))
6130 raise error.Abort(_("can't specify --all and patterns"))
6128 if not (all or pats or show or mark or unmark):
6131 if not (all or pats or show or mark or unmark):
6129 raise error.Abort(_('no files or directories specified'),
6132 raise error.Abort(_('no files or directories specified'),
6130 hint=('use --all to re-merge all unresolved files'))
6133 hint=('use --all to re-merge all unresolved files'))
6131
6134
6132 if show:
6135 if show:
6133 fm = ui.formatter('resolve', opts)
6136 fm = ui.formatter('resolve', opts)
6134 ms = mergemod.mergestate.read(repo)
6137 ms = mergemod.mergestate.read(repo)
6135 m = scmutil.match(repo[None], pats, opts)
6138 m = scmutil.match(repo[None], pats, opts)
6136 for f in ms:
6139 for f in ms:
6137 if not m(f):
6140 if not m(f):
6138 continue
6141 continue
6139 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
6142 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
6140 'd': 'driverresolved'}[ms[f]]
6143 'd': 'driverresolved'}[ms[f]]
6141 fm.startitem()
6144 fm.startitem()
6142 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
6145 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
6143 fm.write('path', '%s\n', f, label=l)
6146 fm.write('path', '%s\n', f, label=l)
6144 fm.end()
6147 fm.end()
6145 return 0
6148 return 0
6146
6149
6147 with repo.wlock():
6150 with repo.wlock():
6148 ms = mergemod.mergestate.read(repo)
6151 ms = mergemod.mergestate.read(repo)
6149
6152
6150 if not (ms.active() or repo.dirstate.p2() != nullid):
6153 if not (ms.active() or repo.dirstate.p2() != nullid):
6151 raise error.Abort(
6154 raise error.Abort(
6152 _('resolve command not applicable when not merging'))
6155 _('resolve command not applicable when not merging'))
6153
6156
6154 wctx = repo[None]
6157 wctx = repo[None]
6155
6158
6156 if ms.mergedriver and ms.mdstate() == 'u':
6159 if ms.mergedriver and ms.mdstate() == 'u':
6157 proceed = mergemod.driverpreprocess(repo, ms, wctx)
6160 proceed = mergemod.driverpreprocess(repo, ms, wctx)
6158 ms.commit()
6161 ms.commit()
6159 # allow mark and unmark to go through
6162 # allow mark and unmark to go through
6160 if not mark and not unmark and not proceed:
6163 if not mark and not unmark and not proceed:
6161 return 1
6164 return 1
6162
6165
6163 m = scmutil.match(wctx, pats, opts)
6166 m = scmutil.match(wctx, pats, opts)
6164 ret = 0
6167 ret = 0
6165 didwork = False
6168 didwork = False
6166 runconclude = False
6169 runconclude = False
6167
6170
6168 tocomplete = []
6171 tocomplete = []
6169 for f in ms:
6172 for f in ms:
6170 if not m(f):
6173 if not m(f):
6171 continue
6174 continue
6172
6175
6173 didwork = True
6176 didwork = True
6174
6177
6175 # don't let driver-resolved files be marked, and run the conclude
6178 # don't let driver-resolved files be marked, and run the conclude
6176 # step if asked to resolve
6179 # step if asked to resolve
6177 if ms[f] == "d":
6180 if ms[f] == "d":
6178 exact = m.exact(f)
6181 exact = m.exact(f)
6179 if mark:
6182 if mark:
6180 if exact:
6183 if exact:
6181 ui.warn(_('not marking %s as it is driver-resolved\n')
6184 ui.warn(_('not marking %s as it is driver-resolved\n')
6182 % f)
6185 % f)
6183 elif unmark:
6186 elif unmark:
6184 if exact:
6187 if exact:
6185 ui.warn(_('not unmarking %s as it is driver-resolved\n')
6188 ui.warn(_('not unmarking %s as it is driver-resolved\n')
6186 % f)
6189 % f)
6187 else:
6190 else:
6188 runconclude = True
6191 runconclude = True
6189 continue
6192 continue
6190
6193
6191 if mark:
6194 if mark:
6192 ms.mark(f, "r")
6195 ms.mark(f, "r")
6193 elif unmark:
6196 elif unmark:
6194 ms.mark(f, "u")
6197 ms.mark(f, "u")
6195 else:
6198 else:
6196 # backup pre-resolve (merge uses .orig for its own purposes)
6199 # backup pre-resolve (merge uses .orig for its own purposes)
6197 a = repo.wjoin(f)
6200 a = repo.wjoin(f)
6198 try:
6201 try:
6199 util.copyfile(a, a + ".resolve")
6202 util.copyfile(a, a + ".resolve")
6200 except (IOError, OSError) as inst:
6203 except (IOError, OSError) as inst:
6201 if inst.errno != errno.ENOENT:
6204 if inst.errno != errno.ENOENT:
6202 raise
6205 raise
6203
6206
6204 try:
6207 try:
6205 # preresolve file
6208 # preresolve file
6206 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
6209 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
6207 'resolve')
6210 'resolve')
6208 complete, r = ms.preresolve(f, wctx)
6211 complete, r = ms.preresolve(f, wctx)
6209 if not complete:
6212 if not complete:
6210 tocomplete.append(f)
6213 tocomplete.append(f)
6211 elif r:
6214 elif r:
6212 ret = 1
6215 ret = 1
6213 finally:
6216 finally:
6214 ui.setconfig('ui', 'forcemerge', '', 'resolve')
6217 ui.setconfig('ui', 'forcemerge', '', 'resolve')
6215 ms.commit()
6218 ms.commit()
6216
6219
6217 # replace filemerge's .orig file with our resolve file, but only
6220 # replace filemerge's .orig file with our resolve file, but only
6218 # for merges that are complete
6221 # for merges that are complete
6219 if complete:
6222 if complete:
6220 try:
6223 try:
6221 util.rename(a + ".resolve",
6224 util.rename(a + ".resolve",
6222 scmutil.origpath(ui, repo, a))
6225 scmutil.origpath(ui, repo, a))
6223 except OSError as inst:
6226 except OSError as inst:
6224 if inst.errno != errno.ENOENT:
6227 if inst.errno != errno.ENOENT:
6225 raise
6228 raise
6226
6229
6227 for f in tocomplete:
6230 for f in tocomplete:
6228 try:
6231 try:
6229 # resolve file
6232 # resolve file
6230 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
6233 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
6231 'resolve')
6234 'resolve')
6232 r = ms.resolve(f, wctx)
6235 r = ms.resolve(f, wctx)
6233 if r:
6236 if r:
6234 ret = 1
6237 ret = 1
6235 finally:
6238 finally:
6236 ui.setconfig('ui', 'forcemerge', '', 'resolve')
6239 ui.setconfig('ui', 'forcemerge', '', 'resolve')
6237 ms.commit()
6240 ms.commit()
6238
6241
6239 # replace filemerge's .orig file with our resolve file
6242 # replace filemerge's .orig file with our resolve file
6240 a = repo.wjoin(f)
6243 a = repo.wjoin(f)
6241 try:
6244 try:
6242 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
6245 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
6243 except OSError as inst:
6246 except OSError as inst:
6244 if inst.errno != errno.ENOENT:
6247 if inst.errno != errno.ENOENT:
6245 raise
6248 raise
6246
6249
6247 ms.commit()
6250 ms.commit()
6248 ms.recordactions()
6251 ms.recordactions()
6249
6252
6250 if not didwork and pats:
6253 if not didwork and pats:
6251 hint = None
6254 hint = None
6252 if not any([p for p in pats if p.find(':') >= 0]):
6255 if not any([p for p in pats if p.find(':') >= 0]):
6253 pats = ['path:%s' % p for p in pats]
6256 pats = ['path:%s' % p for p in pats]
6254 m = scmutil.match(wctx, pats, opts)
6257 m = scmutil.match(wctx, pats, opts)
6255 for f in ms:
6258 for f in ms:
6256 if not m(f):
6259 if not m(f):
6257 continue
6260 continue
6258 flags = ''.join(['-%s ' % o[0] for o in flaglist
6261 flags = ''.join(['-%s ' % o[0] for o in flaglist
6259 if opts.get(o)])
6262 if opts.get(o)])
6260 hint = _("(try: hg resolve %s%s)\n") % (
6263 hint = _("(try: hg resolve %s%s)\n") % (
6261 flags,
6264 flags,
6262 ' '.join(pats))
6265 ' '.join(pats))
6263 break
6266 break
6264 ui.warn(_("arguments do not match paths that need resolving\n"))
6267 ui.warn(_("arguments do not match paths that need resolving\n"))
6265 if hint:
6268 if hint:
6266 ui.warn(hint)
6269 ui.warn(hint)
6267 elif ms.mergedriver and ms.mdstate() != 's':
6270 elif ms.mergedriver and ms.mdstate() != 's':
6268 # run conclude step when either a driver-resolved file is requested
6271 # run conclude step when either a driver-resolved file is requested
6269 # or there are no driver-resolved files
6272 # or there are no driver-resolved files
6270 # we can't use 'ret' to determine whether any files are unresolved
6273 # we can't use 'ret' to determine whether any files are unresolved
6271 # because we might not have tried to resolve some
6274 # because we might not have tried to resolve some
6272 if ((runconclude or not list(ms.driverresolved()))
6275 if ((runconclude or not list(ms.driverresolved()))
6273 and not list(ms.unresolved())):
6276 and not list(ms.unresolved())):
6274 proceed = mergemod.driverconclude(repo, ms, wctx)
6277 proceed = mergemod.driverconclude(repo, ms, wctx)
6275 ms.commit()
6278 ms.commit()
6276 if not proceed:
6279 if not proceed:
6277 return 1
6280 return 1
6278
6281
6279 # Nudge users into finishing an unfinished operation
6282 # Nudge users into finishing an unfinished operation
6280 unresolvedf = list(ms.unresolved())
6283 unresolvedf = list(ms.unresolved())
6281 driverresolvedf = list(ms.driverresolved())
6284 driverresolvedf = list(ms.driverresolved())
6282 if not unresolvedf and not driverresolvedf:
6285 if not unresolvedf and not driverresolvedf:
6283 ui.status(_('(no more unresolved files)\n'))
6286 ui.status(_('(no more unresolved files)\n'))
6284 cmdutil.checkafterresolved(repo)
6287 cmdutil.checkafterresolved(repo)
6285 elif not unresolvedf:
6288 elif not unresolvedf:
6286 ui.status(_('(no more unresolved files -- '
6289 ui.status(_('(no more unresolved files -- '
6287 'run "hg resolve --all" to conclude)\n'))
6290 'run "hg resolve --all" to conclude)\n'))
6288
6291
6289 return ret
6292 return ret
6290
6293
6291 @command('revert',
6294 @command('revert',
6292 [('a', 'all', None, _('revert all changes when no arguments given')),
6295 [('a', 'all', None, _('revert all changes when no arguments given')),
6293 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6296 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6294 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
6297 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
6295 ('C', 'no-backup', None, _('do not save backup copies of files')),
6298 ('C', 'no-backup', None, _('do not save backup copies of files')),
6296 ('i', 'interactive', None,
6299 ('i', 'interactive', None,
6297 _('interactively select the changes (EXPERIMENTAL)')),
6300 _('interactively select the changes (EXPERIMENTAL)')),
6298 ] + walkopts + dryrunopts,
6301 ] + walkopts + dryrunopts,
6299 _('[OPTION]... [-r REV] [NAME]...'))
6302 _('[OPTION]... [-r REV] [NAME]...'))
6300 def revert(ui, repo, *pats, **opts):
6303 def revert(ui, repo, *pats, **opts):
6301 """restore files to their checkout state
6304 """restore files to their checkout state
6302
6305
6303 .. note::
6306 .. note::
6304
6307
6305 To check out earlier revisions, you should use :hg:`update REV`.
6308 To check out earlier revisions, you should use :hg:`update REV`.
6306 To cancel an uncommitted merge (and lose your changes),
6309 To cancel an uncommitted merge (and lose your changes),
6307 use :hg:`update --clean .`.
6310 use :hg:`update --clean .`.
6308
6311
6309 With no revision specified, revert the specified files or directories
6312 With no revision specified, revert the specified files or directories
6310 to the contents they had in the parent of the working directory.
6313 to the contents they had in the parent of the working directory.
6311 This restores the contents of files to an unmodified
6314 This restores the contents of files to an unmodified
6312 state and unschedules adds, removes, copies, and renames. If the
6315 state and unschedules adds, removes, copies, and renames. If the
6313 working directory has two parents, you must explicitly specify a
6316 working directory has two parents, you must explicitly specify a
6314 revision.
6317 revision.
6315
6318
6316 Using the -r/--rev or -d/--date options, revert the given files or
6319 Using the -r/--rev or -d/--date options, revert the given files or
6317 directories to their states as of a specific revision. Because
6320 directories to their states as of a specific revision. Because
6318 revert does not change the working directory parents, this will
6321 revert does not change the working directory parents, this will
6319 cause these files to appear modified. This can be helpful to "back
6322 cause these files to appear modified. This can be helpful to "back
6320 out" some or all of an earlier change. See :hg:`backout` for a
6323 out" some or all of an earlier change. See :hg:`backout` for a
6321 related method.
6324 related method.
6322
6325
6323 Modified files are saved with a .orig suffix before reverting.
6326 Modified files are saved with a .orig suffix before reverting.
6324 To disable these backups, use --no-backup. It is possible to store
6327 To disable these backups, use --no-backup. It is possible to store
6325 the backup files in a custom directory relative to the root of the
6328 the backup files in a custom directory relative to the root of the
6326 repository by setting the ``ui.origbackuppath`` configuration
6329 repository by setting the ``ui.origbackuppath`` configuration
6327 option.
6330 option.
6328
6331
6329 See :hg:`help dates` for a list of formats valid for -d/--date.
6332 See :hg:`help dates` for a list of formats valid for -d/--date.
6330
6333
6331 See :hg:`help backout` for a way to reverse the effect of an
6334 See :hg:`help backout` for a way to reverse the effect of an
6332 earlier changeset.
6335 earlier changeset.
6333
6336
6334 Returns 0 on success.
6337 Returns 0 on success.
6335 """
6338 """
6336
6339
6337 if opts.get("date"):
6340 if opts.get("date"):
6338 if opts.get("rev"):
6341 if opts.get("rev"):
6339 raise error.Abort(_("you can't specify a revision and a date"))
6342 raise error.Abort(_("you can't specify a revision and a date"))
6340 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
6343 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
6341
6344
6342 parent, p2 = repo.dirstate.parents()
6345 parent, p2 = repo.dirstate.parents()
6343 if not opts.get('rev') and p2 != nullid:
6346 if not opts.get('rev') and p2 != nullid:
6344 # revert after merge is a trap for new users (issue2915)
6347 # revert after merge is a trap for new users (issue2915)
6345 raise error.Abort(_('uncommitted merge with no revision specified'),
6348 raise error.Abort(_('uncommitted merge with no revision specified'),
6346 hint=_("use 'hg update' or see 'hg help revert'"))
6349 hint=_("use 'hg update' or see 'hg help revert'"))
6347
6350
6348 ctx = scmutil.revsingle(repo, opts.get('rev'))
6351 ctx = scmutil.revsingle(repo, opts.get('rev'))
6349
6352
6350 if (not (pats or opts.get('include') or opts.get('exclude') or
6353 if (not (pats or opts.get('include') or opts.get('exclude') or
6351 opts.get('all') or opts.get('interactive'))):
6354 opts.get('all') or opts.get('interactive'))):
6352 msg = _("no files or directories specified")
6355 msg = _("no files or directories specified")
6353 if p2 != nullid:
6356 if p2 != nullid:
6354 hint = _("uncommitted merge, use --all to discard all changes,"
6357 hint = _("uncommitted merge, use --all to discard all changes,"
6355 " or 'hg update -C .' to abort the merge")
6358 " or 'hg update -C .' to abort the merge")
6356 raise error.Abort(msg, hint=hint)
6359 raise error.Abort(msg, hint=hint)
6357 dirty = any(repo.status())
6360 dirty = any(repo.status())
6358 node = ctx.node()
6361 node = ctx.node()
6359 if node != parent:
6362 if node != parent:
6360 if dirty:
6363 if dirty:
6361 hint = _("uncommitted changes, use --all to discard all"
6364 hint = _("uncommitted changes, use --all to discard all"
6362 " changes, or 'hg update %s' to update") % ctx.rev()
6365 " changes, or 'hg update %s' to update") % ctx.rev()
6363 else:
6366 else:
6364 hint = _("use --all to revert all files,"
6367 hint = _("use --all to revert all files,"
6365 " or 'hg update %s' to update") % ctx.rev()
6368 " or 'hg update %s' to update") % ctx.rev()
6366 elif dirty:
6369 elif dirty:
6367 hint = _("uncommitted changes, use --all to discard all changes")
6370 hint = _("uncommitted changes, use --all to discard all changes")
6368 else:
6371 else:
6369 hint = _("use --all to revert all files")
6372 hint = _("use --all to revert all files")
6370 raise error.Abort(msg, hint=hint)
6373 raise error.Abort(msg, hint=hint)
6371
6374
6372 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
6375 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
6373
6376
6374 @command('rollback', dryrunopts +
6377 @command('rollback', dryrunopts +
6375 [('f', 'force', False, _('ignore safety measures'))])
6378 [('f', 'force', False, _('ignore safety measures'))])
6376 def rollback(ui, repo, **opts):
6379 def rollback(ui, repo, **opts):
6377 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6380 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6378
6381
6379 Please use :hg:`commit --amend` instead of rollback to correct
6382 Please use :hg:`commit --amend` instead of rollback to correct
6380 mistakes in the last commit.
6383 mistakes in the last commit.
6381
6384
6382 This command should be used with care. There is only one level of
6385 This command should be used with care. There is only one level of
6383 rollback, and there is no way to undo a rollback. It will also
6386 rollback, and there is no way to undo a rollback. It will also
6384 restore the dirstate at the time of the last transaction, losing
6387 restore the dirstate at the time of the last transaction, losing
6385 any dirstate changes since that time. This command does not alter
6388 any dirstate changes since that time. This command does not alter
6386 the working directory.
6389 the working directory.
6387
6390
6388 Transactions are used to encapsulate the effects of all commands
6391 Transactions are used to encapsulate the effects of all commands
6389 that create new changesets or propagate existing changesets into a
6392 that create new changesets or propagate existing changesets into a
6390 repository.
6393 repository.
6391
6394
6392 .. container:: verbose
6395 .. container:: verbose
6393
6396
6394 For example, the following commands are transactional, and their
6397 For example, the following commands are transactional, and their
6395 effects can be rolled back:
6398 effects can be rolled back:
6396
6399
6397 - commit
6400 - commit
6398 - import
6401 - import
6399 - pull
6402 - pull
6400 - push (with this repository as the destination)
6403 - push (with this repository as the destination)
6401 - unbundle
6404 - unbundle
6402
6405
6403 To avoid permanent data loss, rollback will refuse to rollback a
6406 To avoid permanent data loss, rollback will refuse to rollback a
6404 commit transaction if it isn't checked out. Use --force to
6407 commit transaction if it isn't checked out. Use --force to
6405 override this protection.
6408 override this protection.
6406
6409
6407 The rollback command can be entirely disabled by setting the
6410 The rollback command can be entirely disabled by setting the
6408 ``ui.rollback`` configuration setting to false. If you're here
6411 ``ui.rollback`` configuration setting to false. If you're here
6409 because you want to use rollback and it's disabled, you can
6412 because you want to use rollback and it's disabled, you can
6410 re-enable the command by setting ``ui.rollback`` to true.
6413 re-enable the command by setting ``ui.rollback`` to true.
6411
6414
6412 This command is not intended for use on public repositories. Once
6415 This command is not intended for use on public repositories. Once
6413 changes are visible for pull by other users, rolling a transaction
6416 changes are visible for pull by other users, rolling a transaction
6414 back locally is ineffective (someone else may already have pulled
6417 back locally is ineffective (someone else may already have pulled
6415 the changes). Furthermore, a race is possible with readers of the
6418 the changes). Furthermore, a race is possible with readers of the
6416 repository; for example an in-progress pull from the repository
6419 repository; for example an in-progress pull from the repository
6417 may fail if a rollback is performed.
6420 may fail if a rollback is performed.
6418
6421
6419 Returns 0 on success, 1 if no rollback data is available.
6422 Returns 0 on success, 1 if no rollback data is available.
6420 """
6423 """
6421 if not ui.configbool('ui', 'rollback', True):
6424 if not ui.configbool('ui', 'rollback', True):
6422 raise error.Abort(_('rollback is disabled because it is unsafe'),
6425 raise error.Abort(_('rollback is disabled because it is unsafe'),
6423 hint=('see `hg help -v rollback` for information'))
6426 hint=('see `hg help -v rollback` for information'))
6424 return repo.rollback(dryrun=opts.get('dry_run'),
6427 return repo.rollback(dryrun=opts.get('dry_run'),
6425 force=opts.get('force'))
6428 force=opts.get('force'))
6426
6429
6427 @command('root', [])
6430 @command('root', [])
6428 def root(ui, repo):
6431 def root(ui, repo):
6429 """print the root (top) of the current working directory
6432 """print the root (top) of the current working directory
6430
6433
6431 Print the root directory of the current repository.
6434 Print the root directory of the current repository.
6432
6435
6433 Returns 0 on success.
6436 Returns 0 on success.
6434 """
6437 """
6435 ui.write(repo.root + "\n")
6438 ui.write(repo.root + "\n")
6436
6439
6437 @command('^serve',
6440 @command('^serve',
6438 [('A', 'accesslog', '', _('name of access log file to write to'),
6441 [('A', 'accesslog', '', _('name of access log file to write to'),
6439 _('FILE')),
6442 _('FILE')),
6440 ('d', 'daemon', None, _('run server in background')),
6443 ('d', 'daemon', None, _('run server in background')),
6441 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
6444 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
6442 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
6445 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
6443 # use string type, then we can check if something was passed
6446 # use string type, then we can check if something was passed
6444 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
6447 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
6445 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
6448 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
6446 _('ADDR')),
6449 _('ADDR')),
6447 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
6450 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
6448 _('PREFIX')),
6451 _('PREFIX')),
6449 ('n', 'name', '',
6452 ('n', 'name', '',
6450 _('name to show in web pages (default: working directory)'), _('NAME')),
6453 _('name to show in web pages (default: working directory)'), _('NAME')),
6451 ('', 'web-conf', '',
6454 ('', 'web-conf', '',
6452 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
6455 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
6453 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
6456 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
6454 _('FILE')),
6457 _('FILE')),
6455 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
6458 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
6456 ('', 'stdio', None, _('for remote clients')),
6459 ('', 'stdio', None, _('for remote clients')),
6457 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
6460 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
6458 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
6461 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
6459 ('', 'style', '', _('template style to use'), _('STYLE')),
6462 ('', 'style', '', _('template style to use'), _('STYLE')),
6460 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
6463 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
6461 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
6464 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
6462 _('[OPTION]...'),
6465 _('[OPTION]...'),
6463 optionalrepo=True)
6466 optionalrepo=True)
6464 def serve(ui, repo, **opts):
6467 def serve(ui, repo, **opts):
6465 """start stand-alone webserver
6468 """start stand-alone webserver
6466
6469
6467 Start a local HTTP repository browser and pull server. You can use
6470 Start a local HTTP repository browser and pull server. You can use
6468 this for ad-hoc sharing and browsing of repositories. It is
6471 this for ad-hoc sharing and browsing of repositories. It is
6469 recommended to use a real web server to serve a repository for
6472 recommended to use a real web server to serve a repository for
6470 longer periods of time.
6473 longer periods of time.
6471
6474
6472 Please note that the server does not implement access control.
6475 Please note that the server does not implement access control.
6473 This means that, by default, anybody can read from the server and
6476 This means that, by default, anybody can read from the server and
6474 nobody can write to it by default. Set the ``web.allow_push``
6477 nobody can write to it by default. Set the ``web.allow_push``
6475 option to ``*`` to allow everybody to push to the server. You
6478 option to ``*`` to allow everybody to push to the server. You
6476 should use a real web server if you need to authenticate users.
6479 should use a real web server if you need to authenticate users.
6477
6480
6478 By default, the server logs accesses to stdout and errors to
6481 By default, the server logs accesses to stdout and errors to
6479 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6482 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6480 files.
6483 files.
6481
6484
6482 To have the server choose a free port number to listen on, specify
6485 To have the server choose a free port number to listen on, specify
6483 a port number of 0; in this case, the server will print the port
6486 a port number of 0; in this case, the server will print the port
6484 number it uses.
6487 number it uses.
6485
6488
6486 Returns 0 on success.
6489 Returns 0 on success.
6487 """
6490 """
6488
6491
6489 if opts["stdio"] and opts["cmdserver"]:
6492 if opts["stdio"] and opts["cmdserver"]:
6490 raise error.Abort(_("cannot use --stdio with --cmdserver"))
6493 raise error.Abort(_("cannot use --stdio with --cmdserver"))
6491
6494
6492 if opts["stdio"]:
6495 if opts["stdio"]:
6493 if repo is None:
6496 if repo is None:
6494 raise error.RepoError(_("there is no Mercurial repository here"
6497 raise error.RepoError(_("there is no Mercurial repository here"
6495 " (.hg not found)"))
6498 " (.hg not found)"))
6496 s = sshserver.sshserver(ui, repo)
6499 s = sshserver.sshserver(ui, repo)
6497 s.serve_forever()
6500 s.serve_forever()
6498
6501
6499 if opts["cmdserver"]:
6502 if opts["cmdserver"]:
6500 service = commandserver.createservice(ui, repo, opts)
6503 service = commandserver.createservice(ui, repo, opts)
6501 else:
6504 else:
6502 service = hgweb.createservice(ui, repo, opts)
6505 service = hgweb.createservice(ui, repo, opts)
6503 return cmdutil.service(opts, initfn=service.init, runfn=service.run)
6506 return cmdutil.service(opts, initfn=service.init, runfn=service.run)
6504
6507
6505 @command('^status|st',
6508 @command('^status|st',
6506 [('A', 'all', None, _('show status of all files')),
6509 [('A', 'all', None, _('show status of all files')),
6507 ('m', 'modified', None, _('show only modified files')),
6510 ('m', 'modified', None, _('show only modified files')),
6508 ('a', 'added', None, _('show only added files')),
6511 ('a', 'added', None, _('show only added files')),
6509 ('r', 'removed', None, _('show only removed files')),
6512 ('r', 'removed', None, _('show only removed files')),
6510 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
6513 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
6511 ('c', 'clean', None, _('show only files without changes')),
6514 ('c', 'clean', None, _('show only files without changes')),
6512 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
6515 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
6513 ('i', 'ignored', None, _('show only ignored files')),
6516 ('i', 'ignored', None, _('show only ignored files')),
6514 ('n', 'no-status', None, _('hide status prefix')),
6517 ('n', 'no-status', None, _('hide status prefix')),
6515 ('C', 'copies', None, _('show source of copied files')),
6518 ('C', 'copies', None, _('show source of copied files')),
6516 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
6519 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
6517 ('', 'rev', [], _('show difference from revision'), _('REV')),
6520 ('', 'rev', [], _('show difference from revision'), _('REV')),
6518 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
6521 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
6519 ] + walkopts + subrepoopts + formatteropts,
6522 ] + walkopts + subrepoopts + formatteropts,
6520 _('[OPTION]... [FILE]...'),
6523 _('[OPTION]... [FILE]...'),
6521 inferrepo=True)
6524 inferrepo=True)
6522 def status(ui, repo, *pats, **opts):
6525 def status(ui, repo, *pats, **opts):
6523 """show changed files in the working directory
6526 """show changed files in the working directory
6524
6527
6525 Show status of files in the repository. If names are given, only
6528 Show status of files in the repository. If names are given, only
6526 files that match are shown. Files that are clean or ignored or
6529 files that match are shown. Files that are clean or ignored or
6527 the source of a copy/move operation, are not listed unless
6530 the source of a copy/move operation, are not listed unless
6528 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6531 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6529 Unless options described with "show only ..." are given, the
6532 Unless options described with "show only ..." are given, the
6530 options -mardu are used.
6533 options -mardu are used.
6531
6534
6532 Option -q/--quiet hides untracked (unknown and ignored) files
6535 Option -q/--quiet hides untracked (unknown and ignored) files
6533 unless explicitly requested with -u/--unknown or -i/--ignored.
6536 unless explicitly requested with -u/--unknown or -i/--ignored.
6534
6537
6535 .. note::
6538 .. note::
6536
6539
6537 :hg:`status` may appear to disagree with diff if permissions have
6540 :hg:`status` may appear to disagree with diff if permissions have
6538 changed or a merge has occurred. The standard diff format does
6541 changed or a merge has occurred. The standard diff format does
6539 not report permission changes and diff only reports changes
6542 not report permission changes and diff only reports changes
6540 relative to one merge parent.
6543 relative to one merge parent.
6541
6544
6542 If one revision is given, it is used as the base revision.
6545 If one revision is given, it is used as the base revision.
6543 If two revisions are given, the differences between them are
6546 If two revisions are given, the differences between them are
6544 shown. The --change option can also be used as a shortcut to list
6547 shown. The --change option can also be used as a shortcut to list
6545 the changed files of a revision from its first parent.
6548 the changed files of a revision from its first parent.
6546
6549
6547 The codes used to show the status of files are::
6550 The codes used to show the status of files are::
6548
6551
6549 M = modified
6552 M = modified
6550 A = added
6553 A = added
6551 R = removed
6554 R = removed
6552 C = clean
6555 C = clean
6553 ! = missing (deleted by non-hg command, but still tracked)
6556 ! = missing (deleted by non-hg command, but still tracked)
6554 ? = not tracked
6557 ? = not tracked
6555 I = ignored
6558 I = ignored
6556 = origin of the previous file (with --copies)
6559 = origin of the previous file (with --copies)
6557
6560
6558 .. container:: verbose
6561 .. container:: verbose
6559
6562
6560 Examples:
6563 Examples:
6561
6564
6562 - show changes in the working directory relative to a
6565 - show changes in the working directory relative to a
6563 changeset::
6566 changeset::
6564
6567
6565 hg status --rev 9353
6568 hg status --rev 9353
6566
6569
6567 - show changes in the working directory relative to the
6570 - show changes in the working directory relative to the
6568 current directory (see :hg:`help patterns` for more information)::
6571 current directory (see :hg:`help patterns` for more information)::
6569
6572
6570 hg status re:
6573 hg status re:
6571
6574
6572 - show all changes including copies in an existing changeset::
6575 - show all changes including copies in an existing changeset::
6573
6576
6574 hg status --copies --change 9353
6577 hg status --copies --change 9353
6575
6578
6576 - get a NUL separated list of added files, suitable for xargs::
6579 - get a NUL separated list of added files, suitable for xargs::
6577
6580
6578 hg status -an0
6581 hg status -an0
6579
6582
6580 Returns 0 on success.
6583 Returns 0 on success.
6581 """
6584 """
6582
6585
6583 revs = opts.get('rev')
6586 revs = opts.get('rev')
6584 change = opts.get('change')
6587 change = opts.get('change')
6585
6588
6586 if revs and change:
6589 if revs and change:
6587 msg = _('cannot specify --rev and --change at the same time')
6590 msg = _('cannot specify --rev and --change at the same time')
6588 raise error.Abort(msg)
6591 raise error.Abort(msg)
6589 elif change:
6592 elif change:
6590 node2 = scmutil.revsingle(repo, change, None).node()
6593 node2 = scmutil.revsingle(repo, change, None).node()
6591 node1 = repo[node2].p1().node()
6594 node1 = repo[node2].p1().node()
6592 else:
6595 else:
6593 node1, node2 = scmutil.revpair(repo, revs)
6596 node1, node2 = scmutil.revpair(repo, revs)
6594
6597
6595 if pats:
6598 if pats:
6596 cwd = repo.getcwd()
6599 cwd = repo.getcwd()
6597 else:
6600 else:
6598 cwd = ''
6601 cwd = ''
6599
6602
6600 if opts.get('print0'):
6603 if opts.get('print0'):
6601 end = '\0'
6604 end = '\0'
6602 else:
6605 else:
6603 end = '\n'
6606 end = '\n'
6604 copy = {}
6607 copy = {}
6605 states = 'modified added removed deleted unknown ignored clean'.split()
6608 states = 'modified added removed deleted unknown ignored clean'.split()
6606 show = [k for k in states if opts.get(k)]
6609 show = [k for k in states if opts.get(k)]
6607 if opts.get('all'):
6610 if opts.get('all'):
6608 show += ui.quiet and (states[:4] + ['clean']) or states
6611 show += ui.quiet and (states[:4] + ['clean']) or states
6609 if not show:
6612 if not show:
6610 if ui.quiet:
6613 if ui.quiet:
6611 show = states[:4]
6614 show = states[:4]
6612 else:
6615 else:
6613 show = states[:5]
6616 show = states[:5]
6614
6617
6615 m = scmutil.match(repo[node2], pats, opts)
6618 m = scmutil.match(repo[node2], pats, opts)
6616 stat = repo.status(node1, node2, m,
6619 stat = repo.status(node1, node2, m,
6617 'ignored' in show, 'clean' in show, 'unknown' in show,
6620 'ignored' in show, 'clean' in show, 'unknown' in show,
6618 opts.get('subrepos'))
6621 opts.get('subrepos'))
6619 changestates = zip(states, 'MAR!?IC', stat)
6622 changestates = zip(states, 'MAR!?IC', stat)
6620
6623
6621 if (opts.get('all') or opts.get('copies')
6624 if (opts.get('all') or opts.get('copies')
6622 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
6625 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
6623 copy = copies.pathcopies(repo[node1], repo[node2], m)
6626 copy = copies.pathcopies(repo[node1], repo[node2], m)
6624
6627
6625 fm = ui.formatter('status', opts)
6628 fm = ui.formatter('status', opts)
6626 fmt = '%s' + end
6629 fmt = '%s' + end
6627 showchar = not opts.get('no_status')
6630 showchar = not opts.get('no_status')
6628
6631
6629 for state, char, files in changestates:
6632 for state, char, files in changestates:
6630 if state in show:
6633 if state in show:
6631 label = 'status.' + state
6634 label = 'status.' + state
6632 for f in files:
6635 for f in files:
6633 fm.startitem()
6636 fm.startitem()
6634 fm.condwrite(showchar, 'status', '%s ', char, label=label)
6637 fm.condwrite(showchar, 'status', '%s ', char, label=label)
6635 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
6638 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
6636 if f in copy:
6639 if f in copy:
6637 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
6640 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
6638 label='status.copied')
6641 label='status.copied')
6639 fm.end()
6642 fm.end()
6640
6643
6641 @command('^summary|sum',
6644 @command('^summary|sum',
6642 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
6645 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
6643 def summary(ui, repo, **opts):
6646 def summary(ui, repo, **opts):
6644 """summarize working directory state
6647 """summarize working directory state
6645
6648
6646 This generates a brief summary of the working directory state,
6649 This generates a brief summary of the working directory state,
6647 including parents, branch, commit status, phase and available updates.
6650 including parents, branch, commit status, phase and available updates.
6648
6651
6649 With the --remote option, this will check the default paths for
6652 With the --remote option, this will check the default paths for
6650 incoming and outgoing changes. This can be time-consuming.
6653 incoming and outgoing changes. This can be time-consuming.
6651
6654
6652 Returns 0 on success.
6655 Returns 0 on success.
6653 """
6656 """
6654
6657
6655 ctx = repo[None]
6658 ctx = repo[None]
6656 parents = ctx.parents()
6659 parents = ctx.parents()
6657 pnode = parents[0].node()
6660 pnode = parents[0].node()
6658 marks = []
6661 marks = []
6659
6662
6660 ms = None
6663 ms = None
6661 try:
6664 try:
6662 ms = mergemod.mergestate.read(repo)
6665 ms = mergemod.mergestate.read(repo)
6663 except error.UnsupportedMergeRecords as e:
6666 except error.UnsupportedMergeRecords as e:
6664 s = ' '.join(e.recordtypes)
6667 s = ' '.join(e.recordtypes)
6665 ui.warn(
6668 ui.warn(
6666 _('warning: merge state has unsupported record types: %s\n') % s)
6669 _('warning: merge state has unsupported record types: %s\n') % s)
6667 unresolved = 0
6670 unresolved = 0
6668 else:
6671 else:
6669 unresolved = [f for f in ms if ms[f] == 'u']
6672 unresolved = [f for f in ms if ms[f] == 'u']
6670
6673
6671 for p in parents:
6674 for p in parents:
6672 # label with log.changeset (instead of log.parent) since this
6675 # label with log.changeset (instead of log.parent) since this
6673 # shows a working directory parent *changeset*:
6676 # shows a working directory parent *changeset*:
6674 # i18n: column positioning for "hg summary"
6677 # i18n: column positioning for "hg summary"
6675 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
6678 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
6676 label='log.changeset changeset.%s' % p.phasestr())
6679 label='log.changeset changeset.%s' % p.phasestr())
6677 ui.write(' '.join(p.tags()), label='log.tag')
6680 ui.write(' '.join(p.tags()), label='log.tag')
6678 if p.bookmarks():
6681 if p.bookmarks():
6679 marks.extend(p.bookmarks())
6682 marks.extend(p.bookmarks())
6680 if p.rev() == -1:
6683 if p.rev() == -1:
6681 if not len(repo):
6684 if not len(repo):
6682 ui.write(_(' (empty repository)'))
6685 ui.write(_(' (empty repository)'))
6683 else:
6686 else:
6684 ui.write(_(' (no revision checked out)'))
6687 ui.write(_(' (no revision checked out)'))
6685 ui.write('\n')
6688 ui.write('\n')
6686 if p.description():
6689 if p.description():
6687 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
6690 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
6688 label='log.summary')
6691 label='log.summary')
6689
6692
6690 branch = ctx.branch()
6693 branch = ctx.branch()
6691 bheads = repo.branchheads(branch)
6694 bheads = repo.branchheads(branch)
6692 # i18n: column positioning for "hg summary"
6695 # i18n: column positioning for "hg summary"
6693 m = _('branch: %s\n') % branch
6696 m = _('branch: %s\n') % branch
6694 if branch != 'default':
6697 if branch != 'default':
6695 ui.write(m, label='log.branch')
6698 ui.write(m, label='log.branch')
6696 else:
6699 else:
6697 ui.status(m, label='log.branch')
6700 ui.status(m, label='log.branch')
6698
6701
6699 if marks:
6702 if marks:
6700 active = repo._activebookmark
6703 active = repo._activebookmark
6701 # i18n: column positioning for "hg summary"
6704 # i18n: column positioning for "hg summary"
6702 ui.write(_('bookmarks:'), label='log.bookmark')
6705 ui.write(_('bookmarks:'), label='log.bookmark')
6703 if active is not None:
6706 if active is not None:
6704 if active in marks:
6707 if active in marks:
6705 ui.write(' *' + active, label=activebookmarklabel)
6708 ui.write(' *' + active, label=activebookmarklabel)
6706 marks.remove(active)
6709 marks.remove(active)
6707 else:
6710 else:
6708 ui.write(' [%s]' % active, label=activebookmarklabel)
6711 ui.write(' [%s]' % active, label=activebookmarklabel)
6709 for m in marks:
6712 for m in marks:
6710 ui.write(' ' + m, label='log.bookmark')
6713 ui.write(' ' + m, label='log.bookmark')
6711 ui.write('\n', label='log.bookmark')
6714 ui.write('\n', label='log.bookmark')
6712
6715
6713 status = repo.status(unknown=True)
6716 status = repo.status(unknown=True)
6714
6717
6715 c = repo.dirstate.copies()
6718 c = repo.dirstate.copies()
6716 copied, renamed = [], []
6719 copied, renamed = [], []
6717 for d, s in c.iteritems():
6720 for d, s in c.iteritems():
6718 if s in status.removed:
6721 if s in status.removed:
6719 status.removed.remove(s)
6722 status.removed.remove(s)
6720 renamed.append(d)
6723 renamed.append(d)
6721 else:
6724 else:
6722 copied.append(d)
6725 copied.append(d)
6723 if d in status.added:
6726 if d in status.added:
6724 status.added.remove(d)
6727 status.added.remove(d)
6725
6728
6726 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6729 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6727
6730
6728 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
6731 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
6729 (ui.label(_('%d added'), 'status.added'), status.added),
6732 (ui.label(_('%d added'), 'status.added'), status.added),
6730 (ui.label(_('%d removed'), 'status.removed'), status.removed),
6733 (ui.label(_('%d removed'), 'status.removed'), status.removed),
6731 (ui.label(_('%d renamed'), 'status.copied'), renamed),
6734 (ui.label(_('%d renamed'), 'status.copied'), renamed),
6732 (ui.label(_('%d copied'), 'status.copied'), copied),
6735 (ui.label(_('%d copied'), 'status.copied'), copied),
6733 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
6736 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
6734 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
6737 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
6735 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
6738 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
6736 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
6739 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
6737 t = []
6740 t = []
6738 for l, s in labels:
6741 for l, s in labels:
6739 if s:
6742 if s:
6740 t.append(l % len(s))
6743 t.append(l % len(s))
6741
6744
6742 t = ', '.join(t)
6745 t = ', '.join(t)
6743 cleanworkdir = False
6746 cleanworkdir = False
6744
6747
6745 if repo.vfs.exists('graftstate'):
6748 if repo.vfs.exists('graftstate'):
6746 t += _(' (graft in progress)')
6749 t += _(' (graft in progress)')
6747 if repo.vfs.exists('updatestate'):
6750 if repo.vfs.exists('updatestate'):
6748 t += _(' (interrupted update)')
6751 t += _(' (interrupted update)')
6749 elif len(parents) > 1:
6752 elif len(parents) > 1:
6750 t += _(' (merge)')
6753 t += _(' (merge)')
6751 elif branch != parents[0].branch():
6754 elif branch != parents[0].branch():
6752 t += _(' (new branch)')
6755 t += _(' (new branch)')
6753 elif (parents[0].closesbranch() and
6756 elif (parents[0].closesbranch() and
6754 pnode in repo.branchheads(branch, closed=True)):
6757 pnode in repo.branchheads(branch, closed=True)):
6755 t += _(' (head closed)')
6758 t += _(' (head closed)')
6756 elif not (status.modified or status.added or status.removed or renamed or
6759 elif not (status.modified or status.added or status.removed or renamed or
6757 copied or subs):
6760 copied or subs):
6758 t += _(' (clean)')
6761 t += _(' (clean)')
6759 cleanworkdir = True
6762 cleanworkdir = True
6760 elif pnode not in bheads:
6763 elif pnode not in bheads:
6761 t += _(' (new branch head)')
6764 t += _(' (new branch head)')
6762
6765
6763 if parents:
6766 if parents:
6764 pendingphase = max(p.phase() for p in parents)
6767 pendingphase = max(p.phase() for p in parents)
6765 else:
6768 else:
6766 pendingphase = phases.public
6769 pendingphase = phases.public
6767
6770
6768 if pendingphase > phases.newcommitphase(ui):
6771 if pendingphase > phases.newcommitphase(ui):
6769 t += ' (%s)' % phases.phasenames[pendingphase]
6772 t += ' (%s)' % phases.phasenames[pendingphase]
6770
6773
6771 if cleanworkdir:
6774 if cleanworkdir:
6772 # i18n: column positioning for "hg summary"
6775 # i18n: column positioning for "hg summary"
6773 ui.status(_('commit: %s\n') % t.strip())
6776 ui.status(_('commit: %s\n') % t.strip())
6774 else:
6777 else:
6775 # i18n: column positioning for "hg summary"
6778 # i18n: column positioning for "hg summary"
6776 ui.write(_('commit: %s\n') % t.strip())
6779 ui.write(_('commit: %s\n') % t.strip())
6777
6780
6778 # all ancestors of branch heads - all ancestors of parent = new csets
6781 # all ancestors of branch heads - all ancestors of parent = new csets
6779 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
6782 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
6780 bheads))
6783 bheads))
6781
6784
6782 if new == 0:
6785 if new == 0:
6783 # i18n: column positioning for "hg summary"
6786 # i18n: column positioning for "hg summary"
6784 ui.status(_('update: (current)\n'))
6787 ui.status(_('update: (current)\n'))
6785 elif pnode not in bheads:
6788 elif pnode not in bheads:
6786 # i18n: column positioning for "hg summary"
6789 # i18n: column positioning for "hg summary"
6787 ui.write(_('update: %d new changesets (update)\n') % new)
6790 ui.write(_('update: %d new changesets (update)\n') % new)
6788 else:
6791 else:
6789 # i18n: column positioning for "hg summary"
6792 # i18n: column positioning for "hg summary"
6790 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
6793 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
6791 (new, len(bheads)))
6794 (new, len(bheads)))
6792
6795
6793 t = []
6796 t = []
6794 draft = len(repo.revs('draft()'))
6797 draft = len(repo.revs('draft()'))
6795 if draft:
6798 if draft:
6796 t.append(_('%d draft') % draft)
6799 t.append(_('%d draft') % draft)
6797 secret = len(repo.revs('secret()'))
6800 secret = len(repo.revs('secret()'))
6798 if secret:
6801 if secret:
6799 t.append(_('%d secret') % secret)
6802 t.append(_('%d secret') % secret)
6800
6803
6801 if draft or secret:
6804 if draft or secret:
6802 ui.status(_('phases: %s\n') % ', '.join(t))
6805 ui.status(_('phases: %s\n') % ', '.join(t))
6803
6806
6804 if obsolete.isenabled(repo, obsolete.createmarkersopt):
6807 if obsolete.isenabled(repo, obsolete.createmarkersopt):
6805 for trouble in ("unstable", "divergent", "bumped"):
6808 for trouble in ("unstable", "divergent", "bumped"):
6806 numtrouble = len(repo.revs(trouble + "()"))
6809 numtrouble = len(repo.revs(trouble + "()"))
6807 # We write all the possibilities to ease translation
6810 # We write all the possibilities to ease translation
6808 troublemsg = {
6811 troublemsg = {
6809 "unstable": _("unstable: %d changesets"),
6812 "unstable": _("unstable: %d changesets"),
6810 "divergent": _("divergent: %d changesets"),
6813 "divergent": _("divergent: %d changesets"),
6811 "bumped": _("bumped: %d changesets"),
6814 "bumped": _("bumped: %d changesets"),
6812 }
6815 }
6813 if numtrouble > 0:
6816 if numtrouble > 0:
6814 ui.status(troublemsg[trouble] % numtrouble + "\n")
6817 ui.status(troublemsg[trouble] % numtrouble + "\n")
6815
6818
6816 cmdutil.summaryhooks(ui, repo)
6819 cmdutil.summaryhooks(ui, repo)
6817
6820
6818 if opts.get('remote'):
6821 if opts.get('remote'):
6819 needsincoming, needsoutgoing = True, True
6822 needsincoming, needsoutgoing = True, True
6820 else:
6823 else:
6821 needsincoming, needsoutgoing = False, False
6824 needsincoming, needsoutgoing = False, False
6822 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
6825 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
6823 if i:
6826 if i:
6824 needsincoming = True
6827 needsincoming = True
6825 if o:
6828 if o:
6826 needsoutgoing = True
6829 needsoutgoing = True
6827 if not needsincoming and not needsoutgoing:
6830 if not needsincoming and not needsoutgoing:
6828 return
6831 return
6829
6832
6830 def getincoming():
6833 def getincoming():
6831 source, branches = hg.parseurl(ui.expandpath('default'))
6834 source, branches = hg.parseurl(ui.expandpath('default'))
6832 sbranch = branches[0]
6835 sbranch = branches[0]
6833 try:
6836 try:
6834 other = hg.peer(repo, {}, source)
6837 other = hg.peer(repo, {}, source)
6835 except error.RepoError:
6838 except error.RepoError:
6836 if opts.get('remote'):
6839 if opts.get('remote'):
6837 raise
6840 raise
6838 return source, sbranch, None, None, None
6841 return source, sbranch, None, None, None
6839 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6842 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6840 if revs:
6843 if revs:
6841 revs = [other.lookup(rev) for rev in revs]
6844 revs = [other.lookup(rev) for rev in revs]
6842 ui.debug('comparing with %s\n' % util.hidepassword(source))
6845 ui.debug('comparing with %s\n' % util.hidepassword(source))
6843 repo.ui.pushbuffer()
6846 repo.ui.pushbuffer()
6844 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6847 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6845 repo.ui.popbuffer()
6848 repo.ui.popbuffer()
6846 return source, sbranch, other, commoninc, commoninc[1]
6849 return source, sbranch, other, commoninc, commoninc[1]
6847
6850
6848 if needsincoming:
6851 if needsincoming:
6849 source, sbranch, sother, commoninc, incoming = getincoming()
6852 source, sbranch, sother, commoninc, incoming = getincoming()
6850 else:
6853 else:
6851 source = sbranch = sother = commoninc = incoming = None
6854 source = sbranch = sother = commoninc = incoming = None
6852
6855
6853 def getoutgoing():
6856 def getoutgoing():
6854 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
6857 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
6855 dbranch = branches[0]
6858 dbranch = branches[0]
6856 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
6859 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
6857 if source != dest:
6860 if source != dest:
6858 try:
6861 try:
6859 dother = hg.peer(repo, {}, dest)
6862 dother = hg.peer(repo, {}, dest)
6860 except error.RepoError:
6863 except error.RepoError:
6861 if opts.get('remote'):
6864 if opts.get('remote'):
6862 raise
6865 raise
6863 return dest, dbranch, None, None
6866 return dest, dbranch, None, None
6864 ui.debug('comparing with %s\n' % util.hidepassword(dest))
6867 ui.debug('comparing with %s\n' % util.hidepassword(dest))
6865 elif sother is None:
6868 elif sother is None:
6866 # there is no explicit destination peer, but source one is invalid
6869 # there is no explicit destination peer, but source one is invalid
6867 return dest, dbranch, None, None
6870 return dest, dbranch, None, None
6868 else:
6871 else:
6869 dother = sother
6872 dother = sother
6870 if (source != dest or (sbranch is not None and sbranch != dbranch)):
6873 if (source != dest or (sbranch is not None and sbranch != dbranch)):
6871 common = None
6874 common = None
6872 else:
6875 else:
6873 common = commoninc
6876 common = commoninc
6874 if revs:
6877 if revs:
6875 revs = [repo.lookup(rev) for rev in revs]
6878 revs = [repo.lookup(rev) for rev in revs]
6876 repo.ui.pushbuffer()
6879 repo.ui.pushbuffer()
6877 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
6880 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
6878 commoninc=common)
6881 commoninc=common)
6879 repo.ui.popbuffer()
6882 repo.ui.popbuffer()
6880 return dest, dbranch, dother, outgoing
6883 return dest, dbranch, dother, outgoing
6881
6884
6882 if needsoutgoing:
6885 if needsoutgoing:
6883 dest, dbranch, dother, outgoing = getoutgoing()
6886 dest, dbranch, dother, outgoing = getoutgoing()
6884 else:
6887 else:
6885 dest = dbranch = dother = outgoing = None
6888 dest = dbranch = dother = outgoing = None
6886
6889
6887 if opts.get('remote'):
6890 if opts.get('remote'):
6888 t = []
6891 t = []
6889 if incoming:
6892 if incoming:
6890 t.append(_('1 or more incoming'))
6893 t.append(_('1 or more incoming'))
6891 o = outgoing.missing
6894 o = outgoing.missing
6892 if o:
6895 if o:
6893 t.append(_('%d outgoing') % len(o))
6896 t.append(_('%d outgoing') % len(o))
6894 other = dother or sother
6897 other = dother or sother
6895 if 'bookmarks' in other.listkeys('namespaces'):
6898 if 'bookmarks' in other.listkeys('namespaces'):
6896 counts = bookmarks.summary(repo, other)
6899 counts = bookmarks.summary(repo, other)
6897 if counts[0] > 0:
6900 if counts[0] > 0:
6898 t.append(_('%d incoming bookmarks') % counts[0])
6901 t.append(_('%d incoming bookmarks') % counts[0])
6899 if counts[1] > 0:
6902 if counts[1] > 0:
6900 t.append(_('%d outgoing bookmarks') % counts[1])
6903 t.append(_('%d outgoing bookmarks') % counts[1])
6901
6904
6902 if t:
6905 if t:
6903 # i18n: column positioning for "hg summary"
6906 # i18n: column positioning for "hg summary"
6904 ui.write(_('remote: %s\n') % (', '.join(t)))
6907 ui.write(_('remote: %s\n') % (', '.join(t)))
6905 else:
6908 else:
6906 # i18n: column positioning for "hg summary"
6909 # i18n: column positioning for "hg summary"
6907 ui.status(_('remote: (synced)\n'))
6910 ui.status(_('remote: (synced)\n'))
6908
6911
6909 cmdutil.summaryremotehooks(ui, repo, opts,
6912 cmdutil.summaryremotehooks(ui, repo, opts,
6910 ((source, sbranch, sother, commoninc),
6913 ((source, sbranch, sother, commoninc),
6911 (dest, dbranch, dother, outgoing)))
6914 (dest, dbranch, dother, outgoing)))
6912
6915
6913 @command('tag',
6916 @command('tag',
6914 [('f', 'force', None, _('force tag')),
6917 [('f', 'force', None, _('force tag')),
6915 ('l', 'local', None, _('make the tag local')),
6918 ('l', 'local', None, _('make the tag local')),
6916 ('r', 'rev', '', _('revision to tag'), _('REV')),
6919 ('r', 'rev', '', _('revision to tag'), _('REV')),
6917 ('', 'remove', None, _('remove a tag')),
6920 ('', 'remove', None, _('remove a tag')),
6918 # -l/--local is already there, commitopts cannot be used
6921 # -l/--local is already there, commitopts cannot be used
6919 ('e', 'edit', None, _('invoke editor on commit messages')),
6922 ('e', 'edit', None, _('invoke editor on commit messages')),
6920 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6923 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6921 ] + commitopts2,
6924 ] + commitopts2,
6922 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6925 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6923 def tag(ui, repo, name1, *names, **opts):
6926 def tag(ui, repo, name1, *names, **opts):
6924 """add one or more tags for the current or given revision
6927 """add one or more tags for the current or given revision
6925
6928
6926 Name a particular revision using <name>.
6929 Name a particular revision using <name>.
6927
6930
6928 Tags are used to name particular revisions of the repository and are
6931 Tags are used to name particular revisions of the repository and are
6929 very useful to compare different revisions, to go back to significant
6932 very useful to compare different revisions, to go back to significant
6930 earlier versions or to mark branch points as releases, etc. Changing
6933 earlier versions or to mark branch points as releases, etc. Changing
6931 an existing tag is normally disallowed; use -f/--force to override.
6934 an existing tag is normally disallowed; use -f/--force to override.
6932
6935
6933 If no revision is given, the parent of the working directory is
6936 If no revision is given, the parent of the working directory is
6934 used.
6937 used.
6935
6938
6936 To facilitate version control, distribution, and merging of tags,
6939 To facilitate version control, distribution, and merging of tags,
6937 they are stored as a file named ".hgtags" which is managed similarly
6940 they are stored as a file named ".hgtags" which is managed similarly
6938 to other project files and can be hand-edited if necessary. This
6941 to other project files and can be hand-edited if necessary. This
6939 also means that tagging creates a new commit. The file
6942 also means that tagging creates a new commit. The file
6940 ".hg/localtags" is used for local tags (not shared among
6943 ".hg/localtags" is used for local tags (not shared among
6941 repositories).
6944 repositories).
6942
6945
6943 Tag commits are usually made at the head of a branch. If the parent
6946 Tag commits are usually made at the head of a branch. If the parent
6944 of the working directory is not a branch head, :hg:`tag` aborts; use
6947 of the working directory is not a branch head, :hg:`tag` aborts; use
6945 -f/--force to force the tag commit to be based on a non-head
6948 -f/--force to force the tag commit to be based on a non-head
6946 changeset.
6949 changeset.
6947
6950
6948 See :hg:`help dates` for a list of formats valid for -d/--date.
6951 See :hg:`help dates` for a list of formats valid for -d/--date.
6949
6952
6950 Since tag names have priority over branch names during revision
6953 Since tag names have priority over branch names during revision
6951 lookup, using an existing branch name as a tag name is discouraged.
6954 lookup, using an existing branch name as a tag name is discouraged.
6952
6955
6953 Returns 0 on success.
6956 Returns 0 on success.
6954 """
6957 """
6955 wlock = lock = None
6958 wlock = lock = None
6956 try:
6959 try:
6957 wlock = repo.wlock()
6960 wlock = repo.wlock()
6958 lock = repo.lock()
6961 lock = repo.lock()
6959 rev_ = "."
6962 rev_ = "."
6960 names = [t.strip() for t in (name1,) + names]
6963 names = [t.strip() for t in (name1,) + names]
6961 if len(names) != len(set(names)):
6964 if len(names) != len(set(names)):
6962 raise error.Abort(_('tag names must be unique'))
6965 raise error.Abort(_('tag names must be unique'))
6963 for n in names:
6966 for n in names:
6964 scmutil.checknewlabel(repo, n, 'tag')
6967 scmutil.checknewlabel(repo, n, 'tag')
6965 if not n:
6968 if not n:
6966 raise error.Abort(_('tag names cannot consist entirely of '
6969 raise error.Abort(_('tag names cannot consist entirely of '
6967 'whitespace'))
6970 'whitespace'))
6968 if opts.get('rev') and opts.get('remove'):
6971 if opts.get('rev') and opts.get('remove'):
6969 raise error.Abort(_("--rev and --remove are incompatible"))
6972 raise error.Abort(_("--rev and --remove are incompatible"))
6970 if opts.get('rev'):
6973 if opts.get('rev'):
6971 rev_ = opts['rev']
6974 rev_ = opts['rev']
6972 message = opts.get('message')
6975 message = opts.get('message')
6973 if opts.get('remove'):
6976 if opts.get('remove'):
6974 if opts.get('local'):
6977 if opts.get('local'):
6975 expectedtype = 'local'
6978 expectedtype = 'local'
6976 else:
6979 else:
6977 expectedtype = 'global'
6980 expectedtype = 'global'
6978
6981
6979 for n in names:
6982 for n in names:
6980 if not repo.tagtype(n):
6983 if not repo.tagtype(n):
6981 raise error.Abort(_("tag '%s' does not exist") % n)
6984 raise error.Abort(_("tag '%s' does not exist") % n)
6982 if repo.tagtype(n) != expectedtype:
6985 if repo.tagtype(n) != expectedtype:
6983 if expectedtype == 'global':
6986 if expectedtype == 'global':
6984 raise error.Abort(_("tag '%s' is not a global tag") % n)
6987 raise error.Abort(_("tag '%s' is not a global tag") % n)
6985 else:
6988 else:
6986 raise error.Abort(_("tag '%s' is not a local tag") % n)
6989 raise error.Abort(_("tag '%s' is not a local tag") % n)
6987 rev_ = 'null'
6990 rev_ = 'null'
6988 if not message:
6991 if not message:
6989 # we don't translate commit messages
6992 # we don't translate commit messages
6990 message = 'Removed tag %s' % ', '.join(names)
6993 message = 'Removed tag %s' % ', '.join(names)
6991 elif not opts.get('force'):
6994 elif not opts.get('force'):
6992 for n in names:
6995 for n in names:
6993 if n in repo.tags():
6996 if n in repo.tags():
6994 raise error.Abort(_("tag '%s' already exists "
6997 raise error.Abort(_("tag '%s' already exists "
6995 "(use -f to force)") % n)
6998 "(use -f to force)") % n)
6996 if not opts.get('local'):
6999 if not opts.get('local'):
6997 p1, p2 = repo.dirstate.parents()
7000 p1, p2 = repo.dirstate.parents()
6998 if p2 != nullid:
7001 if p2 != nullid:
6999 raise error.Abort(_('uncommitted merge'))
7002 raise error.Abort(_('uncommitted merge'))
7000 bheads = repo.branchheads()
7003 bheads = repo.branchheads()
7001 if not opts.get('force') and bheads and p1 not in bheads:
7004 if not opts.get('force') and bheads and p1 not in bheads:
7002 raise error.Abort(_('not at a branch head (use -f to force)'))
7005 raise error.Abort(_('not at a branch head (use -f to force)'))
7003 r = scmutil.revsingle(repo, rev_).node()
7006 r = scmutil.revsingle(repo, rev_).node()
7004
7007
7005 if not message:
7008 if not message:
7006 # we don't translate commit messages
7009 # we don't translate commit messages
7007 message = ('Added tag %s for changeset %s' %
7010 message = ('Added tag %s for changeset %s' %
7008 (', '.join(names), short(r)))
7011 (', '.join(names), short(r)))
7009
7012
7010 date = opts.get('date')
7013 date = opts.get('date')
7011 if date:
7014 if date:
7012 date = util.parsedate(date)
7015 date = util.parsedate(date)
7013
7016
7014 if opts.get('remove'):
7017 if opts.get('remove'):
7015 editform = 'tag.remove'
7018 editform = 'tag.remove'
7016 else:
7019 else:
7017 editform = 'tag.add'
7020 editform = 'tag.add'
7018 editor = cmdutil.getcommiteditor(editform=editform, **opts)
7021 editor = cmdutil.getcommiteditor(editform=editform, **opts)
7019
7022
7020 # don't allow tagging the null rev
7023 # don't allow tagging the null rev
7021 if (not opts.get('remove') and
7024 if (not opts.get('remove') and
7022 scmutil.revsingle(repo, rev_).rev() == nullrev):
7025 scmutil.revsingle(repo, rev_).rev() == nullrev):
7023 raise error.Abort(_("cannot tag null revision"))
7026 raise error.Abort(_("cannot tag null revision"))
7024
7027
7025 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
7028 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
7026 editor=editor)
7029 editor=editor)
7027 finally:
7030 finally:
7028 release(lock, wlock)
7031 release(lock, wlock)
7029
7032
7030 @command('tags', formatteropts, '')
7033 @command('tags', formatteropts, '')
7031 def tags(ui, repo, **opts):
7034 def tags(ui, repo, **opts):
7032 """list repository tags
7035 """list repository tags
7033
7036
7034 This lists both regular and local tags. When the -v/--verbose
7037 This lists both regular and local tags. When the -v/--verbose
7035 switch is used, a third column "local" is printed for local tags.
7038 switch is used, a third column "local" is printed for local tags.
7036 When the -q/--quiet switch is used, only the tag name is printed.
7039 When the -q/--quiet switch is used, only the tag name is printed.
7037
7040
7038 Returns 0 on success.
7041 Returns 0 on success.
7039 """
7042 """
7040
7043
7041 fm = ui.formatter('tags', opts)
7044 fm = ui.formatter('tags', opts)
7042 hexfunc = fm.hexfunc
7045 hexfunc = fm.hexfunc
7043 tagtype = ""
7046 tagtype = ""
7044
7047
7045 for t, n in reversed(repo.tagslist()):
7048 for t, n in reversed(repo.tagslist()):
7046 hn = hexfunc(n)
7049 hn = hexfunc(n)
7047 label = 'tags.normal'
7050 label = 'tags.normal'
7048 tagtype = ''
7051 tagtype = ''
7049 if repo.tagtype(t) == 'local':
7052 if repo.tagtype(t) == 'local':
7050 label = 'tags.local'
7053 label = 'tags.local'
7051 tagtype = 'local'
7054 tagtype = 'local'
7052
7055
7053 fm.startitem()
7056 fm.startitem()
7054 fm.write('tag', '%s', t, label=label)
7057 fm.write('tag', '%s', t, label=label)
7055 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
7058 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
7056 fm.condwrite(not ui.quiet, 'rev node', fmt,
7059 fm.condwrite(not ui.quiet, 'rev node', fmt,
7057 repo.changelog.rev(n), hn, label=label)
7060 repo.changelog.rev(n), hn, label=label)
7058 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
7061 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
7059 tagtype, label=label)
7062 tagtype, label=label)
7060 fm.plain('\n')
7063 fm.plain('\n')
7061 fm.end()
7064 fm.end()
7062
7065
7063 @command('tip',
7066 @command('tip',
7064 [('p', 'patch', None, _('show patch')),
7067 [('p', 'patch', None, _('show patch')),
7065 ('g', 'git', None, _('use git extended diff format')),
7068 ('g', 'git', None, _('use git extended diff format')),
7066 ] + templateopts,
7069 ] + templateopts,
7067 _('[-p] [-g]'))
7070 _('[-p] [-g]'))
7068 def tip(ui, repo, **opts):
7071 def tip(ui, repo, **opts):
7069 """show the tip revision (DEPRECATED)
7072 """show the tip revision (DEPRECATED)
7070
7073
7071 The tip revision (usually just called the tip) is the changeset
7074 The tip revision (usually just called the tip) is the changeset
7072 most recently added to the repository (and therefore the most
7075 most recently added to the repository (and therefore the most
7073 recently changed head).
7076 recently changed head).
7074
7077
7075 If you have just made a commit, that commit will be the tip. If
7078 If you have just made a commit, that commit will be the tip. If
7076 you have just pulled changes from another repository, the tip of
7079 you have just pulled changes from another repository, the tip of
7077 that repository becomes the current tip. The "tip" tag is special
7080 that repository becomes the current tip. The "tip" tag is special
7078 and cannot be renamed or assigned to a different changeset.
7081 and cannot be renamed or assigned to a different changeset.
7079
7082
7080 This command is deprecated, please use :hg:`heads` instead.
7083 This command is deprecated, please use :hg:`heads` instead.
7081
7084
7082 Returns 0 on success.
7085 Returns 0 on success.
7083 """
7086 """
7084 displayer = cmdutil.show_changeset(ui, repo, opts)
7087 displayer = cmdutil.show_changeset(ui, repo, opts)
7085 displayer.show(repo['tip'])
7088 displayer.show(repo['tip'])
7086 displayer.close()
7089 displayer.close()
7087
7090
7088 @command('unbundle',
7091 @command('unbundle',
7089 [('u', 'update', None,
7092 [('u', 'update', None,
7090 _('update to new branch head if changesets were unbundled'))],
7093 _('update to new branch head if changesets were unbundled'))],
7091 _('[-u] FILE...'))
7094 _('[-u] FILE...'))
7092 def unbundle(ui, repo, fname1, *fnames, **opts):
7095 def unbundle(ui, repo, fname1, *fnames, **opts):
7093 """apply one or more changegroup files
7096 """apply one or more changegroup files
7094
7097
7095 Apply one or more compressed changegroup files generated by the
7098 Apply one or more compressed changegroup files generated by the
7096 bundle command.
7099 bundle command.
7097
7100
7098 Returns 0 on success, 1 if an update has unresolved files.
7101 Returns 0 on success, 1 if an update has unresolved files.
7099 """
7102 """
7100 fnames = (fname1,) + fnames
7103 fnames = (fname1,) + fnames
7101
7104
7102 with repo.lock():
7105 with repo.lock():
7103 for fname in fnames:
7106 for fname in fnames:
7104 f = hg.openpath(ui, fname)
7107 f = hg.openpath(ui, fname)
7105 gen = exchange.readbundle(ui, f, fname)
7108 gen = exchange.readbundle(ui, f, fname)
7106 if isinstance(gen, bundle2.unbundle20):
7109 if isinstance(gen, bundle2.unbundle20):
7107 tr = repo.transaction('unbundle')
7110 tr = repo.transaction('unbundle')
7108 try:
7111 try:
7109 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
7112 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
7110 url='bundle:' + fname)
7113 url='bundle:' + fname)
7111 tr.close()
7114 tr.close()
7112 except error.BundleUnknownFeatureError as exc:
7115 except error.BundleUnknownFeatureError as exc:
7113 raise error.Abort(_('%s: unknown bundle feature, %s')
7116 raise error.Abort(_('%s: unknown bundle feature, %s')
7114 % (fname, exc),
7117 % (fname, exc),
7115 hint=_("see https://mercurial-scm.org/"
7118 hint=_("see https://mercurial-scm.org/"
7116 "wiki/BundleFeature for more "
7119 "wiki/BundleFeature for more "
7117 "information"))
7120 "information"))
7118 finally:
7121 finally:
7119 if tr:
7122 if tr:
7120 tr.release()
7123 tr.release()
7121 changes = [r.get('return', 0)
7124 changes = [r.get('return', 0)
7122 for r in op.records['changegroup']]
7125 for r in op.records['changegroup']]
7123 modheads = changegroup.combineresults(changes)
7126 modheads = changegroup.combineresults(changes)
7124 elif isinstance(gen, streamclone.streamcloneapplier):
7127 elif isinstance(gen, streamclone.streamcloneapplier):
7125 raise error.Abort(
7128 raise error.Abort(
7126 _('packed bundles cannot be applied with '
7129 _('packed bundles cannot be applied with '
7127 '"hg unbundle"'),
7130 '"hg unbundle"'),
7128 hint=_('use "hg debugapplystreamclonebundle"'))
7131 hint=_('use "hg debugapplystreamclonebundle"'))
7129 else:
7132 else:
7130 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
7133 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
7131
7134
7132 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7135 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7133
7136
7134 @command('^update|up|checkout|co',
7137 @command('^update|up|checkout|co',
7135 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
7138 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
7136 ('c', 'check', None, _('require clean working directory')),
7139 ('c', 'check', None, _('require clean working directory')),
7137 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
7140 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
7138 ('r', 'rev', '', _('revision'), _('REV'))
7141 ('r', 'rev', '', _('revision'), _('REV'))
7139 ] + mergetoolopts,
7142 ] + mergetoolopts,
7140 _('[-c] [-C] [-d DATE] [[-r] REV]'))
7143 _('[-c] [-C] [-d DATE] [[-r] REV]'))
7141 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
7144 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
7142 tool=None):
7145 tool=None):
7143 """update working directory (or switch revisions)
7146 """update working directory (or switch revisions)
7144
7147
7145 Update the repository's working directory to the specified
7148 Update the repository's working directory to the specified
7146 changeset. If no changeset is specified, update to the tip of the
7149 changeset. If no changeset is specified, update to the tip of the
7147 current named branch and move the active bookmark (see :hg:`help
7150 current named branch and move the active bookmark (see :hg:`help
7148 bookmarks`).
7151 bookmarks`).
7149
7152
7150 Update sets the working directory's parent revision to the specified
7153 Update sets the working directory's parent revision to the specified
7151 changeset (see :hg:`help parents`).
7154 changeset (see :hg:`help parents`).
7152
7155
7153 If the changeset is not a descendant or ancestor of the working
7156 If the changeset is not a descendant or ancestor of the working
7154 directory's parent, the update is aborted. With the -c/--check
7157 directory's parent, the update is aborted. With the -c/--check
7155 option, the working directory is checked for uncommitted changes; if
7158 option, the working directory is checked for uncommitted changes; if
7156 none are found, the working directory is updated to the specified
7159 none are found, the working directory is updated to the specified
7157 changeset.
7160 changeset.
7158
7161
7159 .. container:: verbose
7162 .. container:: verbose
7160
7163
7161 The following rules apply when the working directory contains
7164 The following rules apply when the working directory contains
7162 uncommitted changes:
7165 uncommitted changes:
7163
7166
7164 1. If neither -c/--check nor -C/--clean is specified, and if
7167 1. If neither -c/--check nor -C/--clean is specified, and if
7165 the requested changeset is an ancestor or descendant of
7168 the requested changeset is an ancestor or descendant of
7166 the working directory's parent, the uncommitted changes
7169 the working directory's parent, the uncommitted changes
7167 are merged into the requested changeset and the merged
7170 are merged into the requested changeset and the merged
7168 result is left uncommitted. If the requested changeset is
7171 result is left uncommitted. If the requested changeset is
7169 not an ancestor or descendant (that is, it is on another
7172 not an ancestor or descendant (that is, it is on another
7170 branch), the update is aborted and the uncommitted changes
7173 branch), the update is aborted and the uncommitted changes
7171 are preserved.
7174 are preserved.
7172
7175
7173 2. With the -c/--check option, the update is aborted and the
7176 2. With the -c/--check option, the update is aborted and the
7174 uncommitted changes are preserved.
7177 uncommitted changes are preserved.
7175
7178
7176 3. With the -C/--clean option, uncommitted changes are discarded and
7179 3. With the -C/--clean option, uncommitted changes are discarded and
7177 the working directory is updated to the requested changeset.
7180 the working directory is updated to the requested changeset.
7178
7181
7179 To cancel an uncommitted merge (and lose your changes), use
7182 To cancel an uncommitted merge (and lose your changes), use
7180 :hg:`update --clean .`.
7183 :hg:`update --clean .`.
7181
7184
7182 Use null as the changeset to remove the working directory (like
7185 Use null as the changeset to remove the working directory (like
7183 :hg:`clone -U`).
7186 :hg:`clone -U`).
7184
7187
7185 If you want to revert just one file to an older revision, use
7188 If you want to revert just one file to an older revision, use
7186 :hg:`revert [-r REV] NAME`.
7189 :hg:`revert [-r REV] NAME`.
7187
7190
7188 See :hg:`help dates` for a list of formats valid for -d/--date.
7191 See :hg:`help dates` for a list of formats valid for -d/--date.
7189
7192
7190 Returns 0 on success, 1 if there are unresolved files.
7193 Returns 0 on success, 1 if there are unresolved files.
7191 """
7194 """
7192 if rev and node:
7195 if rev and node:
7193 raise error.Abort(_("please specify just one revision"))
7196 raise error.Abort(_("please specify just one revision"))
7194
7197
7195 if rev is None or rev == '':
7198 if rev is None or rev == '':
7196 rev = node
7199 rev = node
7197
7200
7198 if date and rev is not None:
7201 if date and rev is not None:
7199 raise error.Abort(_("you can't specify a revision and a date"))
7202 raise error.Abort(_("you can't specify a revision and a date"))
7200
7203
7201 if check and clean:
7204 if check and clean:
7202 raise error.Abort(_("cannot specify both -c/--check and -C/--clean"))
7205 raise error.Abort(_("cannot specify both -c/--check and -C/--clean"))
7203
7206
7204 with repo.wlock():
7207 with repo.wlock():
7205 cmdutil.clearunfinished(repo)
7208 cmdutil.clearunfinished(repo)
7206
7209
7207 if date:
7210 if date:
7208 rev = cmdutil.finddate(ui, repo, date)
7211 rev = cmdutil.finddate(ui, repo, date)
7209
7212
7210 # if we defined a bookmark, we have to remember the original name
7213 # if we defined a bookmark, we have to remember the original name
7211 brev = rev
7214 brev = rev
7212 rev = scmutil.revsingle(repo, rev, rev).rev()
7215 rev = scmutil.revsingle(repo, rev, rev).rev()
7213
7216
7214 if check:
7217 if check:
7215 cmdutil.bailifchanged(repo, merge=False)
7218 cmdutil.bailifchanged(repo, merge=False)
7216
7219
7217 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
7220 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
7218
7221
7219 return hg.updatetotally(ui, repo, rev, brev, clean=clean, check=check)
7222 return hg.updatetotally(ui, repo, rev, brev, clean=clean, check=check)
7220
7223
7221 @command('verify', [])
7224 @command('verify', [])
7222 def verify(ui, repo):
7225 def verify(ui, repo):
7223 """verify the integrity of the repository
7226 """verify the integrity of the repository
7224
7227
7225 Verify the integrity of the current repository.
7228 Verify the integrity of the current repository.
7226
7229
7227 This will perform an extensive check of the repository's
7230 This will perform an extensive check of the repository's
7228 integrity, validating the hashes and checksums of each entry in
7231 integrity, validating the hashes and checksums of each entry in
7229 the changelog, manifest, and tracked files, as well as the
7232 the changelog, manifest, and tracked files, as well as the
7230 integrity of their crosslinks and indices.
7233 integrity of their crosslinks and indices.
7231
7234
7232 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7235 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7233 for more information about recovery from corruption of the
7236 for more information about recovery from corruption of the
7234 repository.
7237 repository.
7235
7238
7236 Returns 0 on success, 1 if errors are encountered.
7239 Returns 0 on success, 1 if errors are encountered.
7237 """
7240 """
7238 return hg.verify(repo)
7241 return hg.verify(repo)
7239
7242
7240 @command('version', [], norepo=True)
7243 @command('version', [], norepo=True)
7241 def version_(ui):
7244 def version_(ui):
7242 """output version and copyright information"""
7245 """output version and copyright information"""
7243 ui.write(_("Mercurial Distributed SCM (version %s)\n")
7246 ui.write(_("Mercurial Distributed SCM (version %s)\n")
7244 % util.version())
7247 % util.version())
7245 ui.status(_(
7248 ui.status(_(
7246 "(see https://mercurial-scm.org for more information)\n"
7249 "(see https://mercurial-scm.org for more information)\n"
7247 "\nCopyright (C) 2005-2016 Matt Mackall and others\n"
7250 "\nCopyright (C) 2005-2016 Matt Mackall and others\n"
7248 "This is free software; see the source for copying conditions. "
7251 "This is free software; see the source for copying conditions. "
7249 "There is NO\nwarranty; "
7252 "There is NO\nwarranty; "
7250 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7253 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7251 ))
7254 ))
7252
7255
7253 ui.note(_("\nEnabled extensions:\n\n"))
7256 ui.note(_("\nEnabled extensions:\n\n"))
7254 if ui.verbose:
7257 if ui.verbose:
7255 # format names and versions into columns
7258 # format names and versions into columns
7256 names = []
7259 names = []
7257 vers = []
7260 vers = []
7258 place = []
7261 place = []
7259 for name, module in extensions.extensions():
7262 for name, module in extensions.extensions():
7260 names.append(name)
7263 names.append(name)
7261 vers.append(extensions.moduleversion(module))
7264 vers.append(extensions.moduleversion(module))
7262 if extensions.ismoduleinternal(module):
7265 if extensions.ismoduleinternal(module):
7263 place.append(_("internal"))
7266 place.append(_("internal"))
7264 else:
7267 else:
7265 place.append(_("external"))
7268 place.append(_("external"))
7266 if names:
7269 if names:
7267 maxnamelen = max(len(n) for n in names)
7270 maxnamelen = max(len(n) for n in names)
7268 for i, name in enumerate(names):
7271 for i, name in enumerate(names):
7269 ui.write(" %-*s %s %s\n" %
7272 ui.write(" %-*s %s %s\n" %
7270 (maxnamelen, name, place[i], vers[i]))
7273 (maxnamelen, name, place[i], vers[i]))
7271
7274
7272 def loadcmdtable(ui, name, cmdtable):
7275 def loadcmdtable(ui, name, cmdtable):
7273 """Load command functions from specified cmdtable
7276 """Load command functions from specified cmdtable
7274 """
7277 """
7275 overrides = [cmd for cmd in cmdtable if cmd in table]
7278 overrides = [cmd for cmd in cmdtable if cmd in table]
7276 if overrides:
7279 if overrides:
7277 ui.warn(_("extension '%s' overrides commands: %s\n")
7280 ui.warn(_("extension '%s' overrides commands: %s\n")
7278 % (name, " ".join(overrides)))
7281 % (name, " ".join(overrides)))
7279 table.update(cmdtable)
7282 table.update(cmdtable)
@@ -1,352 +1,352 b''
1 Show all commands except debug commands
1 Show all commands except debug commands
2 $ hg debugcomplete
2 $ hg debugcomplete
3 add
3 add
4 addremove
4 addremove
5 annotate
5 annotate
6 archive
6 archive
7 backout
7 backout
8 bisect
8 bisect
9 bookmarks
9 bookmarks
10 branch
10 branch
11 branches
11 branches
12 bundle
12 bundle
13 cat
13 cat
14 clone
14 clone
15 commit
15 commit
16 config
16 config
17 copy
17 copy
18 diff
18 diff
19 export
19 export
20 files
20 files
21 forget
21 forget
22 graft
22 graft
23 grep
23 grep
24 heads
24 heads
25 help
25 help
26 identify
26 identify
27 import
27 import
28 incoming
28 incoming
29 init
29 init
30 locate
30 locate
31 log
31 log
32 manifest
32 manifest
33 merge
33 merge
34 outgoing
34 outgoing
35 parents
35 parents
36 paths
36 paths
37 phase
37 phase
38 pull
38 pull
39 push
39 push
40 recover
40 recover
41 remove
41 remove
42 rename
42 rename
43 resolve
43 resolve
44 revert
44 revert
45 rollback
45 rollback
46 root
46 root
47 serve
47 serve
48 status
48 status
49 summary
49 summary
50 tag
50 tag
51 tags
51 tags
52 tip
52 tip
53 unbundle
53 unbundle
54 update
54 update
55 verify
55 verify
56 version
56 version
57
57
58 Show all commands that start with "a"
58 Show all commands that start with "a"
59 $ hg debugcomplete a
59 $ hg debugcomplete a
60 add
60 add
61 addremove
61 addremove
62 annotate
62 annotate
63 archive
63 archive
64
64
65 Do not show debug commands if there are other candidates
65 Do not show debug commands if there are other candidates
66 $ hg debugcomplete d
66 $ hg debugcomplete d
67 diff
67 diff
68
68
69 Show debug commands if there are no other candidates
69 Show debug commands if there are no other candidates
70 $ hg debugcomplete debug
70 $ hg debugcomplete debug
71 debugancestor
71 debugancestor
72 debugapplystreamclonebundle
72 debugapplystreamclonebundle
73 debugbuilddag
73 debugbuilddag
74 debugbundle
74 debugbundle
75 debugcheckstate
75 debugcheckstate
76 debugcommands
76 debugcommands
77 debugcomplete
77 debugcomplete
78 debugconfig
78 debugconfig
79 debugcreatestreamclonebundle
79 debugcreatestreamclonebundle
80 debugdag
80 debugdag
81 debugdata
81 debugdata
82 debugdate
82 debugdate
83 debugdeltachain
83 debugdeltachain
84 debugdirstate
84 debugdirstate
85 debugdiscovery
85 debugdiscovery
86 debugextensions
86 debugextensions
87 debugfileset
87 debugfileset
88 debugfsinfo
88 debugfsinfo
89 debuggetbundle
89 debuggetbundle
90 debugignore
90 debugignore
91 debugindex
91 debugindex
92 debugindexdot
92 debugindexdot
93 debuginstall
93 debuginstall
94 debugknown
94 debugknown
95 debuglabelcomplete
95 debuglabelcomplete
96 debuglocks
96 debuglocks
97 debugmergestate
97 debugmergestate
98 debugnamecomplete
98 debugnamecomplete
99 debugobsolete
99 debugobsolete
100 debugpathcomplete
100 debugpathcomplete
101 debugpushkey
101 debugpushkey
102 debugpvec
102 debugpvec
103 debugrebuilddirstate
103 debugrebuilddirstate
104 debugrebuildfncache
104 debugrebuildfncache
105 debugrename
105 debugrename
106 debugrevlog
106 debugrevlog
107 debugrevspec
107 debugrevspec
108 debugsetparents
108 debugsetparents
109 debugsub
109 debugsub
110 debugsuccessorssets
110 debugsuccessorssets
111 debugtemplate
111 debugtemplate
112 debugwalk
112 debugwalk
113 debugwireargs
113 debugwireargs
114
114
115 Do not show the alias of a debug command if there are other candidates
115 Do not show the alias of a debug command if there are other candidates
116 (this should hide rawcommit)
116 (this should hide rawcommit)
117 $ hg debugcomplete r
117 $ hg debugcomplete r
118 recover
118 recover
119 remove
119 remove
120 rename
120 rename
121 resolve
121 resolve
122 revert
122 revert
123 rollback
123 rollback
124 root
124 root
125 Show the alias of a debug command if there are no other candidates
125 Show the alias of a debug command if there are no other candidates
126 $ hg debugcomplete rawc
126 $ hg debugcomplete rawc
127
127
128
128
129 Show the global options
129 Show the global options
130 $ hg debugcomplete --options | sort
130 $ hg debugcomplete --options | sort
131 --config
131 --config
132 --cwd
132 --cwd
133 --debug
133 --debug
134 --debugger
134 --debugger
135 --encoding
135 --encoding
136 --encodingmode
136 --encodingmode
137 --help
137 --help
138 --hidden
138 --hidden
139 --noninteractive
139 --noninteractive
140 --profile
140 --profile
141 --quiet
141 --quiet
142 --repository
142 --repository
143 --time
143 --time
144 --traceback
144 --traceback
145 --verbose
145 --verbose
146 --version
146 --version
147 -R
147 -R
148 -h
148 -h
149 -q
149 -q
150 -v
150 -v
151 -y
151 -y
152
152
153 Show the options for the "serve" command
153 Show the options for the "serve" command
154 $ hg debugcomplete --options serve | sort
154 $ hg debugcomplete --options serve | sort
155 --accesslog
155 --accesslog
156 --address
156 --address
157 --certificate
157 --certificate
158 --cmdserver
158 --cmdserver
159 --config
159 --config
160 --cwd
160 --cwd
161 --daemon
161 --daemon
162 --daemon-postexec
162 --daemon-postexec
163 --debug
163 --debug
164 --debugger
164 --debugger
165 --encoding
165 --encoding
166 --encodingmode
166 --encodingmode
167 --errorlog
167 --errorlog
168 --help
168 --help
169 --hidden
169 --hidden
170 --ipv6
170 --ipv6
171 --name
171 --name
172 --noninteractive
172 --noninteractive
173 --pid-file
173 --pid-file
174 --port
174 --port
175 --prefix
175 --prefix
176 --profile
176 --profile
177 --quiet
177 --quiet
178 --repository
178 --repository
179 --stdio
179 --stdio
180 --style
180 --style
181 --templates
181 --templates
182 --time
182 --time
183 --traceback
183 --traceback
184 --verbose
184 --verbose
185 --version
185 --version
186 --web-conf
186 --web-conf
187 -6
187 -6
188 -A
188 -A
189 -E
189 -E
190 -R
190 -R
191 -a
191 -a
192 -d
192 -d
193 -h
193 -h
194 -n
194 -n
195 -p
195 -p
196 -q
196 -q
197 -t
197 -t
198 -v
198 -v
199 -y
199 -y
200
200
201 Show an error if we use --options with an ambiguous abbreviation
201 Show an error if we use --options with an ambiguous abbreviation
202 $ hg debugcomplete --options s
202 $ hg debugcomplete --options s
203 hg: command 's' is ambiguous:
203 hg: command 's' is ambiguous:
204 serve showconfig status summary
204 serve showconfig status summary
205 [255]
205 [255]
206
206
207 Show all commands + options
207 Show all commands + options
208 $ hg debugcommands
208 $ hg debugcommands
209 add: include, exclude, subrepos, dry-run
209 add: include, exclude, subrepos, dry-run
210 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, ignore-all-space, ignore-space-change, ignore-blank-lines, include, exclude, template
210 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, ignore-all-space, ignore-space-change, ignore-blank-lines, include, exclude, template
211 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
211 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
212 commit: addremove, close-branch, amend, secret, edit, interactive, include, exclude, message, logfile, date, user, subrepos
212 commit: addremove, close-branch, amend, secret, edit, interactive, include, exclude, message, logfile, date, user, subrepos
213 diff: rev, change, text, git, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, root, include, exclude, subrepos
213 diff: rev, change, text, git, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, root, include, exclude, subrepos
214 export: output, switch-parent, rev, text, git, nodates
214 export: output, switch-parent, rev, text, git, nodates
215 forget: include, exclude
215 forget: include, exclude
216 init: ssh, remotecmd, insecure
216 init: ssh, remotecmd, insecure
217 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
217 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
218 merge: force, rev, preview, tool
218 merge: force, rev, preview, tool
219 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
219 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
220 push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
220 push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
221 remove: after, force, subrepos, include, exclude
221 remove: after, force, subrepos, include, exclude
222 serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate
222 serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate
223 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos, template
223 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos, template
224 summary: remote
224 summary: remote
225 update: clean, check, date, rev, tool
225 update: clean, check, date, rev, tool
226 addremove: similarity, subrepos, include, exclude, dry-run
226 addremove: similarity, subrepos, include, exclude, dry-run
227 archive: no-decode, prefix, rev, type, subrepos, include, exclude
227 archive: no-decode, prefix, rev, type, subrepos, include, exclude
228 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
228 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
229 bisect: reset, good, bad, skip, extend, command, noupdate
229 bisect: reset, good, bad, skip, extend, command, noupdate
230 bookmarks: force, rev, delete, rename, inactive, template
230 bookmarks: force, rev, delete, rename, inactive, template
231 branch: force, clean
231 branch: force, clean
232 branches: active, closed, template
232 branches: active, closed, template
233 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
233 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
234 cat: output, rev, decode, include, exclude
234 cat: output, rev, decode, include, exclude
235 config: untrusted, edit, local, global
235 config: untrusted, edit, local, global
236 copy: after, force, include, exclude, dry-run
236 copy: after, force, include, exclude, dry-run
237 debugancestor:
237 debugancestor:
238 debugapplystreamclonebundle:
238 debugapplystreamclonebundle:
239 debugbuilddag: mergeable-file, overwritten-file, new-file
239 debugbuilddag: mergeable-file, overwritten-file, new-file
240 debugbundle: all, spec
240 debugbundle: all, spec
241 debugcheckstate:
241 debugcheckstate:
242 debugcommands:
242 debugcommands:
243 debugcomplete: options
243 debugcomplete: options
244 debugcreatestreamclonebundle:
244 debugcreatestreamclonebundle:
245 debugdag: tags, branches, dots, spaces
245 debugdag: tags, branches, dots, spaces
246 debugdata: changelog, manifest, dir
246 debugdata: changelog, manifest, dir
247 debugdate: extended
247 debugdate: extended
248 debugdeltachain: changelog, manifest, dir, template
248 debugdeltachain: changelog, manifest, dir, template
249 debugdirstate: nodates, datesort
249 debugdirstate: nodates, datesort
250 debugdiscovery: old, nonheads, ssh, remotecmd, insecure
250 debugdiscovery: old, nonheads, ssh, remotecmd, insecure
251 debugextensions: template
251 debugextensions: template
252 debugfileset: rev
252 debugfileset: rev
253 debugfsinfo:
253 debugfsinfo:
254 debuggetbundle: head, common, type
254 debuggetbundle: head, common, type
255 debugignore:
255 debugignore:
256 debugindex: changelog, manifest, dir, format
256 debugindex: changelog, manifest, dir, format
257 debugindexdot: changelog, manifest, dir
257 debugindexdot: changelog, manifest, dir
258 debuginstall: template
258 debuginstall: template
259 debugknown:
259 debugknown:
260 debuglabelcomplete:
260 debuglabelcomplete:
261 debuglocks: force-lock, force-wlock
261 debuglocks: force-lock, force-wlock
262 debugmergestate:
262 debugmergestate:
263 debugnamecomplete:
263 debugnamecomplete:
264 debugobsolete: flags, record-parents, rev, index, delete, date, user
264 debugobsolete: flags, record-parents, rev, index, delete, date, user, template
265 debugpathcomplete: full, normal, added, removed
265 debugpathcomplete: full, normal, added, removed
266 debugpushkey:
266 debugpushkey:
267 debugpvec:
267 debugpvec:
268 debugrebuilddirstate: rev, minimal
268 debugrebuilddirstate: rev, minimal
269 debugrebuildfncache:
269 debugrebuildfncache:
270 debugrename: rev
270 debugrename: rev
271 debugrevlog: changelog, manifest, dir, dump
271 debugrevlog: changelog, manifest, dir, dump
272 debugrevspec: optimize
272 debugrevspec: optimize
273 debugsetparents:
273 debugsetparents:
274 debugsub: rev
274 debugsub: rev
275 debugsuccessorssets:
275 debugsuccessorssets:
276 debugtemplate: rev, define
276 debugtemplate: rev, define
277 debugwalk: include, exclude
277 debugwalk: include, exclude
278 debugwireargs: three, four, five, ssh, remotecmd, insecure
278 debugwireargs: three, four, five, ssh, remotecmd, insecure
279 files: rev, print0, include, exclude, template, subrepos
279 files: rev, print0, include, exclude, template, subrepos
280 graft: rev, continue, edit, log, force, currentdate, currentuser, date, user, tool, dry-run
280 graft: rev, continue, edit, log, force, currentdate, currentuser, date, user, tool, dry-run
281 grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
281 grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
282 heads: rev, topo, active, closed, style, template
282 heads: rev, topo, active, closed, style, template
283 help: extension, command, keyword, system
283 help: extension, command, keyword, system
284 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure
284 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure
285 import: strip, base, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
285 import: strip, base, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
286 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
286 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
287 locate: rev, print0, fullpath, include, exclude
287 locate: rev, print0, fullpath, include, exclude
288 manifest: rev, all, template
288 manifest: rev, all, template
289 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
289 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
290 parents: rev, style, template
290 parents: rev, style, template
291 paths: template
291 paths: template
292 phase: public, draft, secret, force, rev
292 phase: public, draft, secret, force, rev
293 recover:
293 recover:
294 rename: after, force, include, exclude, dry-run
294 rename: after, force, include, exclude, dry-run
295 resolve: all, list, mark, unmark, no-status, tool, include, exclude, template
295 resolve: all, list, mark, unmark, no-status, tool, include, exclude, template
296 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
296 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
297 rollback: dry-run, force
297 rollback: dry-run, force
298 root:
298 root:
299 tag: force, local, rev, remove, edit, message, date, user
299 tag: force, local, rev, remove, edit, message, date, user
300 tags: template
300 tags: template
301 tip: patch, git, style, template
301 tip: patch, git, style, template
302 unbundle: update
302 unbundle: update
303 verify:
303 verify:
304 version:
304 version:
305
305
306 $ hg init a
306 $ hg init a
307 $ cd a
307 $ cd a
308 $ echo fee > fee
308 $ echo fee > fee
309 $ hg ci -q -Amfee
309 $ hg ci -q -Amfee
310 $ hg tag fee
310 $ hg tag fee
311 $ mkdir fie
311 $ mkdir fie
312 $ echo dead > fie/dead
312 $ echo dead > fie/dead
313 $ echo live > fie/live
313 $ echo live > fie/live
314 $ hg bookmark fo
314 $ hg bookmark fo
315 $ hg branch -q fie
315 $ hg branch -q fie
316 $ hg ci -q -Amfie
316 $ hg ci -q -Amfie
317 $ echo fo > fo
317 $ echo fo > fo
318 $ hg branch -qf default
318 $ hg branch -qf default
319 $ hg ci -q -Amfo
319 $ hg ci -q -Amfo
320 $ echo Fum > Fum
320 $ echo Fum > Fum
321 $ hg ci -q -AmFum
321 $ hg ci -q -AmFum
322 $ hg bookmark Fum
322 $ hg bookmark Fum
323
323
324 Test debugpathcomplete
324 Test debugpathcomplete
325
325
326 $ hg debugpathcomplete f
326 $ hg debugpathcomplete f
327 fee
327 fee
328 fie
328 fie
329 fo
329 fo
330 $ hg debugpathcomplete -f f
330 $ hg debugpathcomplete -f f
331 fee
331 fee
332 fie/dead
332 fie/dead
333 fie/live
333 fie/live
334 fo
334 fo
335
335
336 $ hg rm Fum
336 $ hg rm Fum
337 $ hg debugpathcomplete -r F
337 $ hg debugpathcomplete -r F
338 Fum
338 Fum
339
339
340 Test debugnamecomplete
340 Test debugnamecomplete
341
341
342 $ hg debugnamecomplete
342 $ hg debugnamecomplete
343 Fum
343 Fum
344 default
344 default
345 fee
345 fee
346 fie
346 fie
347 fo
347 fo
348 tip
348 tip
349 $ hg debugnamecomplete f
349 $ hg debugnamecomplete f
350 fee
350 fee
351 fie
351 fie
352 fo
352 fo
@@ -1,1126 +1,1219 b''
1 $ cat >> $HGRCPATH << EOF
1 $ cat >> $HGRCPATH << EOF
2 > [phases]
2 > [phases]
3 > # public changeset are not obsolete
3 > # public changeset are not obsolete
4 > publish=false
4 > publish=false
5 > [ui]
5 > [ui]
6 > logtemplate="{rev}:{node|short} ({phase}) [{tags} {bookmarks}] {desc|firstline}\n"
6 > logtemplate="{rev}:{node|short} ({phase}) [{tags} {bookmarks}] {desc|firstline}\n"
7 > EOF
7 > EOF
8 $ mkcommit() {
8 $ mkcommit() {
9 > echo "$1" > "$1"
9 > echo "$1" > "$1"
10 > hg add "$1"
10 > hg add "$1"
11 > hg ci -m "add $1"
11 > hg ci -m "add $1"
12 > }
12 > }
13 $ getid() {
13 $ getid() {
14 > hg log -T "{node}\n" --hidden -r "desc('$1')"
14 > hg log -T "{node}\n" --hidden -r "desc('$1')"
15 > }
15 > }
16
16
17 $ cat > debugkeys.py <<EOF
17 $ cat > debugkeys.py <<EOF
18 > def reposetup(ui, repo):
18 > def reposetup(ui, repo):
19 > class debugkeysrepo(repo.__class__):
19 > class debugkeysrepo(repo.__class__):
20 > def listkeys(self, namespace):
20 > def listkeys(self, namespace):
21 > ui.write('listkeys %s\n' % (namespace,))
21 > ui.write('listkeys %s\n' % (namespace,))
22 > return super(debugkeysrepo, self).listkeys(namespace)
22 > return super(debugkeysrepo, self).listkeys(namespace)
23 >
23 >
24 > if repo.local():
24 > if repo.local():
25 > repo.__class__ = debugkeysrepo
25 > repo.__class__ = debugkeysrepo
26 > EOF
26 > EOF
27
27
28 $ hg init tmpa
28 $ hg init tmpa
29 $ cd tmpa
29 $ cd tmpa
30 $ mkcommit kill_me
30 $ mkcommit kill_me
31
31
32 Checking that the feature is properly disabled
32 Checking that the feature is properly disabled
33
33
34 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
34 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
35 abort: creating obsolete markers is not enabled on this repo
35 abort: creating obsolete markers is not enabled on this repo
36 [255]
36 [255]
37
37
38 Enabling it
38 Enabling it
39
39
40 $ cat >> $HGRCPATH << EOF
40 $ cat >> $HGRCPATH << EOF
41 > [experimental]
41 > [experimental]
42 > evolution=createmarkers,exchange
42 > evolution=createmarkers,exchange
43 > EOF
43 > EOF
44
44
45 Killing a single changeset without replacement
45 Killing a single changeset without replacement
46
46
47 $ hg debugobsolete 0
47 $ hg debugobsolete 0
48 abort: changeset references must be full hexadecimal node identifiers
48 abort: changeset references must be full hexadecimal node identifiers
49 [255]
49 [255]
50 $ hg debugobsolete '00'
50 $ hg debugobsolete '00'
51 abort: changeset references must be full hexadecimal node identifiers
51 abort: changeset references must be full hexadecimal node identifiers
52 [255]
52 [255]
53 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
53 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
54 $ hg debugobsolete
54 $ hg debugobsolete
55 97b7c2d76b1845ed3eb988cd612611e72406cef0 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'babar'}
55 97b7c2d76b1845ed3eb988cd612611e72406cef0 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'babar'}
56
56
57 (test that mercurial is not confused)
57 (test that mercurial is not confused)
58
58
59 $ hg up null --quiet # having 0 as parent prevents it to be hidden
59 $ hg up null --quiet # having 0 as parent prevents it to be hidden
60 $ hg tip
60 $ hg tip
61 -1:000000000000 (public) [tip ]
61 -1:000000000000 (public) [tip ]
62 $ hg up --hidden tip --quiet
62 $ hg up --hidden tip --quiet
63
63
64 Killing a single changeset with itself should fail
64 Killing a single changeset with itself should fail
65 (simple local safeguard)
65 (simple local safeguard)
66
66
67 $ hg debugobsolete `getid kill_me` `getid kill_me`
67 $ hg debugobsolete `getid kill_me` `getid kill_me`
68 abort: bad obsmarker input: in-marker cycle with 97b7c2d76b1845ed3eb988cd612611e72406cef0
68 abort: bad obsmarker input: in-marker cycle with 97b7c2d76b1845ed3eb988cd612611e72406cef0
69 [255]
69 [255]
70
70
71 $ cd ..
71 $ cd ..
72
72
73 Killing a single changeset with replacement
73 Killing a single changeset with replacement
74 (and testing the format option)
74 (and testing the format option)
75
75
76 $ hg init tmpb
76 $ hg init tmpb
77 $ cd tmpb
77 $ cd tmpb
78 $ mkcommit a
78 $ mkcommit a
79 $ mkcommit b
79 $ mkcommit b
80 $ mkcommit original_c
80 $ mkcommit original_c
81 $ hg up "desc('b')"
81 $ hg up "desc('b')"
82 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
82 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
83 $ mkcommit new_c
83 $ mkcommit new_c
84 created new head
84 created new head
85 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
85 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
86 $ hg debugobsolete --config format.obsstore-version=0 --flag 12 `getid original_c` `getid new_c` -d '121 120'
86 $ hg debugobsolete --config format.obsstore-version=0 --flag 12 `getid original_c` `getid new_c` -d '121 120'
87 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
87 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
88 2:245bde4270cd add original_c
88 2:245bde4270cd add original_c
89 $ hg debugrevlog -cd
89 $ hg debugrevlog -cd
90 # rev p1rev p2rev start end deltastart base p1 p2 rawsize totalsize compression heads chainlen
90 # rev p1rev p2rev start end deltastart base p1 p2 rawsize totalsize compression heads chainlen
91 0 -1 -1 0 59 0 0 0 0 58 58 0 1 0
91 0 -1 -1 0 59 0 0 0 0 58 58 0 1 0
92 1 0 -1 59 118 59 59 0 0 58 116 0 1 0
92 1 0 -1 59 118 59 59 0 0 58 116 0 1 0
93 2 1 -1 118 193 118 118 59 0 76 192 0 1 0
93 2 1 -1 118 193 118 118 59 0 76 192 0 1 0
94 3 1 -1 193 260 193 193 59 0 66 258 0 2 0
94 3 1 -1 193 260 193 193 59 0 66 258 0 2 0
95 $ hg debugobsolete
95 $ hg debugobsolete
96 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
96 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
97
97
98 (check for version number of the obsstore)
98 (check for version number of the obsstore)
99
99
100 $ dd bs=1 count=1 if=.hg/store/obsstore 2>/dev/null
100 $ dd bs=1 count=1 if=.hg/store/obsstore 2>/dev/null
101 \x00 (no-eol) (esc)
101 \x00 (no-eol) (esc)
102
102
103 do it again (it read the obsstore before adding new changeset)
103 do it again (it read the obsstore before adding new changeset)
104
104
105 $ hg up '.^'
105 $ hg up '.^'
106 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
106 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
107 $ mkcommit new_2_c
107 $ mkcommit new_2_c
108 created new head
108 created new head
109 $ hg debugobsolete -d '1337 0' `getid new_c` `getid new_2_c`
109 $ hg debugobsolete -d '1337 0' `getid new_c` `getid new_2_c`
110 $ hg debugobsolete
110 $ hg debugobsolete
111 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
111 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
112 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
112 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
113
113
114 Register two markers with a missing node
114 Register two markers with a missing node
115
115
116 $ hg up '.^'
116 $ hg up '.^'
117 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
117 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
118 $ mkcommit new_3_c
118 $ mkcommit new_3_c
119 created new head
119 created new head
120 $ hg debugobsolete -d '1338 0' `getid new_2_c` 1337133713371337133713371337133713371337
120 $ hg debugobsolete -d '1338 0' `getid new_2_c` 1337133713371337133713371337133713371337
121 $ hg debugobsolete -d '1339 0' 1337133713371337133713371337133713371337 `getid new_3_c`
121 $ hg debugobsolete -d '1339 0' 1337133713371337133713371337133713371337 `getid new_3_c`
122 $ hg debugobsolete
122 $ hg debugobsolete
123 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
123 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
124 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
124 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
125 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
125 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
126 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
126 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
127
127
128 Test the --index option of debugobsolete command
128 Test the --index option of debugobsolete command
129 $ hg debugobsolete --index
129 $ hg debugobsolete --index
130 0 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
130 0 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
131 1 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
131 1 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
132 2 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
132 2 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
133 3 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
133 3 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
134
134
135 Refuse pathological nullid successors
135 Refuse pathological nullid successors
136 $ hg debugobsolete -d '9001 0' 1337133713371337133713371337133713371337 0000000000000000000000000000000000000000
136 $ hg debugobsolete -d '9001 0' 1337133713371337133713371337133713371337 0000000000000000000000000000000000000000
137 transaction abort!
137 transaction abort!
138 rollback completed
138 rollback completed
139 abort: bad obsolescence marker detected: invalid successors nullid
139 abort: bad obsolescence marker detected: invalid successors nullid
140 [255]
140 [255]
141
141
142 Check that graphlog detect that a changeset is obsolete:
142 Check that graphlog detect that a changeset is obsolete:
143
143
144 $ hg log -G
144 $ hg log -G
145 @ 5:5601fb93a350 (draft) [tip ] add new_3_c
145 @ 5:5601fb93a350 (draft) [tip ] add new_3_c
146 |
146 |
147 o 1:7c3bad9141dc (draft) [ ] add b
147 o 1:7c3bad9141dc (draft) [ ] add b
148 |
148 |
149 o 0:1f0dee641bb7 (draft) [ ] add a
149 o 0:1f0dee641bb7 (draft) [ ] add a
150
150
151
151
152 check that heads does not report them
152 check that heads does not report them
153
153
154 $ hg heads
154 $ hg heads
155 5:5601fb93a350 (draft) [tip ] add new_3_c
155 5:5601fb93a350 (draft) [tip ] add new_3_c
156 $ hg heads --hidden
156 $ hg heads --hidden
157 5:5601fb93a350 (draft) [tip ] add new_3_c
157 5:5601fb93a350 (draft) [tip ] add new_3_c
158 4:ca819180edb9 (draft) [ ] add new_2_c
158 4:ca819180edb9 (draft) [ ] add new_2_c
159 3:cdbce2fbb163 (draft) [ ] add new_c
159 3:cdbce2fbb163 (draft) [ ] add new_c
160 2:245bde4270cd (draft) [ ] add original_c
160 2:245bde4270cd (draft) [ ] add original_c
161
161
162
162
163 check that summary does not report them
163 check that summary does not report them
164
164
165 $ hg init ../sink
165 $ hg init ../sink
166 $ echo '[paths]' >> .hg/hgrc
166 $ echo '[paths]' >> .hg/hgrc
167 $ echo 'default=../sink' >> .hg/hgrc
167 $ echo 'default=../sink' >> .hg/hgrc
168 $ hg summary --remote
168 $ hg summary --remote
169 parent: 5:5601fb93a350 tip
169 parent: 5:5601fb93a350 tip
170 add new_3_c
170 add new_3_c
171 branch: default
171 branch: default
172 commit: (clean)
172 commit: (clean)
173 update: (current)
173 update: (current)
174 phases: 3 draft
174 phases: 3 draft
175 remote: 3 outgoing
175 remote: 3 outgoing
176
176
177 $ hg summary --remote --hidden
177 $ hg summary --remote --hidden
178 parent: 5:5601fb93a350 tip
178 parent: 5:5601fb93a350 tip
179 add new_3_c
179 add new_3_c
180 branch: default
180 branch: default
181 commit: (clean)
181 commit: (clean)
182 update: 3 new changesets, 4 branch heads (merge)
182 update: 3 new changesets, 4 branch heads (merge)
183 phases: 6 draft
183 phases: 6 draft
184 remote: 3 outgoing
184 remote: 3 outgoing
185
185
186 check that various commands work well with filtering
186 check that various commands work well with filtering
187
187
188 $ hg tip
188 $ hg tip
189 5:5601fb93a350 (draft) [tip ] add new_3_c
189 5:5601fb93a350 (draft) [tip ] add new_3_c
190 $ hg log -r 6
190 $ hg log -r 6
191 abort: unknown revision '6'!
191 abort: unknown revision '6'!
192 [255]
192 [255]
193 $ hg log -r 4
193 $ hg log -r 4
194 abort: hidden revision '4'!
194 abort: hidden revision '4'!
195 (use --hidden to access hidden revisions)
195 (use --hidden to access hidden revisions)
196 [255]
196 [255]
197 $ hg debugrevspec 'rev(6)'
197 $ hg debugrevspec 'rev(6)'
198 $ hg debugrevspec 'rev(4)'
198 $ hg debugrevspec 'rev(4)'
199 $ hg debugrevspec 'null'
199 $ hg debugrevspec 'null'
200 -1
200 -1
201
201
202 Check that public changeset are not accounted as obsolete:
202 Check that public changeset are not accounted as obsolete:
203
203
204 $ hg --hidden phase --public 2
204 $ hg --hidden phase --public 2
205 $ hg log -G
205 $ hg log -G
206 @ 5:5601fb93a350 (draft) [tip ] add new_3_c
206 @ 5:5601fb93a350 (draft) [tip ] add new_3_c
207 |
207 |
208 | o 2:245bde4270cd (public) [ ] add original_c
208 | o 2:245bde4270cd (public) [ ] add original_c
209 |/
209 |/
210 o 1:7c3bad9141dc (public) [ ] add b
210 o 1:7c3bad9141dc (public) [ ] add b
211 |
211 |
212 o 0:1f0dee641bb7 (public) [ ] add a
212 o 0:1f0dee641bb7 (public) [ ] add a
213
213
214
214
215 And that bumped changeset are detected
215 And that bumped changeset are detected
216 --------------------------------------
216 --------------------------------------
217
217
218 If we didn't filtered obsolete changesets out, 3 and 4 would show up too. Also
218 If we didn't filtered obsolete changesets out, 3 and 4 would show up too. Also
219 note that the bumped changeset (5:5601fb93a350) is not a direct successor of
219 note that the bumped changeset (5:5601fb93a350) is not a direct successor of
220 the public changeset
220 the public changeset
221
221
222 $ hg log --hidden -r 'bumped()'
222 $ hg log --hidden -r 'bumped()'
223 5:5601fb93a350 (draft) [tip ] add new_3_c
223 5:5601fb93a350 (draft) [tip ] add new_3_c
224
224
225 And that we can't push bumped changeset
225 And that we can't push bumped changeset
226
226
227 $ hg push ../tmpa -r 0 --force #(make repo related)
227 $ hg push ../tmpa -r 0 --force #(make repo related)
228 pushing to ../tmpa
228 pushing to ../tmpa
229 searching for changes
229 searching for changes
230 warning: repository is unrelated
230 warning: repository is unrelated
231 adding changesets
231 adding changesets
232 adding manifests
232 adding manifests
233 adding file changes
233 adding file changes
234 added 1 changesets with 1 changes to 1 files (+1 heads)
234 added 1 changesets with 1 changes to 1 files (+1 heads)
235 $ hg push ../tmpa
235 $ hg push ../tmpa
236 pushing to ../tmpa
236 pushing to ../tmpa
237 searching for changes
237 searching for changes
238 abort: push includes bumped changeset: 5601fb93a350!
238 abort: push includes bumped changeset: 5601fb93a350!
239 [255]
239 [255]
240
240
241 Fixing "bumped" situation
241 Fixing "bumped" situation
242 We need to create a clone of 5 and add a special marker with a flag
242 We need to create a clone of 5 and add a special marker with a flag
243
243
244 $ hg summary
244 $ hg summary
245 parent: 5:5601fb93a350 tip
245 parent: 5:5601fb93a350 tip
246 add new_3_c
246 add new_3_c
247 branch: default
247 branch: default
248 commit: (clean)
248 commit: (clean)
249 update: 1 new changesets, 2 branch heads (merge)
249 update: 1 new changesets, 2 branch heads (merge)
250 phases: 1 draft
250 phases: 1 draft
251 bumped: 1 changesets
251 bumped: 1 changesets
252 $ hg up '5^'
252 $ hg up '5^'
253 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
253 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
254 $ hg revert -ar 5
254 $ hg revert -ar 5
255 adding new_3_c
255 adding new_3_c
256 $ hg ci -m 'add n3w_3_c'
256 $ hg ci -m 'add n3w_3_c'
257 created new head
257 created new head
258 $ hg debugobsolete -d '1338 0' --flags 1 `getid new_3_c` `getid n3w_3_c`
258 $ hg debugobsolete -d '1338 0' --flags 1 `getid new_3_c` `getid n3w_3_c`
259 $ hg log -r 'bumped()'
259 $ hg log -r 'bumped()'
260 $ hg log -G
260 $ hg log -G
261 @ 6:6f9641995072 (draft) [tip ] add n3w_3_c
261 @ 6:6f9641995072 (draft) [tip ] add n3w_3_c
262 |
262 |
263 | o 2:245bde4270cd (public) [ ] add original_c
263 | o 2:245bde4270cd (public) [ ] add original_c
264 |/
264 |/
265 o 1:7c3bad9141dc (public) [ ] add b
265 o 1:7c3bad9141dc (public) [ ] add b
266 |
266 |
267 o 0:1f0dee641bb7 (public) [ ] add a
267 o 0:1f0dee641bb7 (public) [ ] add a
268
268
269
269
270 $ cd ..
270 $ cd ..
271
271
272 Revision 0 is hidden
272 Revision 0 is hidden
273 --------------------
273 --------------------
274
274
275 $ hg init rev0hidden
275 $ hg init rev0hidden
276 $ cd rev0hidden
276 $ cd rev0hidden
277
277
278 $ mkcommit kill0
278 $ mkcommit kill0
279 $ hg up -q null
279 $ hg up -q null
280 $ hg debugobsolete `getid kill0`
280 $ hg debugobsolete `getid kill0`
281 $ mkcommit a
281 $ mkcommit a
282 $ mkcommit b
282 $ mkcommit b
283
283
284 Should pick the first visible revision as "repo" node
284 Should pick the first visible revision as "repo" node
285
285
286 $ hg archive ../archive-null
286 $ hg archive ../archive-null
287 $ cat ../archive-null/.hg_archival.txt
287 $ cat ../archive-null/.hg_archival.txt
288 repo: 1f0dee641bb7258c56bd60e93edfa2405381c41e
288 repo: 1f0dee641bb7258c56bd60e93edfa2405381c41e
289 node: 7c3bad9141dcb46ff89abf5f61856facd56e476c
289 node: 7c3bad9141dcb46ff89abf5f61856facd56e476c
290 branch: default
290 branch: default
291 latesttag: null
291 latesttag: null
292 latesttagdistance: 2
292 latesttagdistance: 2
293 changessincelatesttag: 2
293 changessincelatesttag: 2
294
294
295
295
296 $ cd ..
296 $ cd ..
297
297
298 Exchange Test
298 Exchange Test
299 ============================
299 ============================
300
300
301 Destination repo does not have any data
301 Destination repo does not have any data
302 ---------------------------------------
302 ---------------------------------------
303
303
304 Simple incoming test
304 Simple incoming test
305
305
306 $ hg init tmpc
306 $ hg init tmpc
307 $ cd tmpc
307 $ cd tmpc
308 $ hg incoming ../tmpb
308 $ hg incoming ../tmpb
309 comparing with ../tmpb
309 comparing with ../tmpb
310 0:1f0dee641bb7 (public) [ ] add a
310 0:1f0dee641bb7 (public) [ ] add a
311 1:7c3bad9141dc (public) [ ] add b
311 1:7c3bad9141dc (public) [ ] add b
312 2:245bde4270cd (public) [ ] add original_c
312 2:245bde4270cd (public) [ ] add original_c
313 6:6f9641995072 (draft) [tip ] add n3w_3_c
313 6:6f9641995072 (draft) [tip ] add n3w_3_c
314
314
315 Try to pull markers
315 Try to pull markers
316 (extinct changeset are excluded but marker are pushed)
316 (extinct changeset are excluded but marker are pushed)
317
317
318 $ hg pull ../tmpb
318 $ hg pull ../tmpb
319 pulling from ../tmpb
319 pulling from ../tmpb
320 requesting all changes
320 requesting all changes
321 adding changesets
321 adding changesets
322 adding manifests
322 adding manifests
323 adding file changes
323 adding file changes
324 added 4 changesets with 4 changes to 4 files (+1 heads)
324 added 4 changesets with 4 changes to 4 files (+1 heads)
325 5 new obsolescence markers
325 5 new obsolescence markers
326 (run 'hg heads' to see heads, 'hg merge' to merge)
326 (run 'hg heads' to see heads, 'hg merge' to merge)
327 $ hg debugobsolete
327 $ hg debugobsolete
328 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
328 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
329 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
329 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
330 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
330 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
331 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
331 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
332 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
332 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
333
333
334 Rollback//Transaction support
334 Rollback//Transaction support
335
335
336 $ hg debugobsolete -d '1340 0' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
336 $ hg debugobsolete -d '1340 0' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
337 $ hg debugobsolete
337 $ hg debugobsolete
338 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
338 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
339 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
339 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
340 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
340 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
341 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
341 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
342 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
342 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
343 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 0 (Thu Jan 01 00:22:20 1970 +0000) {'user': 'test'}
343 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 0 (Thu Jan 01 00:22:20 1970 +0000) {'user': 'test'}
344 $ hg rollback -n
344 $ hg rollback -n
345 repository tip rolled back to revision 3 (undo debugobsolete)
345 repository tip rolled back to revision 3 (undo debugobsolete)
346 $ hg rollback
346 $ hg rollback
347 repository tip rolled back to revision 3 (undo debugobsolete)
347 repository tip rolled back to revision 3 (undo debugobsolete)
348 $ hg debugobsolete
348 $ hg debugobsolete
349 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
349 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
350 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
350 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
351 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
351 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
352 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
352 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
353 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
353 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
354
354
355 $ cd ..
355 $ cd ..
356
356
357 Try to push markers
357 Try to push markers
358
358
359 $ hg init tmpd
359 $ hg init tmpd
360 $ hg -R tmpb push tmpd
360 $ hg -R tmpb push tmpd
361 pushing to tmpd
361 pushing to tmpd
362 searching for changes
362 searching for changes
363 adding changesets
363 adding changesets
364 adding manifests
364 adding manifests
365 adding file changes
365 adding file changes
366 added 4 changesets with 4 changes to 4 files (+1 heads)
366 added 4 changesets with 4 changes to 4 files (+1 heads)
367 5 new obsolescence markers
367 5 new obsolescence markers
368 $ hg -R tmpd debugobsolete | sort
368 $ hg -R tmpd debugobsolete | sort
369 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
369 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
370 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
370 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
371 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
371 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
372 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
372 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
373 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
373 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
374
374
375 Check obsolete keys are exchanged only if source has an obsolete store
375 Check obsolete keys are exchanged only if source has an obsolete store
376
376
377 $ hg init empty
377 $ hg init empty
378 $ hg --config extensions.debugkeys=debugkeys.py -R empty push tmpd
378 $ hg --config extensions.debugkeys=debugkeys.py -R empty push tmpd
379 pushing to tmpd
379 pushing to tmpd
380 listkeys phases
380 listkeys phases
381 listkeys bookmarks
381 listkeys bookmarks
382 no changes found
382 no changes found
383 listkeys phases
383 listkeys phases
384 [1]
384 [1]
385
385
386 clone support
386 clone support
387 (markers are copied and extinct changesets are included to allow hardlinks)
387 (markers are copied and extinct changesets are included to allow hardlinks)
388
388
389 $ hg clone tmpb clone-dest
389 $ hg clone tmpb clone-dest
390 updating to branch default
390 updating to branch default
391 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
391 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
392 $ hg -R clone-dest log -G --hidden
392 $ hg -R clone-dest log -G --hidden
393 @ 6:6f9641995072 (draft) [tip ] add n3w_3_c
393 @ 6:6f9641995072 (draft) [tip ] add n3w_3_c
394 |
394 |
395 | x 5:5601fb93a350 (draft) [ ] add new_3_c
395 | x 5:5601fb93a350 (draft) [ ] add new_3_c
396 |/
396 |/
397 | x 4:ca819180edb9 (draft) [ ] add new_2_c
397 | x 4:ca819180edb9 (draft) [ ] add new_2_c
398 |/
398 |/
399 | x 3:cdbce2fbb163 (draft) [ ] add new_c
399 | x 3:cdbce2fbb163 (draft) [ ] add new_c
400 |/
400 |/
401 | o 2:245bde4270cd (public) [ ] add original_c
401 | o 2:245bde4270cd (public) [ ] add original_c
402 |/
402 |/
403 o 1:7c3bad9141dc (public) [ ] add b
403 o 1:7c3bad9141dc (public) [ ] add b
404 |
404 |
405 o 0:1f0dee641bb7 (public) [ ] add a
405 o 0:1f0dee641bb7 (public) [ ] add a
406
406
407 $ hg -R clone-dest debugobsolete
407 $ hg -R clone-dest debugobsolete
408 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
408 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
409 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
409 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
410 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
410 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
411 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
411 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
412 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
412 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
413
413
414
414
415 Destination repo have existing data
415 Destination repo have existing data
416 ---------------------------------------
416 ---------------------------------------
417
417
418 On pull
418 On pull
419
419
420 $ hg init tmpe
420 $ hg init tmpe
421 $ cd tmpe
421 $ cd tmpe
422 $ hg debugobsolete -d '1339 0' 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00
422 $ hg debugobsolete -d '1339 0' 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00
423 $ hg pull ../tmpb
423 $ hg pull ../tmpb
424 pulling from ../tmpb
424 pulling from ../tmpb
425 requesting all changes
425 requesting all changes
426 adding changesets
426 adding changesets
427 adding manifests
427 adding manifests
428 adding file changes
428 adding file changes
429 added 4 changesets with 4 changes to 4 files (+1 heads)
429 added 4 changesets with 4 changes to 4 files (+1 heads)
430 5 new obsolescence markers
430 5 new obsolescence markers
431 (run 'hg heads' to see heads, 'hg merge' to merge)
431 (run 'hg heads' to see heads, 'hg merge' to merge)
432 $ hg debugobsolete
432 $ hg debugobsolete
433 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
433 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
434 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
434 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
435 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
435 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
436 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
436 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
437 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
437 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
438 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
438 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
439
439
440
440
441 On push
441 On push
442
442
443 $ hg push ../tmpc
443 $ hg push ../tmpc
444 pushing to ../tmpc
444 pushing to ../tmpc
445 searching for changes
445 searching for changes
446 no changes found
446 no changes found
447 1 new obsolescence markers
447 1 new obsolescence markers
448 [1]
448 [1]
449 $ hg -R ../tmpc debugobsolete
449 $ hg -R ../tmpc debugobsolete
450 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
450 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
451 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
451 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
452 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
452 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
453 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
453 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
454 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
454 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
455 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
455 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
456
456
457 detect outgoing obsolete and unstable
457 detect outgoing obsolete and unstable
458 ---------------------------------------
458 ---------------------------------------
459
459
460
460
461 $ hg log -G
461 $ hg log -G
462 o 3:6f9641995072 (draft) [tip ] add n3w_3_c
462 o 3:6f9641995072 (draft) [tip ] add n3w_3_c
463 |
463 |
464 | o 2:245bde4270cd (public) [ ] add original_c
464 | o 2:245bde4270cd (public) [ ] add original_c
465 |/
465 |/
466 o 1:7c3bad9141dc (public) [ ] add b
466 o 1:7c3bad9141dc (public) [ ] add b
467 |
467 |
468 o 0:1f0dee641bb7 (public) [ ] add a
468 o 0:1f0dee641bb7 (public) [ ] add a
469
469
470 $ hg up 'desc("n3w_3_c")'
470 $ hg up 'desc("n3w_3_c")'
471 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
471 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
472 $ mkcommit original_d
472 $ mkcommit original_d
473 $ mkcommit original_e
473 $ mkcommit original_e
474 $ hg debugobsolete --record-parents `getid original_d` -d '0 0'
474 $ hg debugobsolete --record-parents `getid original_d` -d '0 0'
475 $ hg debugobsolete | grep `getid original_d`
475 $ hg debugobsolete | grep `getid original_d`
476 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
476 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
477 $ hg log -r 'obsolete()'
477 $ hg log -r 'obsolete()'
478 4:94b33453f93b (draft) [ ] add original_d
478 4:94b33453f93b (draft) [ ] add original_d
479 $ hg summary
479 $ hg summary
480 parent: 5:cda648ca50f5 tip
480 parent: 5:cda648ca50f5 tip
481 add original_e
481 add original_e
482 branch: default
482 branch: default
483 commit: (clean)
483 commit: (clean)
484 update: 1 new changesets, 2 branch heads (merge)
484 update: 1 new changesets, 2 branch heads (merge)
485 phases: 3 draft
485 phases: 3 draft
486 unstable: 1 changesets
486 unstable: 1 changesets
487 $ hg log -G -r '::unstable()'
487 $ hg log -G -r '::unstable()'
488 @ 5:cda648ca50f5 (draft) [tip ] add original_e
488 @ 5:cda648ca50f5 (draft) [tip ] add original_e
489 |
489 |
490 x 4:94b33453f93b (draft) [ ] add original_d
490 x 4:94b33453f93b (draft) [ ] add original_d
491 |
491 |
492 o 3:6f9641995072 (draft) [ ] add n3w_3_c
492 o 3:6f9641995072 (draft) [ ] add n3w_3_c
493 |
493 |
494 o 1:7c3bad9141dc (public) [ ] add b
494 o 1:7c3bad9141dc (public) [ ] add b
495 |
495 |
496 o 0:1f0dee641bb7 (public) [ ] add a
496 o 0:1f0dee641bb7 (public) [ ] add a
497
497
498
498
499 refuse to push obsolete changeset
499 refuse to push obsolete changeset
500
500
501 $ hg push ../tmpc/ -r 'desc("original_d")'
501 $ hg push ../tmpc/ -r 'desc("original_d")'
502 pushing to ../tmpc/
502 pushing to ../tmpc/
503 searching for changes
503 searching for changes
504 abort: push includes obsolete changeset: 94b33453f93b!
504 abort: push includes obsolete changeset: 94b33453f93b!
505 [255]
505 [255]
506
506
507 refuse to push unstable changeset
507 refuse to push unstable changeset
508
508
509 $ hg push ../tmpc/
509 $ hg push ../tmpc/
510 pushing to ../tmpc/
510 pushing to ../tmpc/
511 searching for changes
511 searching for changes
512 abort: push includes unstable changeset: cda648ca50f5!
512 abort: push includes unstable changeset: cda648ca50f5!
513 [255]
513 [255]
514
514
515 Test that extinct changeset are properly detected
515 Test that extinct changeset are properly detected
516
516
517 $ hg log -r 'extinct()'
517 $ hg log -r 'extinct()'
518
518
519 Don't try to push extinct changeset
519 Don't try to push extinct changeset
520
520
521 $ hg init ../tmpf
521 $ hg init ../tmpf
522 $ hg out ../tmpf
522 $ hg out ../tmpf
523 comparing with ../tmpf
523 comparing with ../tmpf
524 searching for changes
524 searching for changes
525 0:1f0dee641bb7 (public) [ ] add a
525 0:1f0dee641bb7 (public) [ ] add a
526 1:7c3bad9141dc (public) [ ] add b
526 1:7c3bad9141dc (public) [ ] add b
527 2:245bde4270cd (public) [ ] add original_c
527 2:245bde4270cd (public) [ ] add original_c
528 3:6f9641995072 (draft) [ ] add n3w_3_c
528 3:6f9641995072 (draft) [ ] add n3w_3_c
529 4:94b33453f93b (draft) [ ] add original_d
529 4:94b33453f93b (draft) [ ] add original_d
530 5:cda648ca50f5 (draft) [tip ] add original_e
530 5:cda648ca50f5 (draft) [tip ] add original_e
531 $ hg push ../tmpf -f # -f because be push unstable too
531 $ hg push ../tmpf -f # -f because be push unstable too
532 pushing to ../tmpf
532 pushing to ../tmpf
533 searching for changes
533 searching for changes
534 adding changesets
534 adding changesets
535 adding manifests
535 adding manifests
536 adding file changes
536 adding file changes
537 added 6 changesets with 6 changes to 6 files (+1 heads)
537 added 6 changesets with 6 changes to 6 files (+1 heads)
538 7 new obsolescence markers
538 7 new obsolescence markers
539
539
540 no warning displayed
540 no warning displayed
541
541
542 $ hg push ../tmpf
542 $ hg push ../tmpf
543 pushing to ../tmpf
543 pushing to ../tmpf
544 searching for changes
544 searching for changes
545 no changes found
545 no changes found
546 [1]
546 [1]
547
547
548 Do not warn about new head when the new head is a successors of a remote one
548 Do not warn about new head when the new head is a successors of a remote one
549
549
550 $ hg log -G
550 $ hg log -G
551 @ 5:cda648ca50f5 (draft) [tip ] add original_e
551 @ 5:cda648ca50f5 (draft) [tip ] add original_e
552 |
552 |
553 x 4:94b33453f93b (draft) [ ] add original_d
553 x 4:94b33453f93b (draft) [ ] add original_d
554 |
554 |
555 o 3:6f9641995072 (draft) [ ] add n3w_3_c
555 o 3:6f9641995072 (draft) [ ] add n3w_3_c
556 |
556 |
557 | o 2:245bde4270cd (public) [ ] add original_c
557 | o 2:245bde4270cd (public) [ ] add original_c
558 |/
558 |/
559 o 1:7c3bad9141dc (public) [ ] add b
559 o 1:7c3bad9141dc (public) [ ] add b
560 |
560 |
561 o 0:1f0dee641bb7 (public) [ ] add a
561 o 0:1f0dee641bb7 (public) [ ] add a
562
562
563 $ hg up -q 'desc(n3w_3_c)'
563 $ hg up -q 'desc(n3w_3_c)'
564 $ mkcommit obsolete_e
564 $ mkcommit obsolete_e
565 created new head
565 created new head
566 $ hg debugobsolete `getid 'original_e'` `getid 'obsolete_e'`
566 $ hg debugobsolete `getid 'original_e'` `getid 'obsolete_e'`
567 $ hg outgoing ../tmpf # parasite hg outgoing testin
567 $ hg outgoing ../tmpf # parasite hg outgoing testin
568 comparing with ../tmpf
568 comparing with ../tmpf
569 searching for changes
569 searching for changes
570 6:3de5eca88c00 (draft) [tip ] add obsolete_e
570 6:3de5eca88c00 (draft) [tip ] add obsolete_e
571 $ hg push ../tmpf
571 $ hg push ../tmpf
572 pushing to ../tmpf
572 pushing to ../tmpf
573 searching for changes
573 searching for changes
574 adding changesets
574 adding changesets
575 adding manifests
575 adding manifests
576 adding file changes
576 adding file changes
577 added 1 changesets with 1 changes to 1 files (+1 heads)
577 added 1 changesets with 1 changes to 1 files (+1 heads)
578 1 new obsolescence markers
578 1 new obsolescence markers
579
579
580 test relevance computation
580 test relevance computation
581 ---------------------------------------
581 ---------------------------------------
582
582
583 Checking simple case of "marker relevance".
583 Checking simple case of "marker relevance".
584
584
585
585
586 Reminder of the repo situation
586 Reminder of the repo situation
587
587
588 $ hg log --hidden --graph
588 $ hg log --hidden --graph
589 @ 6:3de5eca88c00 (draft) [tip ] add obsolete_e
589 @ 6:3de5eca88c00 (draft) [tip ] add obsolete_e
590 |
590 |
591 | x 5:cda648ca50f5 (draft) [ ] add original_e
591 | x 5:cda648ca50f5 (draft) [ ] add original_e
592 | |
592 | |
593 | x 4:94b33453f93b (draft) [ ] add original_d
593 | x 4:94b33453f93b (draft) [ ] add original_d
594 |/
594 |/
595 o 3:6f9641995072 (draft) [ ] add n3w_3_c
595 o 3:6f9641995072 (draft) [ ] add n3w_3_c
596 |
596 |
597 | o 2:245bde4270cd (public) [ ] add original_c
597 | o 2:245bde4270cd (public) [ ] add original_c
598 |/
598 |/
599 o 1:7c3bad9141dc (public) [ ] add b
599 o 1:7c3bad9141dc (public) [ ] add b
600 |
600 |
601 o 0:1f0dee641bb7 (public) [ ] add a
601 o 0:1f0dee641bb7 (public) [ ] add a
602
602
603
603
604 List of all markers
604 List of all markers
605
605
606 $ hg debugobsolete
606 $ hg debugobsolete
607 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
607 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
608 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
608 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
609 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
609 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
610 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
610 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
611 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
611 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
612 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
612 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
613 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
613 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
614 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test'} (glob)
614 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test'} (glob)
615
615
616 List of changesets with no chain
616 List of changesets with no chain
617
617
618 $ hg debugobsolete --hidden --rev ::2
618 $ hg debugobsolete --hidden --rev ::2
619
619
620 List of changesets that are included on marker chain
620 List of changesets that are included on marker chain
621
621
622 $ hg debugobsolete --hidden --rev 6
622 $ hg debugobsolete --hidden --rev 6
623 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test'} (glob)
623 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test'} (glob)
624
624
625 List of changesets with a longer chain, (including a pruned children)
625 List of changesets with a longer chain, (including a pruned children)
626
626
627 $ hg debugobsolete --hidden --rev 3
627 $ hg debugobsolete --hidden --rev 3
628 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
628 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
629 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
629 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
630 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
630 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
631 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
631 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
632 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
632 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
633 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
633 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
634 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
634 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
635
635
636 List of both
636 List of both
637
637
638 $ hg debugobsolete --hidden --rev 3::6
638 $ hg debugobsolete --hidden --rev 3::6
639 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
639 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
640 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
640 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
641 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
641 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
642 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
642 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
643 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
643 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
644 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
644 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
645 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test'} (glob)
645 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test'} (glob)
646 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
646 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
647
647
648 List of all markers in JSON
649
650 $ hg debugobsolete -Tjson
651 [
652 {
653 "date": [1339.0, 0],
654 "flag": 0,
655 "metadata": {"user": "test"},
656 "precnode": "1339133913391339133913391339133913391339",
657 "succnodes": ["ca819180edb99ed25ceafb3e9584ac287e240b00"]
658 },
659 {
660 "date": [1339.0, 0],
661 "flag": 0,
662 "metadata": {"user": "test"},
663 "precnode": "1337133713371337133713371337133713371337",
664 "succnodes": ["5601fb93a350734d935195fee37f4054c529ff39"]
665 },
666 {
667 "date": [121.0, 120],
668 "flag": 12,
669 "metadata": {"user": "test"},
670 "precnode": "245bde4270cd1072a27757984f9cda8ba26f08ca",
671 "succnodes": ["cdbce2fbb16313928851e97e0d85413f3f7eb77f"]
672 },
673 {
674 "date": [1338.0, 0],
675 "flag": 1,
676 "metadata": {"user": "test"},
677 "precnode": "5601fb93a350734d935195fee37f4054c529ff39",
678 "succnodes": ["6f96419950729f3671185b847352890f074f7557"]
679 },
680 {
681 "date": [1338.0, 0],
682 "flag": 0,
683 "metadata": {"user": "test"},
684 "precnode": "ca819180edb99ed25ceafb3e9584ac287e240b00",
685 "succnodes": ["1337133713371337133713371337133713371337"]
686 },
687 {
688 "date": [1337.0, 0],
689 "flag": 0,
690 "metadata": {"user": "test"},
691 "precnode": "cdbce2fbb16313928851e97e0d85413f3f7eb77f",
692 "succnodes": ["ca819180edb99ed25ceafb3e9584ac287e240b00"]
693 },
694 {
695 "date": [0.0, 0],
696 "flag": 0,
697 "metadata": {"user": "test"},
698 "parentnodes": ["6f96419950729f3671185b847352890f074f7557"],
699 "precnode": "94b33453f93bdb8d457ef9b770851a618bf413e1",
700 "succnodes": []
701 },
702 {
703 "date": *, (glob)
704 "flag": 0,
705 "metadata": {"user": "test"},
706 "precnode": "cda648ca50f50482b7055c0b0c4c117bba6733d9",
707 "succnodes": ["3de5eca88c00aa039da7399a220f4a5221faa585"]
708 }
709 ]
710
711 Template keywords
712
713 $ hg debugobsolete -r6 -T '{succnodes % "{node|short}"} {date|shortdate}\n'
714 3de5eca88c00 ????-??-?? (glob)
715 $ hg debugobsolete -r6 -T '{join(metadata % "{key}={value}", " ")}\n'
716 user=test
717 $ hg debugobsolete -r6 -T '{metadata}\n'
718 'user': 'test'
719 $ hg debugobsolete -r6 -T '{flag} {get(metadata, "user")}\n'
720 0 test
721
648 #if serve
722 #if serve
649
723
650 Test the debug output for exchange
724 Test the debug output for exchange
651 ----------------------------------
725 ----------------------------------
652
726
653 $ hg pull ../tmpb --config 'experimental.obsmarkers-exchange-debug=True' # bundle2
727 $ hg pull ../tmpb --config 'experimental.obsmarkers-exchange-debug=True' # bundle2
654 pulling from ../tmpb
728 pulling from ../tmpb
655 searching for changes
729 searching for changes
656 no changes found
730 no changes found
657 obsmarker-exchange: 346 bytes received
731 obsmarker-exchange: 346 bytes received
658
732
659 check hgweb does not explode
733 check hgweb does not explode
660 ====================================
734 ====================================
661
735
662 $ hg unbundle $TESTDIR/bundles/hgweb+obs.hg
736 $ hg unbundle $TESTDIR/bundles/hgweb+obs.hg
663 adding changesets
737 adding changesets
664 adding manifests
738 adding manifests
665 adding file changes
739 adding file changes
666 added 62 changesets with 63 changes to 9 files (+60 heads)
740 added 62 changesets with 63 changes to 9 files (+60 heads)
667 (run 'hg heads .' to see heads, 'hg merge' to merge)
741 (run 'hg heads .' to see heads, 'hg merge' to merge)
668 $ for node in `hg log -r 'desc(babar_)' --template '{node}\n'`;
742 $ for node in `hg log -r 'desc(babar_)' --template '{node}\n'`;
669 > do
743 > do
670 > hg debugobsolete $node
744 > hg debugobsolete $node
671 > done
745 > done
672 $ hg up tip
746 $ hg up tip
673 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
747 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
674
748
675 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
749 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
676 $ cat hg.pid >> $DAEMON_PIDS
750 $ cat hg.pid >> $DAEMON_PIDS
677
751
678 check changelog view
752 check changelog view
679
753
680 $ get-with-headers.py --headeronly localhost:$HGPORT 'shortlog/'
754 $ get-with-headers.py --headeronly localhost:$HGPORT 'shortlog/'
681 200 Script output follows
755 200 Script output follows
682
756
683 check graph view
757 check graph view
684
758
685 $ get-with-headers.py --headeronly localhost:$HGPORT 'graph'
759 $ get-with-headers.py --headeronly localhost:$HGPORT 'graph'
686 200 Script output follows
760 200 Script output follows
687
761
688 check filelog view
762 check filelog view
689
763
690 $ get-with-headers.py --headeronly localhost:$HGPORT 'log/'`hg log -r . -T "{node}"`/'babar'
764 $ get-with-headers.py --headeronly localhost:$HGPORT 'log/'`hg log -r . -T "{node}"`/'babar'
691 200 Script output follows
765 200 Script output follows
692
766
693 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/68'
767 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/68'
694 200 Script output follows
768 200 Script output follows
695 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/67'
769 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/67'
696 404 Not Found
770 404 Not Found
697 [1]
771 [1]
698
772
699 check that web.view config option:
773 check that web.view config option:
700
774
701 $ killdaemons.py hg.pid
775 $ killdaemons.py hg.pid
702 $ cat >> .hg/hgrc << EOF
776 $ cat >> .hg/hgrc << EOF
703 > [web]
777 > [web]
704 > view=all
778 > view=all
705 > EOF
779 > EOF
706 $ wait
780 $ wait
707 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
781 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
708 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/67'
782 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/67'
709 200 Script output follows
783 200 Script output follows
710 $ killdaemons.py hg.pid
784 $ killdaemons.py hg.pid
711
785
712 Checking _enable=False warning if obsolete marker exists
786 Checking _enable=False warning if obsolete marker exists
713
787
714 $ echo '[experimental]' >> $HGRCPATH
788 $ echo '[experimental]' >> $HGRCPATH
715 $ echo "evolution=" >> $HGRCPATH
789 $ echo "evolution=" >> $HGRCPATH
716 $ hg log -r tip
790 $ hg log -r tip
717 obsolete feature not enabled but 68 markers found!
791 obsolete feature not enabled but 68 markers found!
718 68:c15e9edfca13 (draft) [tip ] add celestine
792 68:c15e9edfca13 (draft) [tip ] add celestine
719
793
720 reenable for later test
794 reenable for later test
721
795
722 $ echo '[experimental]' >> $HGRCPATH
796 $ echo '[experimental]' >> $HGRCPATH
723 $ echo "evolution=createmarkers,exchange" >> $HGRCPATH
797 $ echo "evolution=createmarkers,exchange" >> $HGRCPATH
724
798
725 #endif
799 #endif
726
800
727 Test incoming/outcoming with changesets obsoleted remotely, known locally
801 Test incoming/outcoming with changesets obsoleted remotely, known locally
728 ===============================================================================
802 ===============================================================================
729
803
730 This test issue 3805
804 This test issue 3805
731
805
732 $ hg init repo-issue3805
806 $ hg init repo-issue3805
733 $ cd repo-issue3805
807 $ cd repo-issue3805
734 $ echo "base" > base
808 $ echo "base" > base
735 $ hg ci -Am "base"
809 $ hg ci -Am "base"
736 adding base
810 adding base
737 $ echo "foo" > foo
811 $ echo "foo" > foo
738 $ hg ci -Am "A"
812 $ hg ci -Am "A"
739 adding foo
813 adding foo
740 $ hg clone . ../other-issue3805
814 $ hg clone . ../other-issue3805
741 updating to branch default
815 updating to branch default
742 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
816 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
743 $ echo "bar" >> foo
817 $ echo "bar" >> foo
744 $ hg ci --amend
818 $ hg ci --amend
745 $ cd ../other-issue3805
819 $ cd ../other-issue3805
746 $ hg log -G
820 $ hg log -G
747 @ 1:29f0c6921ddd (draft) [tip ] A
821 @ 1:29f0c6921ddd (draft) [tip ] A
748 |
822 |
749 o 0:d20a80d4def3 (draft) [ ] base
823 o 0:d20a80d4def3 (draft) [ ] base
750
824
751 $ hg log -G -R ../repo-issue3805
825 $ hg log -G -R ../repo-issue3805
752 @ 3:323a9c3ddd91 (draft) [tip ] A
826 @ 3:323a9c3ddd91 (draft) [tip ] A
753 |
827 |
754 o 0:d20a80d4def3 (draft) [ ] base
828 o 0:d20a80d4def3 (draft) [ ] base
755
829
756 $ hg incoming
830 $ hg incoming
757 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
831 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
758 searching for changes
832 searching for changes
759 3:323a9c3ddd91 (draft) [tip ] A
833 3:323a9c3ddd91 (draft) [tip ] A
760 $ hg incoming --bundle ../issue3805.hg
834 $ hg incoming --bundle ../issue3805.hg
761 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
835 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
762 searching for changes
836 searching for changes
763 3:323a9c3ddd91 (draft) [tip ] A
837 3:323a9c3ddd91 (draft) [tip ] A
764 $ hg outgoing
838 $ hg outgoing
765 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
839 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
766 searching for changes
840 searching for changes
767 1:29f0c6921ddd (draft) [tip ] A
841 1:29f0c6921ddd (draft) [tip ] A
768
842
769 #if serve
843 #if serve
770
844
771 $ hg serve -R ../repo-issue3805 -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
845 $ hg serve -R ../repo-issue3805 -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
772 $ cat hg.pid >> $DAEMON_PIDS
846 $ cat hg.pid >> $DAEMON_PIDS
773
847
774 $ hg incoming http://localhost:$HGPORT
848 $ hg incoming http://localhost:$HGPORT
775 comparing with http://localhost:$HGPORT/
849 comparing with http://localhost:$HGPORT/
776 searching for changes
850 searching for changes
777 2:323a9c3ddd91 (draft) [tip ] A
851 2:323a9c3ddd91 (draft) [tip ] A
778 $ hg outgoing http://localhost:$HGPORT
852 $ hg outgoing http://localhost:$HGPORT
779 comparing with http://localhost:$HGPORT/
853 comparing with http://localhost:$HGPORT/
780 searching for changes
854 searching for changes
781 1:29f0c6921ddd (draft) [tip ] A
855 1:29f0c6921ddd (draft) [tip ] A
782
856
783 $ killdaemons.py
857 $ killdaemons.py
784
858
785 #endif
859 #endif
786
860
787 This test issue 3814
861 This test issue 3814
788
862
789 (nothing to push but locally hidden changeset)
863 (nothing to push but locally hidden changeset)
790
864
791 $ cd ..
865 $ cd ..
792 $ hg init repo-issue3814
866 $ hg init repo-issue3814
793 $ cd repo-issue3805
867 $ cd repo-issue3805
794 $ hg push -r 323a9c3ddd91 ../repo-issue3814
868 $ hg push -r 323a9c3ddd91 ../repo-issue3814
795 pushing to ../repo-issue3814
869 pushing to ../repo-issue3814
796 searching for changes
870 searching for changes
797 adding changesets
871 adding changesets
798 adding manifests
872 adding manifests
799 adding file changes
873 adding file changes
800 added 2 changesets with 2 changes to 2 files
874 added 2 changesets with 2 changes to 2 files
801 2 new obsolescence markers
875 2 new obsolescence markers
802 $ hg out ../repo-issue3814
876 $ hg out ../repo-issue3814
803 comparing with ../repo-issue3814
877 comparing with ../repo-issue3814
804 searching for changes
878 searching for changes
805 no changes found
879 no changes found
806 [1]
880 [1]
807
881
808 Test that a local tag blocks a changeset from being hidden
882 Test that a local tag blocks a changeset from being hidden
809
883
810 $ hg tag -l visible -r 1 --hidden
884 $ hg tag -l visible -r 1 --hidden
811 $ hg log -G
885 $ hg log -G
812 @ 3:323a9c3ddd91 (draft) [tip ] A
886 @ 3:323a9c3ddd91 (draft) [tip ] A
813 |
887 |
814 | x 1:29f0c6921ddd (draft) [visible ] A
888 | x 1:29f0c6921ddd (draft) [visible ] A
815 |/
889 |/
816 o 0:d20a80d4def3 (draft) [ ] base
890 o 0:d20a80d4def3 (draft) [ ] base
817
891
818 Test that removing a local tag does not cause some commands to fail
892 Test that removing a local tag does not cause some commands to fail
819
893
820 $ hg tag -l -r tip tiptag
894 $ hg tag -l -r tip tiptag
821 $ hg tags
895 $ hg tags
822 tiptag 3:323a9c3ddd91
896 tiptag 3:323a9c3ddd91
823 tip 3:323a9c3ddd91
897 tip 3:323a9c3ddd91
824 visible 1:29f0c6921ddd
898 visible 1:29f0c6921ddd
825 $ hg --config extensions.strip= strip -r tip --no-backup
899 $ hg --config extensions.strip= strip -r tip --no-backup
826 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
900 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
827 $ hg tags
901 $ hg tags
828 visible 1:29f0c6921ddd
902 visible 1:29f0c6921ddd
829 tip 1:29f0c6921ddd
903 tip 1:29f0c6921ddd
830
904
831 Test bundle overlay onto hidden revision
905 Test bundle overlay onto hidden revision
832
906
833 $ cd ..
907 $ cd ..
834 $ hg init repo-bundleoverlay
908 $ hg init repo-bundleoverlay
835 $ cd repo-bundleoverlay
909 $ cd repo-bundleoverlay
836 $ echo "A" > foo
910 $ echo "A" > foo
837 $ hg ci -Am "A"
911 $ hg ci -Am "A"
838 adding foo
912 adding foo
839 $ echo "B" >> foo
913 $ echo "B" >> foo
840 $ hg ci -m "B"
914 $ hg ci -m "B"
841 $ hg up 0
915 $ hg up 0
842 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
916 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
843 $ echo "C" >> foo
917 $ echo "C" >> foo
844 $ hg ci -m "C"
918 $ hg ci -m "C"
845 created new head
919 created new head
846 $ hg log -G
920 $ hg log -G
847 @ 2:c186d7714947 (draft) [tip ] C
921 @ 2:c186d7714947 (draft) [tip ] C
848 |
922 |
849 | o 1:44526ebb0f98 (draft) [ ] B
923 | o 1:44526ebb0f98 (draft) [ ] B
850 |/
924 |/
851 o 0:4b34ecfb0d56 (draft) [ ] A
925 o 0:4b34ecfb0d56 (draft) [ ] A
852
926
853
927
854 $ hg clone -r1 . ../other-bundleoverlay
928 $ hg clone -r1 . ../other-bundleoverlay
855 adding changesets
929 adding changesets
856 adding manifests
930 adding manifests
857 adding file changes
931 adding file changes
858 added 2 changesets with 2 changes to 1 files
932 added 2 changesets with 2 changes to 1 files
859 updating to branch default
933 updating to branch default
860 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
934 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
861 $ cd ../other-bundleoverlay
935 $ cd ../other-bundleoverlay
862 $ echo "B+" >> foo
936 $ echo "B+" >> foo
863 $ hg ci --amend -m "B+"
937 $ hg ci --amend -m "B+"
864 $ hg log -G --hidden
938 $ hg log -G --hidden
865 @ 3:b7d587542d40 (draft) [tip ] B+
939 @ 3:b7d587542d40 (draft) [tip ] B+
866 |
940 |
867 | x 2:eb95e9297e18 (draft) [ ] temporary amend commit for 44526ebb0f98
941 | x 2:eb95e9297e18 (draft) [ ] temporary amend commit for 44526ebb0f98
868 | |
942 | |
869 | x 1:44526ebb0f98 (draft) [ ] B
943 | x 1:44526ebb0f98 (draft) [ ] B
870 |/
944 |/
871 o 0:4b34ecfb0d56 (draft) [ ] A
945 o 0:4b34ecfb0d56 (draft) [ ] A
872
946
873
947
874 $ hg incoming ../repo-bundleoverlay --bundle ../bundleoverlay.hg
948 $ hg incoming ../repo-bundleoverlay --bundle ../bundleoverlay.hg
875 comparing with ../repo-bundleoverlay
949 comparing with ../repo-bundleoverlay
876 searching for changes
950 searching for changes
877 1:44526ebb0f98 (draft) [ ] B
951 1:44526ebb0f98 (draft) [ ] B
878 2:c186d7714947 (draft) [tip ] C
952 2:c186d7714947 (draft) [tip ] C
879 $ hg log -G -R ../bundleoverlay.hg
953 $ hg log -G -R ../bundleoverlay.hg
880 o 4:c186d7714947 (draft) [tip ] C
954 o 4:c186d7714947 (draft) [tip ] C
881 |
955 |
882 | @ 3:b7d587542d40 (draft) [ ] B+
956 | @ 3:b7d587542d40 (draft) [ ] B+
883 |/
957 |/
884 o 0:4b34ecfb0d56 (draft) [ ] A
958 o 0:4b34ecfb0d56 (draft) [ ] A
885
959
886
960
887 #if serve
961 #if serve
888
962
889 Test issue 4506
963 Test issue 4506
890
964
891 $ cd ..
965 $ cd ..
892 $ hg init repo-issue4506
966 $ hg init repo-issue4506
893 $ cd repo-issue4506
967 $ cd repo-issue4506
894 $ echo "0" > foo
968 $ echo "0" > foo
895 $ hg add foo
969 $ hg add foo
896 $ hg ci -m "content-0"
970 $ hg ci -m "content-0"
897
971
898 $ hg up null
972 $ hg up null
899 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
973 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
900 $ echo "1" > bar
974 $ echo "1" > bar
901 $ hg add bar
975 $ hg add bar
902 $ hg ci -m "content-1"
976 $ hg ci -m "content-1"
903 created new head
977 created new head
904 $ hg up 0
978 $ hg up 0
905 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
979 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
906 $ hg graft 1
980 $ hg graft 1
907 grafting 1:1c9eddb02162 "content-1" (tip)
981 grafting 1:1c9eddb02162 "content-1" (tip)
908
982
909 $ hg debugobsolete `hg log -r1 -T'{node}'` `hg log -r2 -T'{node}'`
983 $ hg debugobsolete `hg log -r1 -T'{node}'` `hg log -r2 -T'{node}'`
910
984
911 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
985 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
912 $ cat hg.pid >> $DAEMON_PIDS
986 $ cat hg.pid >> $DAEMON_PIDS
913
987
914 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/1'
988 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/1'
915 404 Not Found
989 404 Not Found
916 [1]
990 [1]
917 $ get-with-headers.py --headeronly localhost:$HGPORT 'file/tip/bar'
991 $ get-with-headers.py --headeronly localhost:$HGPORT 'file/tip/bar'
918 200 Script output follows
992 200 Script output follows
919 $ get-with-headers.py --headeronly localhost:$HGPORT 'annotate/tip/bar'
993 $ get-with-headers.py --headeronly localhost:$HGPORT 'annotate/tip/bar'
920 200 Script output follows
994 200 Script output follows
921
995
922 $ killdaemons.py
996 $ killdaemons.py
923
997
924 #endif
998 #endif
925
999
926 Test heads computation on pending index changes with obsolescence markers
1000 Test heads computation on pending index changes with obsolescence markers
927 $ cd ..
1001 $ cd ..
928 $ cat >$TESTTMP/test_extension.py << EOF
1002 $ cat >$TESTTMP/test_extension.py << EOF
929 > from mercurial import cmdutil
1003 > from mercurial import cmdutil
930 > from mercurial.i18n import _
1004 > from mercurial.i18n import _
931 >
1005 >
932 > cmdtable = {}
1006 > cmdtable = {}
933 > command = cmdutil.command(cmdtable)
1007 > command = cmdutil.command(cmdtable)
934 > @command("amendtransient",[], _('hg amendtransient [rev]'))
1008 > @command("amendtransient",[], _('hg amendtransient [rev]'))
935 > def amend(ui, repo, *pats, **opts):
1009 > def amend(ui, repo, *pats, **opts):
936 > def commitfunc(ui, repo, message, match, opts):
1010 > def commitfunc(ui, repo, message, match, opts):
937 > return repo.commit(message, repo['.'].user(), repo['.'].date(), match)
1011 > return repo.commit(message, repo['.'].user(), repo['.'].date(), match)
938 > opts['message'] = 'Test'
1012 > opts['message'] = 'Test'
939 > opts['logfile'] = None
1013 > opts['logfile'] = None
940 > cmdutil.amend(ui, repo, commitfunc, repo['.'], {}, pats, opts)
1014 > cmdutil.amend(ui, repo, commitfunc, repo['.'], {}, pats, opts)
941 > ui.write('%s\n' % repo.changelog.headrevs())
1015 > ui.write('%s\n' % repo.changelog.headrevs())
942 > EOF
1016 > EOF
943 $ cat >> $HGRCPATH << EOF
1017 $ cat >> $HGRCPATH << EOF
944 > [extensions]
1018 > [extensions]
945 > testextension=$TESTTMP/test_extension.py
1019 > testextension=$TESTTMP/test_extension.py
946 > EOF
1020 > EOF
947 $ hg init repo-issue-nativerevs-pending-changes
1021 $ hg init repo-issue-nativerevs-pending-changes
948 $ cd repo-issue-nativerevs-pending-changes
1022 $ cd repo-issue-nativerevs-pending-changes
949 $ mkcommit a
1023 $ mkcommit a
950 $ mkcommit b
1024 $ mkcommit b
951 $ hg up ".^"
1025 $ hg up ".^"
952 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1026 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
953 $ echo aa > a
1027 $ echo aa > a
954 $ hg amendtransient
1028 $ hg amendtransient
955 [1, 3]
1029 [1, 3]
956
1030
957 Check that corrupted hidden cache does not crash
1031 Check that corrupted hidden cache does not crash
958
1032
959 $ printf "" > .hg/cache/hidden
1033 $ printf "" > .hg/cache/hidden
960 $ hg log -r . -T '{node}' --debug
1034 $ hg log -r . -T '{node}' --debug
961 corrupted hidden cache
1035 corrupted hidden cache
962 8fd96dfc63e51ed5a8af1bec18eb4b19dbf83812 (no-eol)
1036 8fd96dfc63e51ed5a8af1bec18eb4b19dbf83812 (no-eol)
963 $ hg log -r . -T '{node}' --debug
1037 $ hg log -r . -T '{node}' --debug
964 8fd96dfc63e51ed5a8af1bec18eb4b19dbf83812 (no-eol)
1038 8fd96dfc63e51ed5a8af1bec18eb4b19dbf83812 (no-eol)
965
1039
966 #if unix-permissions
1040 #if unix-permissions
967 Check that wrong hidden cache permission does not crash
1041 Check that wrong hidden cache permission does not crash
968
1042
969 $ chmod 000 .hg/cache/hidden
1043 $ chmod 000 .hg/cache/hidden
970 $ hg log -r . -T '{node}' --debug
1044 $ hg log -r . -T '{node}' --debug
971 cannot read hidden cache
1045 cannot read hidden cache
972 error writing hidden changesets cache
1046 error writing hidden changesets cache
973 8fd96dfc63e51ed5a8af1bec18eb4b19dbf83812 (no-eol)
1047 8fd96dfc63e51ed5a8af1bec18eb4b19dbf83812 (no-eol)
974 #endif
1048 #endif
975
1049
976 Test cache consistency for the visible filter
1050 Test cache consistency for the visible filter
977 1) We want to make sure that the cached filtered revs are invalidated when
1051 1) We want to make sure that the cached filtered revs are invalidated when
978 bookmarks change
1052 bookmarks change
979 $ cd ..
1053 $ cd ..
980 $ cat >$TESTTMP/test_extension.py << EOF
1054 $ cat >$TESTTMP/test_extension.py << EOF
981 > import weakref
1055 > import weakref
982 > from mercurial import cmdutil, extensions, bookmarks, repoview
1056 > from mercurial import cmdutil, extensions, bookmarks, repoview
983 > def _bookmarkchanged(orig, bkmstoreinst, *args, **kwargs):
1057 > def _bookmarkchanged(orig, bkmstoreinst, *args, **kwargs):
984 > reporef = weakref.ref(bkmstoreinst._repo)
1058 > reporef = weakref.ref(bkmstoreinst._repo)
985 > def trhook(tr):
1059 > def trhook(tr):
986 > repo = reporef()
1060 > repo = reporef()
987 > hidden1 = repoview.computehidden(repo)
1061 > hidden1 = repoview.computehidden(repo)
988 > hidden = repoview.filterrevs(repo, 'visible')
1062 > hidden = repoview.filterrevs(repo, 'visible')
989 > if sorted(hidden1) != sorted(hidden):
1063 > if sorted(hidden1) != sorted(hidden):
990 > print "cache inconsistency"
1064 > print "cache inconsistency"
991 > bkmstoreinst._repo.currenttransaction().addpostclose('test_extension', trhook)
1065 > bkmstoreinst._repo.currenttransaction().addpostclose('test_extension', trhook)
992 > orig(bkmstoreinst, *args, **kwargs)
1066 > orig(bkmstoreinst, *args, **kwargs)
993 > def extsetup(ui):
1067 > def extsetup(ui):
994 > extensions.wrapfunction(bookmarks.bmstore, 'recordchange',
1068 > extensions.wrapfunction(bookmarks.bmstore, 'recordchange',
995 > _bookmarkchanged)
1069 > _bookmarkchanged)
996 > EOF
1070 > EOF
997
1071
998 $ hg init repo-cache-inconsistency
1072 $ hg init repo-cache-inconsistency
999 $ cd repo-issue-nativerevs-pending-changes
1073 $ cd repo-issue-nativerevs-pending-changes
1000 $ mkcommit a
1074 $ mkcommit a
1001 a already tracked!
1075 a already tracked!
1002 $ mkcommit b
1076 $ mkcommit b
1003 $ hg id
1077 $ hg id
1004 13bedc178fce tip
1078 13bedc178fce tip
1005 $ echo "hello" > b
1079 $ echo "hello" > b
1006 $ hg commit --amend -m "message"
1080 $ hg commit --amend -m "message"
1007 $ hg book bookb -r 13bedc178fce --hidden
1081 $ hg book bookb -r 13bedc178fce --hidden
1008 $ hg log -r 13bedc178fce
1082 $ hg log -r 13bedc178fce
1009 5:13bedc178fce (draft) [ bookb] add b
1083 5:13bedc178fce (draft) [ bookb] add b
1010 $ hg book -d bookb
1084 $ hg book -d bookb
1011 $ hg log -r 13bedc178fce
1085 $ hg log -r 13bedc178fce
1012 abort: hidden revision '13bedc178fce'!
1086 abort: hidden revision '13bedc178fce'!
1013 (use --hidden to access hidden revisions)
1087 (use --hidden to access hidden revisions)
1014 [255]
1088 [255]
1015
1089
1016 Empty out the test extension, as it isn't compatible with later parts
1090 Empty out the test extension, as it isn't compatible with later parts
1017 of the test.
1091 of the test.
1018 $ echo > $TESTTMP/test_extension.py
1092 $ echo > $TESTTMP/test_extension.py
1019
1093
1020 Test ability to pull changeset with locally applying obsolescence markers
1094 Test ability to pull changeset with locally applying obsolescence markers
1021 (issue4945)
1095 (issue4945)
1022
1096
1023 $ cd ..
1097 $ cd ..
1024 $ hg init issue4845
1098 $ hg init issue4845
1025 $ cd issue4845
1099 $ cd issue4845
1026
1100
1027 $ echo foo > f0
1101 $ echo foo > f0
1028 $ hg add f0
1102 $ hg add f0
1029 $ hg ci -m '0'
1103 $ hg ci -m '0'
1030 $ echo foo > f1
1104 $ echo foo > f1
1031 $ hg add f1
1105 $ hg add f1
1032 $ hg ci -m '1'
1106 $ hg ci -m '1'
1033 $ echo foo > f2
1107 $ echo foo > f2
1034 $ hg add f2
1108 $ hg add f2
1035 $ hg ci -m '2'
1109 $ hg ci -m '2'
1036
1110
1037 $ echo bar > f2
1111 $ echo bar > f2
1038 $ hg commit --amend --config experimetnal.evolution=createmarkers
1112 $ hg commit --amend --config experimetnal.evolution=createmarkers
1039 $ hg log -G
1113 $ hg log -G
1040 @ 4:b0551702f918 (draft) [tip ] 2
1114 @ 4:b0551702f918 (draft) [tip ] 2
1041 |
1115 |
1042 o 1:e016b03fd86f (draft) [ ] 1
1116 o 1:e016b03fd86f (draft) [ ] 1
1043 |
1117 |
1044 o 0:a78f55e5508c (draft) [ ] 0
1118 o 0:a78f55e5508c (draft) [ ] 0
1045
1119
1046 $ hg log -G --hidden
1120 $ hg log -G --hidden
1047 @ 4:b0551702f918 (draft) [tip ] 2
1121 @ 4:b0551702f918 (draft) [tip ] 2
1048 |
1122 |
1049 | x 3:f27abbcc1f77 (draft) [ ] temporary amend commit for e008cf283490
1123 | x 3:f27abbcc1f77 (draft) [ ] temporary amend commit for e008cf283490
1050 | |
1124 | |
1051 | x 2:e008cf283490 (draft) [ ] 2
1125 | x 2:e008cf283490 (draft) [ ] 2
1052 |/
1126 |/
1053 o 1:e016b03fd86f (draft) [ ] 1
1127 o 1:e016b03fd86f (draft) [ ] 1
1054 |
1128 |
1055 o 0:a78f55e5508c (draft) [ ] 0
1129 o 0:a78f55e5508c (draft) [ ] 0
1056
1130
1057
1131
1058 $ hg strip -r 1 --config extensions.strip=
1132 $ hg strip -r 1 --config extensions.strip=
1059 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1133 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1060 saved backup bundle to $TESTTMP/tmpe/issue4845/.hg/strip-backup/e016b03fd86f-c41c6bcc-backup.hg (glob)
1134 saved backup bundle to $TESTTMP/tmpe/issue4845/.hg/strip-backup/e016b03fd86f-c41c6bcc-backup.hg (glob)
1061 $ hg log -G
1135 $ hg log -G
1062 @ 0:a78f55e5508c (draft) [tip ] 0
1136 @ 0:a78f55e5508c (draft) [tip ] 0
1063
1137
1064 $ hg log -G --hidden
1138 $ hg log -G --hidden
1065 @ 0:a78f55e5508c (draft) [tip ] 0
1139 @ 0:a78f55e5508c (draft) [tip ] 0
1066
1140
1067
1141
1068 $ hg pull .hg/strip-backup/*
1142 $ hg pull .hg/strip-backup/*
1069 pulling from .hg/strip-backup/e016b03fd86f-c41c6bcc-backup.hg
1143 pulling from .hg/strip-backup/e016b03fd86f-c41c6bcc-backup.hg
1070 searching for changes
1144 searching for changes
1071 adding changesets
1145 adding changesets
1072 adding manifests
1146 adding manifests
1073 adding file changes
1147 adding file changes
1074 added 2 changesets with 2 changes to 2 files
1148 added 2 changesets with 2 changes to 2 files
1075 (run 'hg update' to get a working copy)
1149 (run 'hg update' to get a working copy)
1076 $ hg log -G
1150 $ hg log -G
1077 o 2:b0551702f918 (draft) [tip ] 2
1151 o 2:b0551702f918 (draft) [tip ] 2
1078 |
1152 |
1079 o 1:e016b03fd86f (draft) [ ] 1
1153 o 1:e016b03fd86f (draft) [ ] 1
1080 |
1154 |
1081 @ 0:a78f55e5508c (draft) [ ] 0
1155 @ 0:a78f55e5508c (draft) [ ] 0
1082
1156
1083 $ hg log -G --hidden
1157 $ hg log -G --hidden
1084 o 2:b0551702f918 (draft) [tip ] 2
1158 o 2:b0551702f918 (draft) [tip ] 2
1085 |
1159 |
1086 o 1:e016b03fd86f (draft) [ ] 1
1160 o 1:e016b03fd86f (draft) [ ] 1
1087 |
1161 |
1088 @ 0:a78f55e5508c (draft) [ ] 0
1162 @ 0:a78f55e5508c (draft) [ ] 0
1089
1163
1090 Test that 'hg debugobsolete --index --rev' can show indices of obsmarkers when
1164 Test that 'hg debugobsolete --index --rev' can show indices of obsmarkers when
1091 only a subset of those are displayed (because of --rev option)
1165 only a subset of those are displayed (because of --rev option)
1092 $ hg init doindexrev
1166 $ hg init doindexrev
1093 $ cd doindexrev
1167 $ cd doindexrev
1094 $ echo a > a
1168 $ echo a > a
1095 $ hg ci -Am a
1169 $ hg ci -Am a
1096 adding a
1170 adding a
1097 $ hg ci --amend -m aa
1171 $ hg ci --amend -m aa
1098 $ echo b > b
1172 $ echo b > b
1099 $ hg ci -Am b
1173 $ hg ci -Am b
1100 adding b
1174 adding b
1101 $ hg ci --amend -m bb
1175 $ hg ci --amend -m bb
1102 $ echo c > c
1176 $ echo c > c
1103 $ hg ci -Am c
1177 $ hg ci -Am c
1104 adding c
1178 adding c
1105 $ hg ci --amend -m cc
1179 $ hg ci --amend -m cc
1106 $ echo d > d
1180 $ echo d > d
1107 $ hg ci -Am d
1181 $ hg ci -Am d
1108 adding d
1182 adding d
1109 $ hg ci --amend -m dd
1183 $ hg ci --amend -m dd
1110 $ hg debugobsolete --index --rev "3+7"
1184 $ hg debugobsolete --index --rev "3+7"
1111 1 6fdef60fcbabbd3d50e9b9cbc2a240724b91a5e1 d27fb9b066076fd921277a4b9e8b9cb48c95bc6a 0 \(.*\) {'user': 'test'} (re)
1185 1 6fdef60fcbabbd3d50e9b9cbc2a240724b91a5e1 d27fb9b066076fd921277a4b9e8b9cb48c95bc6a 0 \(.*\) {'user': 'test'} (re)
1112 3 4715cf767440ed891755448016c2b8cf70760c30 7ae79c5d60f049c7b0dd02f5f25b9d60aaf7b36d 0 \(.*\) {'user': 'test'} (re)
1186 3 4715cf767440ed891755448016c2b8cf70760c30 7ae79c5d60f049c7b0dd02f5f25b9d60aaf7b36d 0 \(.*\) {'user': 'test'} (re)
1187 $ hg debugobsolete --index --rev "3+7" -Tjson
1188 [
1189 {
1190 "date": *, (glob)
1191 "flag": 0,
1192 "index": 1,
1193 "metadata": {"user": "test"},
1194 "precnode": "6fdef60fcbabbd3d50e9b9cbc2a240724b91a5e1",
1195 "succnodes": ["d27fb9b066076fd921277a4b9e8b9cb48c95bc6a"]
1196 },
1197 {
1198 "date": *, (glob)
1199 "flag": 0,
1200 "index": 3,
1201 "metadata": {"user": "test"},
1202 "precnode": "4715cf767440ed891755448016c2b8cf70760c30",
1203 "succnodes": ["7ae79c5d60f049c7b0dd02f5f25b9d60aaf7b36d"]
1204 }
1205 ]
1113
1206
1114 Test the --delete option of debugobsolete command
1207 Test the --delete option of debugobsolete command
1115 $ hg debugobsolete --index
1208 $ hg debugobsolete --index
1116 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b f9bd49731b0b175e42992a3c8fa6c678b2bc11f1 0 \(.*\) {'user': 'test'} (re)
1209 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b f9bd49731b0b175e42992a3c8fa6c678b2bc11f1 0 \(.*\) {'user': 'test'} (re)
1117 1 6fdef60fcbabbd3d50e9b9cbc2a240724b91a5e1 d27fb9b066076fd921277a4b9e8b9cb48c95bc6a 0 \(.*\) {'user': 'test'} (re)
1210 1 6fdef60fcbabbd3d50e9b9cbc2a240724b91a5e1 d27fb9b066076fd921277a4b9e8b9cb48c95bc6a 0 \(.*\) {'user': 'test'} (re)
1118 2 1ab51af8f9b41ef8c7f6f3312d4706d870b1fb74 29346082e4a9e27042b62d2da0e2de211c027621 0 \(.*\) {'user': 'test'} (re)
1211 2 1ab51af8f9b41ef8c7f6f3312d4706d870b1fb74 29346082e4a9e27042b62d2da0e2de211c027621 0 \(.*\) {'user': 'test'} (re)
1119 3 4715cf767440ed891755448016c2b8cf70760c30 7ae79c5d60f049c7b0dd02f5f25b9d60aaf7b36d 0 \(.*\) {'user': 'test'} (re)
1212 3 4715cf767440ed891755448016c2b8cf70760c30 7ae79c5d60f049c7b0dd02f5f25b9d60aaf7b36d 0 \(.*\) {'user': 'test'} (re)
1120 $ hg debugobsolete --delete 1 --delete 3
1213 $ hg debugobsolete --delete 1 --delete 3
1121 deleted 2 obsolescense markers
1214 deleted 2 obsolescense markers
1122 $ hg debugobsolete
1215 $ hg debugobsolete
1123 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b f9bd49731b0b175e42992a3c8fa6c678b2bc11f1 0 \(.*\) {'user': 'test'} (re)
1216 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b f9bd49731b0b175e42992a3c8fa6c678b2bc11f1 0 \(.*\) {'user': 'test'} (re)
1124 1ab51af8f9b41ef8c7f6f3312d4706d870b1fb74 29346082e4a9e27042b62d2da0e2de211c027621 0 \(.*\) {'user': 'test'} (re)
1217 1ab51af8f9b41ef8c7f6f3312d4706d870b1fb74 29346082e4a9e27042b62d2da0e2de211c027621 0 \(.*\) {'user': 'test'} (re)
1125 $ cd ..
1218 $ cd ..
1126
1219
General Comments 0
You need to be logged in to leave comments. Login now