##// END OF EJS Templates
server: move cmdutil.service() to new module (API)...
Yuya Nishihara -
r30506:d9d8d78e default
parent child Browse files
Show More
@@ -0,0 +1,107 b''
1 # server.py - utility and factory of server
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
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.
7
8 from __future__ import absolute_import
9
10 import errno
11 import os
12 import sys
13 import tempfile
14
15 from .i18n import _
16
17 from . import (
18 error,
19 util,
20 )
21
22 def runservice(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
23 runargs=None, appendpid=False):
24 '''Run a command as a service.'''
25
26 def writepid(pid):
27 if opts['pid_file']:
28 if appendpid:
29 mode = 'a'
30 else:
31 mode = 'w'
32 fp = open(opts['pid_file'], mode)
33 fp.write(str(pid) + '\n')
34 fp.close()
35
36 if opts['daemon'] and not opts['daemon_postexec']:
37 # Signal child process startup with file removal
38 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
39 os.close(lockfd)
40 try:
41 if not runargs:
42 runargs = util.hgcmd() + sys.argv[1:]
43 runargs.append('--daemon-postexec=unlink:%s' % lockpath)
44 # Don't pass --cwd to the child process, because we've already
45 # changed directory.
46 for i in xrange(1, len(runargs)):
47 if runargs[i].startswith('--cwd='):
48 del runargs[i]
49 break
50 elif runargs[i].startswith('--cwd'):
51 del runargs[i:i + 2]
52 break
53 def condfn():
54 return not os.path.exists(lockpath)
55 pid = util.rundetached(runargs, condfn)
56 if pid < 0:
57 raise error.Abort(_('child process failed to start'))
58 writepid(pid)
59 finally:
60 try:
61 os.unlink(lockpath)
62 except OSError as e:
63 if e.errno != errno.ENOENT:
64 raise
65 if parentfn:
66 return parentfn(pid)
67 else:
68 return
69
70 if initfn:
71 initfn()
72
73 if not opts['daemon']:
74 writepid(util.getpid())
75
76 if opts['daemon_postexec']:
77 try:
78 os.setsid()
79 except AttributeError:
80 pass
81 for inst in opts['daemon_postexec']:
82 if inst.startswith('unlink:'):
83 lockpath = inst[7:]
84 os.unlink(lockpath)
85 elif inst.startswith('chdir:'):
86 os.chdir(inst[6:])
87 elif inst != 'none':
88 raise error.Abort(_('invalid value for --daemon-postexec: %s')
89 % inst)
90 util.hidewindow()
91 util.stdout.flush()
92 util.stderr.flush()
93
94 nullfd = os.open(os.devnull, os.O_RDWR)
95 logfilefd = nullfd
96 if logfile:
97 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
98 os.dup2(nullfd, 0)
99 os.dup2(logfilefd, 1)
100 os.dup2(logfilefd, 2)
101 if nullfd not in (0, 1, 2):
102 os.close(nullfd)
103 if logfile and logfilefd not in (0, 1, 2):
104 os.close(logfilefd)
105
106 if runfn:
107 return runfn()
@@ -1,3528 +1,3440 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
14 import tempfile
13 import tempfile
15
14
16 from .i18n import _
15 from .i18n import _
17 from .node import (
16 from .node import (
18 bin,
17 bin,
19 hex,
18 hex,
20 nullid,
19 nullid,
21 nullrev,
20 nullrev,
22 short,
21 short,
23 )
22 )
24
23
25 from . import (
24 from . import (
26 bookmarks,
25 bookmarks,
27 changelog,
26 changelog,
28 copies,
27 copies,
29 crecord as crecordmod,
28 crecord as crecordmod,
30 dirstateguard as dirstateguardmod,
29 dirstateguard as dirstateguardmod,
31 encoding,
30 encoding,
32 error,
31 error,
33 formatter,
32 formatter,
34 graphmod,
33 graphmod,
35 lock as lockmod,
34 lock as lockmod,
36 match as matchmod,
35 match as matchmod,
37 mergeutil,
36 mergeutil,
38 obsolete,
37 obsolete,
39 patch,
38 patch,
40 pathutil,
39 pathutil,
41 phases,
40 phases,
42 repair,
41 repair,
43 revlog,
42 revlog,
44 revset,
43 revset,
45 scmutil,
44 scmutil,
46 templatekw,
45 templatekw,
47 templater,
46 templater,
48 util,
47 util,
49 )
48 )
50 stringio = util.stringio
49 stringio = util.stringio
51
50
52 def ishunk(x):
51 def ishunk(x):
53 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
52 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
54 return isinstance(x, hunkclasses)
53 return isinstance(x, hunkclasses)
55
54
56 def newandmodified(chunks, originalchunks):
55 def newandmodified(chunks, originalchunks):
57 newlyaddedandmodifiedfiles = set()
56 newlyaddedandmodifiedfiles = set()
58 for chunk in chunks:
57 for chunk in chunks:
59 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
58 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
60 originalchunks:
59 originalchunks:
61 newlyaddedandmodifiedfiles.add(chunk.header.filename())
60 newlyaddedandmodifiedfiles.add(chunk.header.filename())
62 return newlyaddedandmodifiedfiles
61 return newlyaddedandmodifiedfiles
63
62
64 def parsealiases(cmd):
63 def parsealiases(cmd):
65 return cmd.lstrip("^").split("|")
64 return cmd.lstrip("^").split("|")
66
65
67 def setupwrapcolorwrite(ui):
66 def setupwrapcolorwrite(ui):
68 # wrap ui.write so diff output can be labeled/colorized
67 # wrap ui.write so diff output can be labeled/colorized
69 def wrapwrite(orig, *args, **kw):
68 def wrapwrite(orig, *args, **kw):
70 label = kw.pop('label', '')
69 label = kw.pop('label', '')
71 for chunk, l in patch.difflabel(lambda: args):
70 for chunk, l in patch.difflabel(lambda: args):
72 orig(chunk, label=label + l)
71 orig(chunk, label=label + l)
73
72
74 oldwrite = ui.write
73 oldwrite = ui.write
75 def wrap(*args, **kwargs):
74 def wrap(*args, **kwargs):
76 return wrapwrite(oldwrite, *args, **kwargs)
75 return wrapwrite(oldwrite, *args, **kwargs)
77 setattr(ui, 'write', wrap)
76 setattr(ui, 'write', wrap)
78 return oldwrite
77 return oldwrite
79
78
80 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
79 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
81 if usecurses:
80 if usecurses:
82 if testfile:
81 if testfile:
83 recordfn = crecordmod.testdecorator(testfile,
82 recordfn = crecordmod.testdecorator(testfile,
84 crecordmod.testchunkselector)
83 crecordmod.testchunkselector)
85 else:
84 else:
86 recordfn = crecordmod.chunkselector
85 recordfn = crecordmod.chunkselector
87
86
88 return crecordmod.filterpatch(ui, originalhunks, recordfn)
87 return crecordmod.filterpatch(ui, originalhunks, recordfn)
89
88
90 else:
89 else:
91 return patch.filterpatch(ui, originalhunks, operation)
90 return patch.filterpatch(ui, originalhunks, operation)
92
91
93 def recordfilter(ui, originalhunks, operation=None):
92 def recordfilter(ui, originalhunks, operation=None):
94 """ Prompts the user to filter the originalhunks and return a list of
93 """ Prompts the user to filter the originalhunks and return a list of
95 selected hunks.
94 selected hunks.
96 *operation* is used for to build ui messages to indicate the user what
95 *operation* is used for to build ui messages to indicate the user what
97 kind of filtering they are doing: reverting, committing, shelving, etc.
96 kind of filtering they are doing: reverting, committing, shelving, etc.
98 (see patch.filterpatch).
97 (see patch.filterpatch).
99 """
98 """
100 usecurses = crecordmod.checkcurses(ui)
99 usecurses = crecordmod.checkcurses(ui)
101 testfile = ui.config('experimental', 'crecordtest', None)
100 testfile = ui.config('experimental', 'crecordtest', None)
102 oldwrite = setupwrapcolorwrite(ui)
101 oldwrite = setupwrapcolorwrite(ui)
103 try:
102 try:
104 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
103 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
105 testfile, operation)
104 testfile, operation)
106 finally:
105 finally:
107 ui.write = oldwrite
106 ui.write = oldwrite
108 return newchunks, newopts
107 return newchunks, newopts
109
108
110 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
109 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
111 filterfn, *pats, **opts):
110 filterfn, *pats, **opts):
112 from . import merge as mergemod
111 from . import merge as mergemod
113 if not ui.interactive():
112 if not ui.interactive():
114 if cmdsuggest:
113 if cmdsuggest:
115 msg = _('running non-interactively, use %s instead') % cmdsuggest
114 msg = _('running non-interactively, use %s instead') % cmdsuggest
116 else:
115 else:
117 msg = _('running non-interactively')
116 msg = _('running non-interactively')
118 raise error.Abort(msg)
117 raise error.Abort(msg)
119
118
120 # make sure username is set before going interactive
119 # make sure username is set before going interactive
121 if not opts.get('user'):
120 if not opts.get('user'):
122 ui.username() # raise exception, username not provided
121 ui.username() # raise exception, username not provided
123
122
124 def recordfunc(ui, repo, message, match, opts):
123 def recordfunc(ui, repo, message, match, opts):
125 """This is generic record driver.
124 """This is generic record driver.
126
125
127 Its job is to interactively filter local changes, and
126 Its job is to interactively filter local changes, and
128 accordingly prepare working directory into a state in which the
127 accordingly prepare working directory into a state in which the
129 job can be delegated to a non-interactive commit command such as
128 job can be delegated to a non-interactive commit command such as
130 'commit' or 'qrefresh'.
129 'commit' or 'qrefresh'.
131
130
132 After the actual job is done by non-interactive command, the
131 After the actual job is done by non-interactive command, the
133 working directory is restored to its original state.
132 working directory is restored to its original state.
134
133
135 In the end we'll record interesting changes, and everything else
134 In the end we'll record interesting changes, and everything else
136 will be left in place, so the user can continue working.
135 will be left in place, so the user can continue working.
137 """
136 """
138
137
139 checkunfinished(repo, commit=True)
138 checkunfinished(repo, commit=True)
140 wctx = repo[None]
139 wctx = repo[None]
141 merge = len(wctx.parents()) > 1
140 merge = len(wctx.parents()) > 1
142 if merge:
141 if merge:
143 raise error.Abort(_('cannot partially commit a merge '
142 raise error.Abort(_('cannot partially commit a merge '
144 '(use "hg commit" instead)'))
143 '(use "hg commit" instead)'))
145
144
146 def fail(f, msg):
145 def fail(f, msg):
147 raise error.Abort('%s: %s' % (f, msg))
146 raise error.Abort('%s: %s' % (f, msg))
148
147
149 force = opts.get('force')
148 force = opts.get('force')
150 if not force:
149 if not force:
151 vdirs = []
150 vdirs = []
152 match.explicitdir = vdirs.append
151 match.explicitdir = vdirs.append
153 match.bad = fail
152 match.bad = fail
154
153
155 status = repo.status(match=match)
154 status = repo.status(match=match)
156 if not force:
155 if not force:
157 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
156 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
158 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
157 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
159 diffopts.nodates = True
158 diffopts.nodates = True
160 diffopts.git = True
159 diffopts.git = True
161 diffopts.showfunc = True
160 diffopts.showfunc = True
162 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
161 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
163 originalchunks = patch.parsepatch(originaldiff)
162 originalchunks = patch.parsepatch(originaldiff)
164
163
165 # 1. filter patch, since we are intending to apply subset of it
164 # 1. filter patch, since we are intending to apply subset of it
166 try:
165 try:
167 chunks, newopts = filterfn(ui, originalchunks)
166 chunks, newopts = filterfn(ui, originalchunks)
168 except patch.PatchError as err:
167 except patch.PatchError as err:
169 raise error.Abort(_('error parsing patch: %s') % err)
168 raise error.Abort(_('error parsing patch: %s') % err)
170 opts.update(newopts)
169 opts.update(newopts)
171
170
172 # We need to keep a backup of files that have been newly added and
171 # We need to keep a backup of files that have been newly added and
173 # modified during the recording process because there is a previous
172 # modified during the recording process because there is a previous
174 # version without the edit in the workdir
173 # version without the edit in the workdir
175 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
174 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
176 contenders = set()
175 contenders = set()
177 for h in chunks:
176 for h in chunks:
178 try:
177 try:
179 contenders.update(set(h.files()))
178 contenders.update(set(h.files()))
180 except AttributeError:
179 except AttributeError:
181 pass
180 pass
182
181
183 changed = status.modified + status.added + status.removed
182 changed = status.modified + status.added + status.removed
184 newfiles = [f for f in changed if f in contenders]
183 newfiles = [f for f in changed if f in contenders]
185 if not newfiles:
184 if not newfiles:
186 ui.status(_('no changes to record\n'))
185 ui.status(_('no changes to record\n'))
187 return 0
186 return 0
188
187
189 modified = set(status.modified)
188 modified = set(status.modified)
190
189
191 # 2. backup changed files, so we can restore them in the end
190 # 2. backup changed files, so we can restore them in the end
192
191
193 if backupall:
192 if backupall:
194 tobackup = changed
193 tobackup = changed
195 else:
194 else:
196 tobackup = [f for f in newfiles if f in modified or f in \
195 tobackup = [f for f in newfiles if f in modified or f in \
197 newlyaddedandmodifiedfiles]
196 newlyaddedandmodifiedfiles]
198 backups = {}
197 backups = {}
199 if tobackup:
198 if tobackup:
200 backupdir = repo.join('record-backups')
199 backupdir = repo.join('record-backups')
201 try:
200 try:
202 os.mkdir(backupdir)
201 os.mkdir(backupdir)
203 except OSError as err:
202 except OSError as err:
204 if err.errno != errno.EEXIST:
203 if err.errno != errno.EEXIST:
205 raise
204 raise
206 try:
205 try:
207 # backup continues
206 # backup continues
208 for f in tobackup:
207 for f in tobackup:
209 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
208 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
210 dir=backupdir)
209 dir=backupdir)
211 os.close(fd)
210 os.close(fd)
212 ui.debug('backup %r as %r\n' % (f, tmpname))
211 ui.debug('backup %r as %r\n' % (f, tmpname))
213 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
212 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
214 backups[f] = tmpname
213 backups[f] = tmpname
215
214
216 fp = stringio()
215 fp = stringio()
217 for c in chunks:
216 for c in chunks:
218 fname = c.filename()
217 fname = c.filename()
219 if fname in backups:
218 if fname in backups:
220 c.write(fp)
219 c.write(fp)
221 dopatch = fp.tell()
220 dopatch = fp.tell()
222 fp.seek(0)
221 fp.seek(0)
223
222
224 # 2.5 optionally review / modify patch in text editor
223 # 2.5 optionally review / modify patch in text editor
225 if opts.get('review', False):
224 if opts.get('review', False):
226 patchtext = (crecordmod.diffhelptext
225 patchtext = (crecordmod.diffhelptext
227 + crecordmod.patchhelptext
226 + crecordmod.patchhelptext
228 + fp.read())
227 + fp.read())
229 reviewedpatch = ui.edit(patchtext, "",
228 reviewedpatch = ui.edit(patchtext, "",
230 extra={"suffix": ".diff"})
229 extra={"suffix": ".diff"})
231 fp.truncate(0)
230 fp.truncate(0)
232 fp.write(reviewedpatch)
231 fp.write(reviewedpatch)
233 fp.seek(0)
232 fp.seek(0)
234
233
235 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
234 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
236 # 3a. apply filtered patch to clean repo (clean)
235 # 3a. apply filtered patch to clean repo (clean)
237 if backups:
236 if backups:
238 # Equivalent to hg.revert
237 # Equivalent to hg.revert
239 m = scmutil.matchfiles(repo, backups.keys())
238 m = scmutil.matchfiles(repo, backups.keys())
240 mergemod.update(repo, repo.dirstate.p1(),
239 mergemod.update(repo, repo.dirstate.p1(),
241 False, True, matcher=m)
240 False, True, matcher=m)
242
241
243 # 3b. (apply)
242 # 3b. (apply)
244 if dopatch:
243 if dopatch:
245 try:
244 try:
246 ui.debug('applying patch\n')
245 ui.debug('applying patch\n')
247 ui.debug(fp.getvalue())
246 ui.debug(fp.getvalue())
248 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
247 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
249 except patch.PatchError as err:
248 except patch.PatchError as err:
250 raise error.Abort(str(err))
249 raise error.Abort(str(err))
251 del fp
250 del fp
252
251
253 # 4. We prepared working directory according to filtered
252 # 4. We prepared working directory according to filtered
254 # patch. Now is the time to delegate the job to
253 # patch. Now is the time to delegate the job to
255 # commit/qrefresh or the like!
254 # commit/qrefresh or the like!
256
255
257 # Make all of the pathnames absolute.
256 # Make all of the pathnames absolute.
258 newfiles = [repo.wjoin(nf) for nf in newfiles]
257 newfiles = [repo.wjoin(nf) for nf in newfiles]
259 return commitfunc(ui, repo, *newfiles, **opts)
258 return commitfunc(ui, repo, *newfiles, **opts)
260 finally:
259 finally:
261 # 5. finally restore backed-up files
260 # 5. finally restore backed-up files
262 try:
261 try:
263 dirstate = repo.dirstate
262 dirstate = repo.dirstate
264 for realname, tmpname in backups.iteritems():
263 for realname, tmpname in backups.iteritems():
265 ui.debug('restoring %r to %r\n' % (tmpname, realname))
264 ui.debug('restoring %r to %r\n' % (tmpname, realname))
266
265
267 if dirstate[realname] == 'n':
266 if dirstate[realname] == 'n':
268 # without normallookup, restoring timestamp
267 # without normallookup, restoring timestamp
269 # may cause partially committed files
268 # may cause partially committed files
270 # to be treated as unmodified
269 # to be treated as unmodified
271 dirstate.normallookup(realname)
270 dirstate.normallookup(realname)
272
271
273 # copystat=True here and above are a hack to trick any
272 # copystat=True here and above are a hack to trick any
274 # editors that have f open that we haven't modified them.
273 # editors that have f open that we haven't modified them.
275 #
274 #
276 # Also note that this racy as an editor could notice the
275 # Also note that this racy as an editor could notice the
277 # file's mtime before we've finished writing it.
276 # file's mtime before we've finished writing it.
278 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
277 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
279 os.unlink(tmpname)
278 os.unlink(tmpname)
280 if tobackup:
279 if tobackup:
281 os.rmdir(backupdir)
280 os.rmdir(backupdir)
282 except OSError:
281 except OSError:
283 pass
282 pass
284
283
285 def recordinwlock(ui, repo, message, match, opts):
284 def recordinwlock(ui, repo, message, match, opts):
286 with repo.wlock():
285 with repo.wlock():
287 return recordfunc(ui, repo, message, match, opts)
286 return recordfunc(ui, repo, message, match, opts)
288
287
289 return commit(ui, repo, recordinwlock, pats, opts)
288 return commit(ui, repo, recordinwlock, pats, opts)
290
289
291 def findpossible(cmd, table, strict=False):
290 def findpossible(cmd, table, strict=False):
292 """
291 """
293 Return cmd -> (aliases, command table entry)
292 Return cmd -> (aliases, command table entry)
294 for each matching command.
293 for each matching command.
295 Return debug commands (or their aliases) only if no normal command matches.
294 Return debug commands (or their aliases) only if no normal command matches.
296 """
295 """
297 choice = {}
296 choice = {}
298 debugchoice = {}
297 debugchoice = {}
299
298
300 if cmd in table:
299 if cmd in table:
301 # short-circuit exact matches, "log" alias beats "^log|history"
300 # short-circuit exact matches, "log" alias beats "^log|history"
302 keys = [cmd]
301 keys = [cmd]
303 else:
302 else:
304 keys = table.keys()
303 keys = table.keys()
305
304
306 allcmds = []
305 allcmds = []
307 for e in keys:
306 for e in keys:
308 aliases = parsealiases(e)
307 aliases = parsealiases(e)
309 allcmds.extend(aliases)
308 allcmds.extend(aliases)
310 found = None
309 found = None
311 if cmd in aliases:
310 if cmd in aliases:
312 found = cmd
311 found = cmd
313 elif not strict:
312 elif not strict:
314 for a in aliases:
313 for a in aliases:
315 if a.startswith(cmd):
314 if a.startswith(cmd):
316 found = a
315 found = a
317 break
316 break
318 if found is not None:
317 if found is not None:
319 if aliases[0].startswith("debug") or found.startswith("debug"):
318 if aliases[0].startswith("debug") or found.startswith("debug"):
320 debugchoice[found] = (aliases, table[e])
319 debugchoice[found] = (aliases, table[e])
321 else:
320 else:
322 choice[found] = (aliases, table[e])
321 choice[found] = (aliases, table[e])
323
322
324 if not choice and debugchoice:
323 if not choice and debugchoice:
325 choice = debugchoice
324 choice = debugchoice
326
325
327 return choice, allcmds
326 return choice, allcmds
328
327
329 def findcmd(cmd, table, strict=True):
328 def findcmd(cmd, table, strict=True):
330 """Return (aliases, command table entry) for command string."""
329 """Return (aliases, command table entry) for command string."""
331 choice, allcmds = findpossible(cmd, table, strict)
330 choice, allcmds = findpossible(cmd, table, strict)
332
331
333 if cmd in choice:
332 if cmd in choice:
334 return choice[cmd]
333 return choice[cmd]
335
334
336 if len(choice) > 1:
335 if len(choice) > 1:
337 clist = choice.keys()
336 clist = choice.keys()
338 clist.sort()
337 clist.sort()
339 raise error.AmbiguousCommand(cmd, clist)
338 raise error.AmbiguousCommand(cmd, clist)
340
339
341 if choice:
340 if choice:
342 return choice.values()[0]
341 return choice.values()[0]
343
342
344 raise error.UnknownCommand(cmd, allcmds)
343 raise error.UnknownCommand(cmd, allcmds)
345
344
346 def findrepo(p):
345 def findrepo(p):
347 while not os.path.isdir(os.path.join(p, ".hg")):
346 while not os.path.isdir(os.path.join(p, ".hg")):
348 oldp, p = p, os.path.dirname(p)
347 oldp, p = p, os.path.dirname(p)
349 if p == oldp:
348 if p == oldp:
350 return None
349 return None
351
350
352 return p
351 return p
353
352
354 def bailifchanged(repo, merge=True):
353 def bailifchanged(repo, merge=True):
355 if merge and repo.dirstate.p2() != nullid:
354 if merge and repo.dirstate.p2() != nullid:
356 raise error.Abort(_('outstanding uncommitted merge'))
355 raise error.Abort(_('outstanding uncommitted merge'))
357 modified, added, removed, deleted = repo.status()[:4]
356 modified, added, removed, deleted = repo.status()[:4]
358 if modified or added or removed or deleted:
357 if modified or added or removed or deleted:
359 raise error.Abort(_('uncommitted changes'))
358 raise error.Abort(_('uncommitted changes'))
360 ctx = repo[None]
359 ctx = repo[None]
361 for s in sorted(ctx.substate):
360 for s in sorted(ctx.substate):
362 ctx.sub(s).bailifchanged()
361 ctx.sub(s).bailifchanged()
363
362
364 def logmessage(ui, opts):
363 def logmessage(ui, opts):
365 """ get the log message according to -m and -l option """
364 """ get the log message according to -m and -l option """
366 message = opts.get('message')
365 message = opts.get('message')
367 logfile = opts.get('logfile')
366 logfile = opts.get('logfile')
368
367
369 if message and logfile:
368 if message and logfile:
370 raise error.Abort(_('options --message and --logfile are mutually '
369 raise error.Abort(_('options --message and --logfile are mutually '
371 'exclusive'))
370 'exclusive'))
372 if not message and logfile:
371 if not message and logfile:
373 try:
372 try:
374 if logfile == '-':
373 if logfile == '-':
375 message = ui.fin.read()
374 message = ui.fin.read()
376 else:
375 else:
377 message = '\n'.join(util.readfile(logfile).splitlines())
376 message = '\n'.join(util.readfile(logfile).splitlines())
378 except IOError as inst:
377 except IOError as inst:
379 raise error.Abort(_("can't read commit message '%s': %s") %
378 raise error.Abort(_("can't read commit message '%s': %s") %
380 (logfile, inst.strerror))
379 (logfile, inst.strerror))
381 return message
380 return message
382
381
383 def mergeeditform(ctxorbool, baseformname):
382 def mergeeditform(ctxorbool, baseformname):
384 """return appropriate editform name (referencing a committemplate)
383 """return appropriate editform name (referencing a committemplate)
385
384
386 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
385 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
387 merging is committed.
386 merging is committed.
388
387
389 This returns baseformname with '.merge' appended if it is a merge,
388 This returns baseformname with '.merge' appended if it is a merge,
390 otherwise '.normal' is appended.
389 otherwise '.normal' is appended.
391 """
390 """
392 if isinstance(ctxorbool, bool):
391 if isinstance(ctxorbool, bool):
393 if ctxorbool:
392 if ctxorbool:
394 return baseformname + ".merge"
393 return baseformname + ".merge"
395 elif 1 < len(ctxorbool.parents()):
394 elif 1 < len(ctxorbool.parents()):
396 return baseformname + ".merge"
395 return baseformname + ".merge"
397
396
398 return baseformname + ".normal"
397 return baseformname + ".normal"
399
398
400 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
399 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
401 editform='', **opts):
400 editform='', **opts):
402 """get appropriate commit message editor according to '--edit' option
401 """get appropriate commit message editor according to '--edit' option
403
402
404 'finishdesc' is a function to be called with edited commit message
403 'finishdesc' is a function to be called with edited commit message
405 (= 'description' of the new changeset) just after editing, but
404 (= 'description' of the new changeset) just after editing, but
406 before checking empty-ness. It should return actual text to be
405 before checking empty-ness. It should return actual text to be
407 stored into history. This allows to change description before
406 stored into history. This allows to change description before
408 storing.
407 storing.
409
408
410 'extramsg' is a extra message to be shown in the editor instead of
409 'extramsg' is a extra message to be shown in the editor instead of
411 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
410 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
412 is automatically added.
411 is automatically added.
413
412
414 'editform' is a dot-separated list of names, to distinguish
413 'editform' is a dot-separated list of names, to distinguish
415 the purpose of commit text editing.
414 the purpose of commit text editing.
416
415
417 'getcommiteditor' returns 'commitforceeditor' regardless of
416 'getcommiteditor' returns 'commitforceeditor' regardless of
418 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
417 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
419 they are specific for usage in MQ.
418 they are specific for usage in MQ.
420 """
419 """
421 if edit or finishdesc or extramsg:
420 if edit or finishdesc or extramsg:
422 return lambda r, c, s: commitforceeditor(r, c, s,
421 return lambda r, c, s: commitforceeditor(r, c, s,
423 finishdesc=finishdesc,
422 finishdesc=finishdesc,
424 extramsg=extramsg,
423 extramsg=extramsg,
425 editform=editform)
424 editform=editform)
426 elif editform:
425 elif editform:
427 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
426 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
428 else:
427 else:
429 return commiteditor
428 return commiteditor
430
429
431 def loglimit(opts):
430 def loglimit(opts):
432 """get the log limit according to option -l/--limit"""
431 """get the log limit according to option -l/--limit"""
433 limit = opts.get('limit')
432 limit = opts.get('limit')
434 if limit:
433 if limit:
435 try:
434 try:
436 limit = int(limit)
435 limit = int(limit)
437 except ValueError:
436 except ValueError:
438 raise error.Abort(_('limit must be a positive integer'))
437 raise error.Abort(_('limit must be a positive integer'))
439 if limit <= 0:
438 if limit <= 0:
440 raise error.Abort(_('limit must be positive'))
439 raise error.Abort(_('limit must be positive'))
441 else:
440 else:
442 limit = None
441 limit = None
443 return limit
442 return limit
444
443
445 def makefilename(repo, pat, node, desc=None,
444 def makefilename(repo, pat, node, desc=None,
446 total=None, seqno=None, revwidth=None, pathname=None):
445 total=None, seqno=None, revwidth=None, pathname=None):
447 node_expander = {
446 node_expander = {
448 'H': lambda: hex(node),
447 'H': lambda: hex(node),
449 'R': lambda: str(repo.changelog.rev(node)),
448 'R': lambda: str(repo.changelog.rev(node)),
450 'h': lambda: short(node),
449 'h': lambda: short(node),
451 'm': lambda: re.sub('[^\w]', '_', str(desc))
450 'm': lambda: re.sub('[^\w]', '_', str(desc))
452 }
451 }
453 expander = {
452 expander = {
454 '%': lambda: '%',
453 '%': lambda: '%',
455 'b': lambda: os.path.basename(repo.root),
454 'b': lambda: os.path.basename(repo.root),
456 }
455 }
457
456
458 try:
457 try:
459 if node:
458 if node:
460 expander.update(node_expander)
459 expander.update(node_expander)
461 if node:
460 if node:
462 expander['r'] = (lambda:
461 expander['r'] = (lambda:
463 str(repo.changelog.rev(node)).zfill(revwidth or 0))
462 str(repo.changelog.rev(node)).zfill(revwidth or 0))
464 if total is not None:
463 if total is not None:
465 expander['N'] = lambda: str(total)
464 expander['N'] = lambda: str(total)
466 if seqno is not None:
465 if seqno is not None:
467 expander['n'] = lambda: str(seqno)
466 expander['n'] = lambda: str(seqno)
468 if total is not None and seqno is not None:
467 if total is not None and seqno is not None:
469 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
468 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
470 if pathname is not None:
469 if pathname is not None:
471 expander['s'] = lambda: os.path.basename(pathname)
470 expander['s'] = lambda: os.path.basename(pathname)
472 expander['d'] = lambda: os.path.dirname(pathname) or '.'
471 expander['d'] = lambda: os.path.dirname(pathname) or '.'
473 expander['p'] = lambda: pathname
472 expander['p'] = lambda: pathname
474
473
475 newname = []
474 newname = []
476 patlen = len(pat)
475 patlen = len(pat)
477 i = 0
476 i = 0
478 while i < patlen:
477 while i < patlen:
479 c = pat[i]
478 c = pat[i]
480 if c == '%':
479 if c == '%':
481 i += 1
480 i += 1
482 c = pat[i]
481 c = pat[i]
483 c = expander[c]()
482 c = expander[c]()
484 newname.append(c)
483 newname.append(c)
485 i += 1
484 i += 1
486 return ''.join(newname)
485 return ''.join(newname)
487 except KeyError as inst:
486 except KeyError as inst:
488 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
487 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
489 inst.args[0])
488 inst.args[0])
490
489
491 class _unclosablefile(object):
490 class _unclosablefile(object):
492 def __init__(self, fp):
491 def __init__(self, fp):
493 self._fp = fp
492 self._fp = fp
494
493
495 def close(self):
494 def close(self):
496 pass
495 pass
497
496
498 def __iter__(self):
497 def __iter__(self):
499 return iter(self._fp)
498 return iter(self._fp)
500
499
501 def __getattr__(self, attr):
500 def __getattr__(self, attr):
502 return getattr(self._fp, attr)
501 return getattr(self._fp, attr)
503
502
504 def __enter__(self):
503 def __enter__(self):
505 return self
504 return self
506
505
507 def __exit__(self, exc_type, exc_value, exc_tb):
506 def __exit__(self, exc_type, exc_value, exc_tb):
508 pass
507 pass
509
508
510 def makefileobj(repo, pat, node=None, desc=None, total=None,
509 def makefileobj(repo, pat, node=None, desc=None, total=None,
511 seqno=None, revwidth=None, mode='wb', modemap=None,
510 seqno=None, revwidth=None, mode='wb', modemap=None,
512 pathname=None):
511 pathname=None):
513
512
514 writable = mode not in ('r', 'rb')
513 writable = mode not in ('r', 'rb')
515
514
516 if not pat or pat == '-':
515 if not pat or pat == '-':
517 if writable:
516 if writable:
518 fp = repo.ui.fout
517 fp = repo.ui.fout
519 else:
518 else:
520 fp = repo.ui.fin
519 fp = repo.ui.fin
521 return _unclosablefile(fp)
520 return _unclosablefile(fp)
522 if util.safehasattr(pat, 'write') and writable:
521 if util.safehasattr(pat, 'write') and writable:
523 return pat
522 return pat
524 if util.safehasattr(pat, 'read') and 'r' in mode:
523 if util.safehasattr(pat, 'read') and 'r' in mode:
525 return pat
524 return pat
526 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
525 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
527 if modemap is not None:
526 if modemap is not None:
528 mode = modemap.get(fn, mode)
527 mode = modemap.get(fn, mode)
529 if mode == 'wb':
528 if mode == 'wb':
530 modemap[fn] = 'ab'
529 modemap[fn] = 'ab'
531 return open(fn, mode)
530 return open(fn, mode)
532
531
533 def openrevlog(repo, cmd, file_, opts):
532 def openrevlog(repo, cmd, file_, opts):
534 """opens the changelog, manifest, a filelog or a given revlog"""
533 """opens the changelog, manifest, a filelog or a given revlog"""
535 cl = opts['changelog']
534 cl = opts['changelog']
536 mf = opts['manifest']
535 mf = opts['manifest']
537 dir = opts['dir']
536 dir = opts['dir']
538 msg = None
537 msg = None
539 if cl and mf:
538 if cl and mf:
540 msg = _('cannot specify --changelog and --manifest at the same time')
539 msg = _('cannot specify --changelog and --manifest at the same time')
541 elif cl and dir:
540 elif cl and dir:
542 msg = _('cannot specify --changelog and --dir at the same time')
541 msg = _('cannot specify --changelog and --dir at the same time')
543 elif cl or mf or dir:
542 elif cl or mf or dir:
544 if file_:
543 if file_:
545 msg = _('cannot specify filename with --changelog or --manifest')
544 msg = _('cannot specify filename with --changelog or --manifest')
546 elif not repo:
545 elif not repo:
547 msg = _('cannot specify --changelog or --manifest or --dir '
546 msg = _('cannot specify --changelog or --manifest or --dir '
548 'without a repository')
547 'without a repository')
549 if msg:
548 if msg:
550 raise error.Abort(msg)
549 raise error.Abort(msg)
551
550
552 r = None
551 r = None
553 if repo:
552 if repo:
554 if cl:
553 if cl:
555 r = repo.unfiltered().changelog
554 r = repo.unfiltered().changelog
556 elif dir:
555 elif dir:
557 if 'treemanifest' not in repo.requirements:
556 if 'treemanifest' not in repo.requirements:
558 raise error.Abort(_("--dir can only be used on repos with "
557 raise error.Abort(_("--dir can only be used on repos with "
559 "treemanifest enabled"))
558 "treemanifest enabled"))
560 dirlog = repo.manifestlog._revlog.dirlog(dir)
559 dirlog = repo.manifestlog._revlog.dirlog(dir)
561 if len(dirlog):
560 if len(dirlog):
562 r = dirlog
561 r = dirlog
563 elif mf:
562 elif mf:
564 r = repo.manifestlog._revlog
563 r = repo.manifestlog._revlog
565 elif file_:
564 elif file_:
566 filelog = repo.file(file_)
565 filelog = repo.file(file_)
567 if len(filelog):
566 if len(filelog):
568 r = filelog
567 r = filelog
569 if not r:
568 if not r:
570 if not file_:
569 if not file_:
571 raise error.CommandError(cmd, _('invalid arguments'))
570 raise error.CommandError(cmd, _('invalid arguments'))
572 if not os.path.isfile(file_):
571 if not os.path.isfile(file_):
573 raise error.Abort(_("revlog '%s' not found") % file_)
572 raise error.Abort(_("revlog '%s' not found") % file_)
574 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
573 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
575 file_[:-2] + ".i")
574 file_[:-2] + ".i")
576 return r
575 return r
577
576
578 def copy(ui, repo, pats, opts, rename=False):
577 def copy(ui, repo, pats, opts, rename=False):
579 # called with the repo lock held
578 # called with the repo lock held
580 #
579 #
581 # hgsep => pathname that uses "/" to separate directories
580 # hgsep => pathname that uses "/" to separate directories
582 # ossep => pathname that uses os.sep to separate directories
581 # ossep => pathname that uses os.sep to separate directories
583 cwd = repo.getcwd()
582 cwd = repo.getcwd()
584 targets = {}
583 targets = {}
585 after = opts.get("after")
584 after = opts.get("after")
586 dryrun = opts.get("dry_run")
585 dryrun = opts.get("dry_run")
587 wctx = repo[None]
586 wctx = repo[None]
588
587
589 def walkpat(pat):
588 def walkpat(pat):
590 srcs = []
589 srcs = []
591 if after:
590 if after:
592 badstates = '?'
591 badstates = '?'
593 else:
592 else:
594 badstates = '?r'
593 badstates = '?r'
595 m = scmutil.match(repo[None], [pat], opts, globbed=True)
594 m = scmutil.match(repo[None], [pat], opts, globbed=True)
596 for abs in repo.walk(m):
595 for abs in repo.walk(m):
597 state = repo.dirstate[abs]
596 state = repo.dirstate[abs]
598 rel = m.rel(abs)
597 rel = m.rel(abs)
599 exact = m.exact(abs)
598 exact = m.exact(abs)
600 if state in badstates:
599 if state in badstates:
601 if exact and state == '?':
600 if exact and state == '?':
602 ui.warn(_('%s: not copying - file is not managed\n') % rel)
601 ui.warn(_('%s: not copying - file is not managed\n') % rel)
603 if exact and state == 'r':
602 if exact and state == 'r':
604 ui.warn(_('%s: not copying - file has been marked for'
603 ui.warn(_('%s: not copying - file has been marked for'
605 ' remove\n') % rel)
604 ' remove\n') % rel)
606 continue
605 continue
607 # abs: hgsep
606 # abs: hgsep
608 # rel: ossep
607 # rel: ossep
609 srcs.append((abs, rel, exact))
608 srcs.append((abs, rel, exact))
610 return srcs
609 return srcs
611
610
612 # abssrc: hgsep
611 # abssrc: hgsep
613 # relsrc: ossep
612 # relsrc: ossep
614 # otarget: ossep
613 # otarget: ossep
615 def copyfile(abssrc, relsrc, otarget, exact):
614 def copyfile(abssrc, relsrc, otarget, exact):
616 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
615 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
617 if '/' in abstarget:
616 if '/' in abstarget:
618 # We cannot normalize abstarget itself, this would prevent
617 # We cannot normalize abstarget itself, this would prevent
619 # case only renames, like a => A.
618 # case only renames, like a => A.
620 abspath, absname = abstarget.rsplit('/', 1)
619 abspath, absname = abstarget.rsplit('/', 1)
621 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
620 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
622 reltarget = repo.pathto(abstarget, cwd)
621 reltarget = repo.pathto(abstarget, cwd)
623 target = repo.wjoin(abstarget)
622 target = repo.wjoin(abstarget)
624 src = repo.wjoin(abssrc)
623 src = repo.wjoin(abssrc)
625 state = repo.dirstate[abstarget]
624 state = repo.dirstate[abstarget]
626
625
627 scmutil.checkportable(ui, abstarget)
626 scmutil.checkportable(ui, abstarget)
628
627
629 # check for collisions
628 # check for collisions
630 prevsrc = targets.get(abstarget)
629 prevsrc = targets.get(abstarget)
631 if prevsrc is not None:
630 if prevsrc is not None:
632 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
631 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
633 (reltarget, repo.pathto(abssrc, cwd),
632 (reltarget, repo.pathto(abssrc, cwd),
634 repo.pathto(prevsrc, cwd)))
633 repo.pathto(prevsrc, cwd)))
635 return
634 return
636
635
637 # check for overwrites
636 # check for overwrites
638 exists = os.path.lexists(target)
637 exists = os.path.lexists(target)
639 samefile = False
638 samefile = False
640 if exists and abssrc != abstarget:
639 if exists and abssrc != abstarget:
641 if (repo.dirstate.normalize(abssrc) ==
640 if (repo.dirstate.normalize(abssrc) ==
642 repo.dirstate.normalize(abstarget)):
641 repo.dirstate.normalize(abstarget)):
643 if not rename:
642 if not rename:
644 ui.warn(_("%s: can't copy - same file\n") % reltarget)
643 ui.warn(_("%s: can't copy - same file\n") % reltarget)
645 return
644 return
646 exists = False
645 exists = False
647 samefile = True
646 samefile = True
648
647
649 if not after and exists or after and state in 'mn':
648 if not after and exists or after and state in 'mn':
650 if not opts['force']:
649 if not opts['force']:
651 if state in 'mn':
650 if state in 'mn':
652 msg = _('%s: not overwriting - file already committed\n')
651 msg = _('%s: not overwriting - file already committed\n')
653 if after:
652 if after:
654 flags = '--after --force'
653 flags = '--after --force'
655 else:
654 else:
656 flags = '--force'
655 flags = '--force'
657 if rename:
656 if rename:
658 hint = _('(hg rename %s to replace the file by '
657 hint = _('(hg rename %s to replace the file by '
659 'recording a rename)\n') % flags
658 'recording a rename)\n') % flags
660 else:
659 else:
661 hint = _('(hg copy %s to replace the file by '
660 hint = _('(hg copy %s to replace the file by '
662 'recording a copy)\n') % flags
661 'recording a copy)\n') % flags
663 else:
662 else:
664 msg = _('%s: not overwriting - file exists\n')
663 msg = _('%s: not overwriting - file exists\n')
665 if rename:
664 if rename:
666 hint = _('(hg rename --after to record the rename)\n')
665 hint = _('(hg rename --after to record the rename)\n')
667 else:
666 else:
668 hint = _('(hg copy --after to record the copy)\n')
667 hint = _('(hg copy --after to record the copy)\n')
669 ui.warn(msg % reltarget)
668 ui.warn(msg % reltarget)
670 ui.warn(hint)
669 ui.warn(hint)
671 return
670 return
672
671
673 if after:
672 if after:
674 if not exists:
673 if not exists:
675 if rename:
674 if rename:
676 ui.warn(_('%s: not recording move - %s does not exist\n') %
675 ui.warn(_('%s: not recording move - %s does not exist\n') %
677 (relsrc, reltarget))
676 (relsrc, reltarget))
678 else:
677 else:
679 ui.warn(_('%s: not recording copy - %s does not exist\n') %
678 ui.warn(_('%s: not recording copy - %s does not exist\n') %
680 (relsrc, reltarget))
679 (relsrc, reltarget))
681 return
680 return
682 elif not dryrun:
681 elif not dryrun:
683 try:
682 try:
684 if exists:
683 if exists:
685 os.unlink(target)
684 os.unlink(target)
686 targetdir = os.path.dirname(target) or '.'
685 targetdir = os.path.dirname(target) or '.'
687 if not os.path.isdir(targetdir):
686 if not os.path.isdir(targetdir):
688 os.makedirs(targetdir)
687 os.makedirs(targetdir)
689 if samefile:
688 if samefile:
690 tmp = target + "~hgrename"
689 tmp = target + "~hgrename"
691 os.rename(src, tmp)
690 os.rename(src, tmp)
692 os.rename(tmp, target)
691 os.rename(tmp, target)
693 else:
692 else:
694 util.copyfile(src, target)
693 util.copyfile(src, target)
695 srcexists = True
694 srcexists = True
696 except IOError as inst:
695 except IOError as inst:
697 if inst.errno == errno.ENOENT:
696 if inst.errno == errno.ENOENT:
698 ui.warn(_('%s: deleted in working directory\n') % relsrc)
697 ui.warn(_('%s: deleted in working directory\n') % relsrc)
699 srcexists = False
698 srcexists = False
700 else:
699 else:
701 ui.warn(_('%s: cannot copy - %s\n') %
700 ui.warn(_('%s: cannot copy - %s\n') %
702 (relsrc, inst.strerror))
701 (relsrc, inst.strerror))
703 return True # report a failure
702 return True # report a failure
704
703
705 if ui.verbose or not exact:
704 if ui.verbose or not exact:
706 if rename:
705 if rename:
707 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
706 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
708 else:
707 else:
709 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
708 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
710
709
711 targets[abstarget] = abssrc
710 targets[abstarget] = abssrc
712
711
713 # fix up dirstate
712 # fix up dirstate
714 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
713 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
715 dryrun=dryrun, cwd=cwd)
714 dryrun=dryrun, cwd=cwd)
716 if rename and not dryrun:
715 if rename and not dryrun:
717 if not after and srcexists and not samefile:
716 if not after and srcexists and not samefile:
718 util.unlinkpath(repo.wjoin(abssrc))
717 util.unlinkpath(repo.wjoin(abssrc))
719 wctx.forget([abssrc])
718 wctx.forget([abssrc])
720
719
721 # pat: ossep
720 # pat: ossep
722 # dest ossep
721 # dest ossep
723 # srcs: list of (hgsep, hgsep, ossep, bool)
722 # srcs: list of (hgsep, hgsep, ossep, bool)
724 # return: function that takes hgsep and returns ossep
723 # return: function that takes hgsep and returns ossep
725 def targetpathfn(pat, dest, srcs):
724 def targetpathfn(pat, dest, srcs):
726 if os.path.isdir(pat):
725 if os.path.isdir(pat):
727 abspfx = pathutil.canonpath(repo.root, cwd, pat)
726 abspfx = pathutil.canonpath(repo.root, cwd, pat)
728 abspfx = util.localpath(abspfx)
727 abspfx = util.localpath(abspfx)
729 if destdirexists:
728 if destdirexists:
730 striplen = len(os.path.split(abspfx)[0])
729 striplen = len(os.path.split(abspfx)[0])
731 else:
730 else:
732 striplen = len(abspfx)
731 striplen = len(abspfx)
733 if striplen:
732 if striplen:
734 striplen += len(os.sep)
733 striplen += len(os.sep)
735 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
734 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
736 elif destdirexists:
735 elif destdirexists:
737 res = lambda p: os.path.join(dest,
736 res = lambda p: os.path.join(dest,
738 os.path.basename(util.localpath(p)))
737 os.path.basename(util.localpath(p)))
739 else:
738 else:
740 res = lambda p: dest
739 res = lambda p: dest
741 return res
740 return res
742
741
743 # pat: ossep
742 # pat: ossep
744 # dest ossep
743 # dest ossep
745 # srcs: list of (hgsep, hgsep, ossep, bool)
744 # srcs: list of (hgsep, hgsep, ossep, bool)
746 # return: function that takes hgsep and returns ossep
745 # return: function that takes hgsep and returns ossep
747 def targetpathafterfn(pat, dest, srcs):
746 def targetpathafterfn(pat, dest, srcs):
748 if matchmod.patkind(pat):
747 if matchmod.patkind(pat):
749 # a mercurial pattern
748 # a mercurial pattern
750 res = lambda p: os.path.join(dest,
749 res = lambda p: os.path.join(dest,
751 os.path.basename(util.localpath(p)))
750 os.path.basename(util.localpath(p)))
752 else:
751 else:
753 abspfx = pathutil.canonpath(repo.root, cwd, pat)
752 abspfx = pathutil.canonpath(repo.root, cwd, pat)
754 if len(abspfx) < len(srcs[0][0]):
753 if len(abspfx) < len(srcs[0][0]):
755 # A directory. Either the target path contains the last
754 # A directory. Either the target path contains the last
756 # component of the source path or it does not.
755 # component of the source path or it does not.
757 def evalpath(striplen):
756 def evalpath(striplen):
758 score = 0
757 score = 0
759 for s in srcs:
758 for s in srcs:
760 t = os.path.join(dest, util.localpath(s[0])[striplen:])
759 t = os.path.join(dest, util.localpath(s[0])[striplen:])
761 if os.path.lexists(t):
760 if os.path.lexists(t):
762 score += 1
761 score += 1
763 return score
762 return score
764
763
765 abspfx = util.localpath(abspfx)
764 abspfx = util.localpath(abspfx)
766 striplen = len(abspfx)
765 striplen = len(abspfx)
767 if striplen:
766 if striplen:
768 striplen += len(os.sep)
767 striplen += len(os.sep)
769 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
768 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
770 score = evalpath(striplen)
769 score = evalpath(striplen)
771 striplen1 = len(os.path.split(abspfx)[0])
770 striplen1 = len(os.path.split(abspfx)[0])
772 if striplen1:
771 if striplen1:
773 striplen1 += len(os.sep)
772 striplen1 += len(os.sep)
774 if evalpath(striplen1) > score:
773 if evalpath(striplen1) > score:
775 striplen = striplen1
774 striplen = striplen1
776 res = lambda p: os.path.join(dest,
775 res = lambda p: os.path.join(dest,
777 util.localpath(p)[striplen:])
776 util.localpath(p)[striplen:])
778 else:
777 else:
779 # a file
778 # a file
780 if destdirexists:
779 if destdirexists:
781 res = lambda p: os.path.join(dest,
780 res = lambda p: os.path.join(dest,
782 os.path.basename(util.localpath(p)))
781 os.path.basename(util.localpath(p)))
783 else:
782 else:
784 res = lambda p: dest
783 res = lambda p: dest
785 return res
784 return res
786
785
787 pats = scmutil.expandpats(pats)
786 pats = scmutil.expandpats(pats)
788 if not pats:
787 if not pats:
789 raise error.Abort(_('no source or destination specified'))
788 raise error.Abort(_('no source or destination specified'))
790 if len(pats) == 1:
789 if len(pats) == 1:
791 raise error.Abort(_('no destination specified'))
790 raise error.Abort(_('no destination specified'))
792 dest = pats.pop()
791 dest = pats.pop()
793 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
792 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
794 if not destdirexists:
793 if not destdirexists:
795 if len(pats) > 1 or matchmod.patkind(pats[0]):
794 if len(pats) > 1 or matchmod.patkind(pats[0]):
796 raise error.Abort(_('with multiple sources, destination must be an '
795 raise error.Abort(_('with multiple sources, destination must be an '
797 'existing directory'))
796 'existing directory'))
798 if util.endswithsep(dest):
797 if util.endswithsep(dest):
799 raise error.Abort(_('destination %s is not a directory') % dest)
798 raise error.Abort(_('destination %s is not a directory') % dest)
800
799
801 tfn = targetpathfn
800 tfn = targetpathfn
802 if after:
801 if after:
803 tfn = targetpathafterfn
802 tfn = targetpathafterfn
804 copylist = []
803 copylist = []
805 for pat in pats:
804 for pat in pats:
806 srcs = walkpat(pat)
805 srcs = walkpat(pat)
807 if not srcs:
806 if not srcs:
808 continue
807 continue
809 copylist.append((tfn(pat, dest, srcs), srcs))
808 copylist.append((tfn(pat, dest, srcs), srcs))
810 if not copylist:
809 if not copylist:
811 raise error.Abort(_('no files to copy'))
810 raise error.Abort(_('no files to copy'))
812
811
813 errors = 0
812 errors = 0
814 for targetpath, srcs in copylist:
813 for targetpath, srcs in copylist:
815 for abssrc, relsrc, exact in srcs:
814 for abssrc, relsrc, exact in srcs:
816 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
815 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
817 errors += 1
816 errors += 1
818
817
819 if errors:
818 if errors:
820 ui.warn(_('(consider using --after)\n'))
819 ui.warn(_('(consider using --after)\n'))
821
820
822 return errors != 0
821 return errors != 0
823
822
824 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
825 runargs=None, appendpid=False):
826 '''Run a command as a service.'''
827
828 def writepid(pid):
829 if opts['pid_file']:
830 if appendpid:
831 mode = 'a'
832 else:
833 mode = 'w'
834 fp = open(opts['pid_file'], mode)
835 fp.write(str(pid) + '\n')
836 fp.close()
837
838 if opts['daemon'] and not opts['daemon_postexec']:
839 # Signal child process startup with file removal
840 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
841 os.close(lockfd)
842 try:
843 if not runargs:
844 runargs = util.hgcmd() + sys.argv[1:]
845 runargs.append('--daemon-postexec=unlink:%s' % lockpath)
846 # Don't pass --cwd to the child process, because we've already
847 # changed directory.
848 for i in xrange(1, len(runargs)):
849 if runargs[i].startswith('--cwd='):
850 del runargs[i]
851 break
852 elif runargs[i].startswith('--cwd'):
853 del runargs[i:i + 2]
854 break
855 def condfn():
856 return not os.path.exists(lockpath)
857 pid = util.rundetached(runargs, condfn)
858 if pid < 0:
859 raise error.Abort(_('child process failed to start'))
860 writepid(pid)
861 finally:
862 try:
863 os.unlink(lockpath)
864 except OSError as e:
865 if e.errno != errno.ENOENT:
866 raise
867 if parentfn:
868 return parentfn(pid)
869 else:
870 return
871
872 if initfn:
873 initfn()
874
875 if not opts['daemon']:
876 writepid(util.getpid())
877
878 if opts['daemon_postexec']:
879 try:
880 os.setsid()
881 except AttributeError:
882 pass
883 for inst in opts['daemon_postexec']:
884 if inst.startswith('unlink:'):
885 lockpath = inst[7:]
886 os.unlink(lockpath)
887 elif inst.startswith('chdir:'):
888 os.chdir(inst[6:])
889 elif inst != 'none':
890 raise error.Abort(_('invalid value for --daemon-postexec: %s')
891 % inst)
892 util.hidewindow()
893 util.stdout.flush()
894 util.stderr.flush()
895
896 nullfd = os.open(os.devnull, os.O_RDWR)
897 logfilefd = nullfd
898 if logfile:
899 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
900 os.dup2(nullfd, 0)
901 os.dup2(logfilefd, 1)
902 os.dup2(logfilefd, 2)
903 if nullfd not in (0, 1, 2):
904 os.close(nullfd)
905 if logfile and logfilefd not in (0, 1, 2):
906 os.close(logfilefd)
907
908 if runfn:
909 return runfn()
910
911 ## facility to let extension process additional data into an import patch
823 ## facility to let extension process additional data into an import patch
912 # list of identifier to be executed in order
824 # list of identifier to be executed in order
913 extrapreimport = [] # run before commit
825 extrapreimport = [] # run before commit
914 extrapostimport = [] # run after commit
826 extrapostimport = [] # run after commit
915 # mapping from identifier to actual import function
827 # mapping from identifier to actual import function
916 #
828 #
917 # 'preimport' are run before the commit is made and are provided the following
829 # 'preimport' are run before the commit is made and are provided the following
918 # arguments:
830 # arguments:
919 # - repo: the localrepository instance,
831 # - repo: the localrepository instance,
920 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
832 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
921 # - extra: the future extra dictionary of the changeset, please mutate it,
833 # - extra: the future extra dictionary of the changeset, please mutate it,
922 # - opts: the import options.
834 # - opts: the import options.
923 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
835 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
924 # mutation of in memory commit and more. Feel free to rework the code to get
836 # mutation of in memory commit and more. Feel free to rework the code to get
925 # there.
837 # there.
926 extrapreimportmap = {}
838 extrapreimportmap = {}
927 # 'postimport' are run after the commit is made and are provided the following
839 # 'postimport' are run after the commit is made and are provided the following
928 # argument:
840 # argument:
929 # - ctx: the changectx created by import.
841 # - ctx: the changectx created by import.
930 extrapostimportmap = {}
842 extrapostimportmap = {}
931
843
932 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
844 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
933 """Utility function used by commands.import to import a single patch
845 """Utility function used by commands.import to import a single patch
934
846
935 This function is explicitly defined here to help the evolve extension to
847 This function is explicitly defined here to help the evolve extension to
936 wrap this part of the import logic.
848 wrap this part of the import logic.
937
849
938 The API is currently a bit ugly because it a simple code translation from
850 The API is currently a bit ugly because it a simple code translation from
939 the import command. Feel free to make it better.
851 the import command. Feel free to make it better.
940
852
941 :hunk: a patch (as a binary string)
853 :hunk: a patch (as a binary string)
942 :parents: nodes that will be parent of the created commit
854 :parents: nodes that will be parent of the created commit
943 :opts: the full dict of option passed to the import command
855 :opts: the full dict of option passed to the import command
944 :msgs: list to save commit message to.
856 :msgs: list to save commit message to.
945 (used in case we need to save it when failing)
857 (used in case we need to save it when failing)
946 :updatefunc: a function that update a repo to a given node
858 :updatefunc: a function that update a repo to a given node
947 updatefunc(<repo>, <node>)
859 updatefunc(<repo>, <node>)
948 """
860 """
949 # avoid cycle context -> subrepo -> cmdutil
861 # avoid cycle context -> subrepo -> cmdutil
950 from . import context
862 from . import context
951 extractdata = patch.extract(ui, hunk)
863 extractdata = patch.extract(ui, hunk)
952 tmpname = extractdata.get('filename')
864 tmpname = extractdata.get('filename')
953 message = extractdata.get('message')
865 message = extractdata.get('message')
954 user = opts.get('user') or extractdata.get('user')
866 user = opts.get('user') or extractdata.get('user')
955 date = opts.get('date') or extractdata.get('date')
867 date = opts.get('date') or extractdata.get('date')
956 branch = extractdata.get('branch')
868 branch = extractdata.get('branch')
957 nodeid = extractdata.get('nodeid')
869 nodeid = extractdata.get('nodeid')
958 p1 = extractdata.get('p1')
870 p1 = extractdata.get('p1')
959 p2 = extractdata.get('p2')
871 p2 = extractdata.get('p2')
960
872
961 nocommit = opts.get('no_commit')
873 nocommit = opts.get('no_commit')
962 importbranch = opts.get('import_branch')
874 importbranch = opts.get('import_branch')
963 update = not opts.get('bypass')
875 update = not opts.get('bypass')
964 strip = opts["strip"]
876 strip = opts["strip"]
965 prefix = opts["prefix"]
877 prefix = opts["prefix"]
966 sim = float(opts.get('similarity') or 0)
878 sim = float(opts.get('similarity') or 0)
967 if not tmpname:
879 if not tmpname:
968 return (None, None, False)
880 return (None, None, False)
969
881
970 rejects = False
882 rejects = False
971
883
972 try:
884 try:
973 cmdline_message = logmessage(ui, opts)
885 cmdline_message = logmessage(ui, opts)
974 if cmdline_message:
886 if cmdline_message:
975 # pickup the cmdline msg
887 # pickup the cmdline msg
976 message = cmdline_message
888 message = cmdline_message
977 elif message:
889 elif message:
978 # pickup the patch msg
890 # pickup the patch msg
979 message = message.strip()
891 message = message.strip()
980 else:
892 else:
981 # launch the editor
893 # launch the editor
982 message = None
894 message = None
983 ui.debug('message:\n%s\n' % message)
895 ui.debug('message:\n%s\n' % message)
984
896
985 if len(parents) == 1:
897 if len(parents) == 1:
986 parents.append(repo[nullid])
898 parents.append(repo[nullid])
987 if opts.get('exact'):
899 if opts.get('exact'):
988 if not nodeid or not p1:
900 if not nodeid or not p1:
989 raise error.Abort(_('not a Mercurial patch'))
901 raise error.Abort(_('not a Mercurial patch'))
990 p1 = repo[p1]
902 p1 = repo[p1]
991 p2 = repo[p2 or nullid]
903 p2 = repo[p2 or nullid]
992 elif p2:
904 elif p2:
993 try:
905 try:
994 p1 = repo[p1]
906 p1 = repo[p1]
995 p2 = repo[p2]
907 p2 = repo[p2]
996 # Without any options, consider p2 only if the
908 # Without any options, consider p2 only if the
997 # patch is being applied on top of the recorded
909 # patch is being applied on top of the recorded
998 # first parent.
910 # first parent.
999 if p1 != parents[0]:
911 if p1 != parents[0]:
1000 p1 = parents[0]
912 p1 = parents[0]
1001 p2 = repo[nullid]
913 p2 = repo[nullid]
1002 except error.RepoError:
914 except error.RepoError:
1003 p1, p2 = parents
915 p1, p2 = parents
1004 if p2.node() == nullid:
916 if p2.node() == nullid:
1005 ui.warn(_("warning: import the patch as a normal revision\n"
917 ui.warn(_("warning: import the patch as a normal revision\n"
1006 "(use --exact to import the patch as a merge)\n"))
918 "(use --exact to import the patch as a merge)\n"))
1007 else:
919 else:
1008 p1, p2 = parents
920 p1, p2 = parents
1009
921
1010 n = None
922 n = None
1011 if update:
923 if update:
1012 if p1 != parents[0]:
924 if p1 != parents[0]:
1013 updatefunc(repo, p1.node())
925 updatefunc(repo, p1.node())
1014 if p2 != parents[1]:
926 if p2 != parents[1]:
1015 repo.setparents(p1.node(), p2.node())
927 repo.setparents(p1.node(), p2.node())
1016
928
1017 if opts.get('exact') or importbranch:
929 if opts.get('exact') or importbranch:
1018 repo.dirstate.setbranch(branch or 'default')
930 repo.dirstate.setbranch(branch or 'default')
1019
931
1020 partial = opts.get('partial', False)
932 partial = opts.get('partial', False)
1021 files = set()
933 files = set()
1022 try:
934 try:
1023 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
935 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
1024 files=files, eolmode=None, similarity=sim / 100.0)
936 files=files, eolmode=None, similarity=sim / 100.0)
1025 except patch.PatchError as e:
937 except patch.PatchError as e:
1026 if not partial:
938 if not partial:
1027 raise error.Abort(str(e))
939 raise error.Abort(str(e))
1028 if partial:
940 if partial:
1029 rejects = True
941 rejects = True
1030
942
1031 files = list(files)
943 files = list(files)
1032 if nocommit:
944 if nocommit:
1033 if message:
945 if message:
1034 msgs.append(message)
946 msgs.append(message)
1035 else:
947 else:
1036 if opts.get('exact') or p2:
948 if opts.get('exact') or p2:
1037 # If you got here, you either use --force and know what
949 # If you got here, you either use --force and know what
1038 # you are doing or used --exact or a merge patch while
950 # you are doing or used --exact or a merge patch while
1039 # being updated to its first parent.
951 # being updated to its first parent.
1040 m = None
952 m = None
1041 else:
953 else:
1042 m = scmutil.matchfiles(repo, files or [])
954 m = scmutil.matchfiles(repo, files or [])
1043 editform = mergeeditform(repo[None], 'import.normal')
955 editform = mergeeditform(repo[None], 'import.normal')
1044 if opts.get('exact'):
956 if opts.get('exact'):
1045 editor = None
957 editor = None
1046 else:
958 else:
1047 editor = getcommiteditor(editform=editform, **opts)
959 editor = getcommiteditor(editform=editform, **opts)
1048 allowemptyback = repo.ui.backupconfig('ui', 'allowemptycommit')
960 allowemptyback = repo.ui.backupconfig('ui', 'allowemptycommit')
1049 extra = {}
961 extra = {}
1050 for idfunc in extrapreimport:
962 for idfunc in extrapreimport:
1051 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
963 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
1052 try:
964 try:
1053 if partial:
965 if partial:
1054 repo.ui.setconfig('ui', 'allowemptycommit', True)
966 repo.ui.setconfig('ui', 'allowemptycommit', True)
1055 n = repo.commit(message, user,
967 n = repo.commit(message, user,
1056 date, match=m,
968 date, match=m,
1057 editor=editor, extra=extra)
969 editor=editor, extra=extra)
1058 for idfunc in extrapostimport:
970 for idfunc in extrapostimport:
1059 extrapostimportmap[idfunc](repo[n])
971 extrapostimportmap[idfunc](repo[n])
1060 finally:
972 finally:
1061 repo.ui.restoreconfig(allowemptyback)
973 repo.ui.restoreconfig(allowemptyback)
1062 else:
974 else:
1063 if opts.get('exact') or importbranch:
975 if opts.get('exact') or importbranch:
1064 branch = branch or 'default'
976 branch = branch or 'default'
1065 else:
977 else:
1066 branch = p1.branch()
978 branch = p1.branch()
1067 store = patch.filestore()
979 store = patch.filestore()
1068 try:
980 try:
1069 files = set()
981 files = set()
1070 try:
982 try:
1071 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
983 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1072 files, eolmode=None)
984 files, eolmode=None)
1073 except patch.PatchError as e:
985 except patch.PatchError as e:
1074 raise error.Abort(str(e))
986 raise error.Abort(str(e))
1075 if opts.get('exact'):
987 if opts.get('exact'):
1076 editor = None
988 editor = None
1077 else:
989 else:
1078 editor = getcommiteditor(editform='import.bypass')
990 editor = getcommiteditor(editform='import.bypass')
1079 memctx = context.makememctx(repo, (p1.node(), p2.node()),
991 memctx = context.makememctx(repo, (p1.node(), p2.node()),
1080 message,
992 message,
1081 user,
993 user,
1082 date,
994 date,
1083 branch, files, store,
995 branch, files, store,
1084 editor=editor)
996 editor=editor)
1085 n = memctx.commit()
997 n = memctx.commit()
1086 finally:
998 finally:
1087 store.close()
999 store.close()
1088 if opts.get('exact') and nocommit:
1000 if opts.get('exact') and nocommit:
1089 # --exact with --no-commit is still useful in that it does merge
1001 # --exact with --no-commit is still useful in that it does merge
1090 # and branch bits
1002 # and branch bits
1091 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1003 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1092 elif opts.get('exact') and hex(n) != nodeid:
1004 elif opts.get('exact') and hex(n) != nodeid:
1093 raise error.Abort(_('patch is damaged or loses information'))
1005 raise error.Abort(_('patch is damaged or loses information'))
1094 msg = _('applied to working directory')
1006 msg = _('applied to working directory')
1095 if n:
1007 if n:
1096 # i18n: refers to a short changeset id
1008 # i18n: refers to a short changeset id
1097 msg = _('created %s') % short(n)
1009 msg = _('created %s') % short(n)
1098 return (msg, n, rejects)
1010 return (msg, n, rejects)
1099 finally:
1011 finally:
1100 os.unlink(tmpname)
1012 os.unlink(tmpname)
1101
1013
1102 # facility to let extensions include additional data in an exported patch
1014 # facility to let extensions include additional data in an exported patch
1103 # list of identifiers to be executed in order
1015 # list of identifiers to be executed in order
1104 extraexport = []
1016 extraexport = []
1105 # mapping from identifier to actual export function
1017 # mapping from identifier to actual export function
1106 # function as to return a string to be added to the header or None
1018 # function as to return a string to be added to the header or None
1107 # it is given two arguments (sequencenumber, changectx)
1019 # it is given two arguments (sequencenumber, changectx)
1108 extraexportmap = {}
1020 extraexportmap = {}
1109
1021
1110 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1022 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1111 opts=None, match=None):
1023 opts=None, match=None):
1112 '''export changesets as hg patches.'''
1024 '''export changesets as hg patches.'''
1113
1025
1114 total = len(revs)
1026 total = len(revs)
1115 revwidth = max([len(str(rev)) for rev in revs])
1027 revwidth = max([len(str(rev)) for rev in revs])
1116 filemode = {}
1028 filemode = {}
1117
1029
1118 def single(rev, seqno, fp):
1030 def single(rev, seqno, fp):
1119 ctx = repo[rev]
1031 ctx = repo[rev]
1120 node = ctx.node()
1032 node = ctx.node()
1121 parents = [p.node() for p in ctx.parents() if p]
1033 parents = [p.node() for p in ctx.parents() if p]
1122 branch = ctx.branch()
1034 branch = ctx.branch()
1123 if switch_parent:
1035 if switch_parent:
1124 parents.reverse()
1036 parents.reverse()
1125
1037
1126 if parents:
1038 if parents:
1127 prev = parents[0]
1039 prev = parents[0]
1128 else:
1040 else:
1129 prev = nullid
1041 prev = nullid
1130
1042
1131 shouldclose = False
1043 shouldclose = False
1132 if not fp and len(template) > 0:
1044 if not fp and len(template) > 0:
1133 desc_lines = ctx.description().rstrip().split('\n')
1045 desc_lines = ctx.description().rstrip().split('\n')
1134 desc = desc_lines[0] #Commit always has a first line.
1046 desc = desc_lines[0] #Commit always has a first line.
1135 fp = makefileobj(repo, template, node, desc=desc, total=total,
1047 fp = makefileobj(repo, template, node, desc=desc, total=total,
1136 seqno=seqno, revwidth=revwidth, mode='wb',
1048 seqno=seqno, revwidth=revwidth, mode='wb',
1137 modemap=filemode)
1049 modemap=filemode)
1138 shouldclose = True
1050 shouldclose = True
1139 if fp and not getattr(fp, 'name', '<unnamed>').startswith('<'):
1051 if fp and not getattr(fp, 'name', '<unnamed>').startswith('<'):
1140 repo.ui.note("%s\n" % fp.name)
1052 repo.ui.note("%s\n" % fp.name)
1141
1053
1142 if not fp:
1054 if not fp:
1143 write = repo.ui.write
1055 write = repo.ui.write
1144 else:
1056 else:
1145 def write(s, **kw):
1057 def write(s, **kw):
1146 fp.write(s)
1058 fp.write(s)
1147
1059
1148 write("# HG changeset patch\n")
1060 write("# HG changeset patch\n")
1149 write("# User %s\n" % ctx.user())
1061 write("# User %s\n" % ctx.user())
1150 write("# Date %d %d\n" % ctx.date())
1062 write("# Date %d %d\n" % ctx.date())
1151 write("# %s\n" % util.datestr(ctx.date()))
1063 write("# %s\n" % util.datestr(ctx.date()))
1152 if branch and branch != 'default':
1064 if branch and branch != 'default':
1153 write("# Branch %s\n" % branch)
1065 write("# Branch %s\n" % branch)
1154 write("# Node ID %s\n" % hex(node))
1066 write("# Node ID %s\n" % hex(node))
1155 write("# Parent %s\n" % hex(prev))
1067 write("# Parent %s\n" % hex(prev))
1156 if len(parents) > 1:
1068 if len(parents) > 1:
1157 write("# Parent %s\n" % hex(parents[1]))
1069 write("# Parent %s\n" % hex(parents[1]))
1158
1070
1159 for headerid in extraexport:
1071 for headerid in extraexport:
1160 header = extraexportmap[headerid](seqno, ctx)
1072 header = extraexportmap[headerid](seqno, ctx)
1161 if header is not None:
1073 if header is not None:
1162 write('# %s\n' % header)
1074 write('# %s\n' % header)
1163 write(ctx.description().rstrip())
1075 write(ctx.description().rstrip())
1164 write("\n\n")
1076 write("\n\n")
1165
1077
1166 for chunk, label in patch.diffui(repo, prev, node, match, opts=opts):
1078 for chunk, label in patch.diffui(repo, prev, node, match, opts=opts):
1167 write(chunk, label=label)
1079 write(chunk, label=label)
1168
1080
1169 if shouldclose:
1081 if shouldclose:
1170 fp.close()
1082 fp.close()
1171
1083
1172 for seqno, rev in enumerate(revs):
1084 for seqno, rev in enumerate(revs):
1173 single(rev, seqno + 1, fp)
1085 single(rev, seqno + 1, fp)
1174
1086
1175 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1087 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1176 changes=None, stat=False, fp=None, prefix='',
1088 changes=None, stat=False, fp=None, prefix='',
1177 root='', listsubrepos=False):
1089 root='', listsubrepos=False):
1178 '''show diff or diffstat.'''
1090 '''show diff or diffstat.'''
1179 if fp is None:
1091 if fp is None:
1180 write = ui.write
1092 write = ui.write
1181 else:
1093 else:
1182 def write(s, **kw):
1094 def write(s, **kw):
1183 fp.write(s)
1095 fp.write(s)
1184
1096
1185 if root:
1097 if root:
1186 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1098 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1187 else:
1099 else:
1188 relroot = ''
1100 relroot = ''
1189 if relroot != '':
1101 if relroot != '':
1190 # XXX relative roots currently don't work if the root is within a
1102 # XXX relative roots currently don't work if the root is within a
1191 # subrepo
1103 # subrepo
1192 uirelroot = match.uipath(relroot)
1104 uirelroot = match.uipath(relroot)
1193 relroot += '/'
1105 relroot += '/'
1194 for matchroot in match.files():
1106 for matchroot in match.files():
1195 if not matchroot.startswith(relroot):
1107 if not matchroot.startswith(relroot):
1196 ui.warn(_('warning: %s not inside relative root %s\n') % (
1108 ui.warn(_('warning: %s not inside relative root %s\n') % (
1197 match.uipath(matchroot), uirelroot))
1109 match.uipath(matchroot), uirelroot))
1198
1110
1199 if stat:
1111 if stat:
1200 diffopts = diffopts.copy(context=0)
1112 diffopts = diffopts.copy(context=0)
1201 width = 80
1113 width = 80
1202 if not ui.plain():
1114 if not ui.plain():
1203 width = ui.termwidth()
1115 width = ui.termwidth()
1204 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1116 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1205 prefix=prefix, relroot=relroot)
1117 prefix=prefix, relroot=relroot)
1206 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1118 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1207 width=width):
1119 width=width):
1208 write(chunk, label=label)
1120 write(chunk, label=label)
1209 else:
1121 else:
1210 for chunk, label in patch.diffui(repo, node1, node2, match,
1122 for chunk, label in patch.diffui(repo, node1, node2, match,
1211 changes, diffopts, prefix=prefix,
1123 changes, diffopts, prefix=prefix,
1212 relroot=relroot):
1124 relroot=relroot):
1213 write(chunk, label=label)
1125 write(chunk, label=label)
1214
1126
1215 if listsubrepos:
1127 if listsubrepos:
1216 ctx1 = repo[node1]
1128 ctx1 = repo[node1]
1217 ctx2 = repo[node2]
1129 ctx2 = repo[node2]
1218 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1130 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1219 tempnode2 = node2
1131 tempnode2 = node2
1220 try:
1132 try:
1221 if node2 is not None:
1133 if node2 is not None:
1222 tempnode2 = ctx2.substate[subpath][1]
1134 tempnode2 = ctx2.substate[subpath][1]
1223 except KeyError:
1135 except KeyError:
1224 # A subrepo that existed in node1 was deleted between node1 and
1136 # A subrepo that existed in node1 was deleted between node1 and
1225 # node2 (inclusive). Thus, ctx2's substate won't contain that
1137 # node2 (inclusive). Thus, ctx2's substate won't contain that
1226 # subpath. The best we can do is to ignore it.
1138 # subpath. The best we can do is to ignore it.
1227 tempnode2 = None
1139 tempnode2 = None
1228 submatch = matchmod.subdirmatcher(subpath, match)
1140 submatch = matchmod.subdirmatcher(subpath, match)
1229 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1141 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1230 stat=stat, fp=fp, prefix=prefix)
1142 stat=stat, fp=fp, prefix=prefix)
1231
1143
1232 class changeset_printer(object):
1144 class changeset_printer(object):
1233 '''show changeset information when templating not requested.'''
1145 '''show changeset information when templating not requested.'''
1234
1146
1235 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1147 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1236 self.ui = ui
1148 self.ui = ui
1237 self.repo = repo
1149 self.repo = repo
1238 self.buffered = buffered
1150 self.buffered = buffered
1239 self.matchfn = matchfn
1151 self.matchfn = matchfn
1240 self.diffopts = diffopts
1152 self.diffopts = diffopts
1241 self.header = {}
1153 self.header = {}
1242 self.hunk = {}
1154 self.hunk = {}
1243 self.lastheader = None
1155 self.lastheader = None
1244 self.footer = None
1156 self.footer = None
1245
1157
1246 def flush(self, ctx):
1158 def flush(self, ctx):
1247 rev = ctx.rev()
1159 rev = ctx.rev()
1248 if rev in self.header:
1160 if rev in self.header:
1249 h = self.header[rev]
1161 h = self.header[rev]
1250 if h != self.lastheader:
1162 if h != self.lastheader:
1251 self.lastheader = h
1163 self.lastheader = h
1252 self.ui.write(h)
1164 self.ui.write(h)
1253 del self.header[rev]
1165 del self.header[rev]
1254 if rev in self.hunk:
1166 if rev in self.hunk:
1255 self.ui.write(self.hunk[rev])
1167 self.ui.write(self.hunk[rev])
1256 del self.hunk[rev]
1168 del self.hunk[rev]
1257 return 1
1169 return 1
1258 return 0
1170 return 0
1259
1171
1260 def close(self):
1172 def close(self):
1261 if self.footer:
1173 if self.footer:
1262 self.ui.write(self.footer)
1174 self.ui.write(self.footer)
1263
1175
1264 def show(self, ctx, copies=None, matchfn=None, **props):
1176 def show(self, ctx, copies=None, matchfn=None, **props):
1265 if self.buffered:
1177 if self.buffered:
1266 self.ui.pushbuffer(labeled=True)
1178 self.ui.pushbuffer(labeled=True)
1267 self._show(ctx, copies, matchfn, props)
1179 self._show(ctx, copies, matchfn, props)
1268 self.hunk[ctx.rev()] = self.ui.popbuffer()
1180 self.hunk[ctx.rev()] = self.ui.popbuffer()
1269 else:
1181 else:
1270 self._show(ctx, copies, matchfn, props)
1182 self._show(ctx, copies, matchfn, props)
1271
1183
1272 def _show(self, ctx, copies, matchfn, props):
1184 def _show(self, ctx, copies, matchfn, props):
1273 '''show a single changeset or file revision'''
1185 '''show a single changeset or file revision'''
1274 changenode = ctx.node()
1186 changenode = ctx.node()
1275 rev = ctx.rev()
1187 rev = ctx.rev()
1276 if self.ui.debugflag:
1188 if self.ui.debugflag:
1277 hexfunc = hex
1189 hexfunc = hex
1278 else:
1190 else:
1279 hexfunc = short
1191 hexfunc = short
1280 # as of now, wctx.node() and wctx.rev() return None, but we want to
1192 # as of now, wctx.node() and wctx.rev() return None, but we want to
1281 # show the same values as {node} and {rev} templatekw
1193 # show the same values as {node} and {rev} templatekw
1282 revnode = (scmutil.intrev(rev), hexfunc(bin(ctx.hex())))
1194 revnode = (scmutil.intrev(rev), hexfunc(bin(ctx.hex())))
1283
1195
1284 if self.ui.quiet:
1196 if self.ui.quiet:
1285 self.ui.write("%d:%s\n" % revnode, label='log.node')
1197 self.ui.write("%d:%s\n" % revnode, label='log.node')
1286 return
1198 return
1287
1199
1288 date = util.datestr(ctx.date())
1200 date = util.datestr(ctx.date())
1289
1201
1290 # i18n: column positioning for "hg log"
1202 # i18n: column positioning for "hg log"
1291 self.ui.write(_("changeset: %d:%s\n") % revnode,
1203 self.ui.write(_("changeset: %d:%s\n") % revnode,
1292 label='log.changeset changeset.%s' % ctx.phasestr())
1204 label='log.changeset changeset.%s' % ctx.phasestr())
1293
1205
1294 # branches are shown first before any other names due to backwards
1206 # branches are shown first before any other names due to backwards
1295 # compatibility
1207 # compatibility
1296 branch = ctx.branch()
1208 branch = ctx.branch()
1297 # don't show the default branch name
1209 # don't show the default branch name
1298 if branch != 'default':
1210 if branch != 'default':
1299 # i18n: column positioning for "hg log"
1211 # i18n: column positioning for "hg log"
1300 self.ui.write(_("branch: %s\n") % branch,
1212 self.ui.write(_("branch: %s\n") % branch,
1301 label='log.branch')
1213 label='log.branch')
1302
1214
1303 for nsname, ns in self.repo.names.iteritems():
1215 for nsname, ns in self.repo.names.iteritems():
1304 # branches has special logic already handled above, so here we just
1216 # branches has special logic already handled above, so here we just
1305 # skip it
1217 # skip it
1306 if nsname == 'branches':
1218 if nsname == 'branches':
1307 continue
1219 continue
1308 # we will use the templatename as the color name since those two
1220 # we will use the templatename as the color name since those two
1309 # should be the same
1221 # should be the same
1310 for name in ns.names(self.repo, changenode):
1222 for name in ns.names(self.repo, changenode):
1311 self.ui.write(ns.logfmt % name,
1223 self.ui.write(ns.logfmt % name,
1312 label='log.%s' % ns.colorname)
1224 label='log.%s' % ns.colorname)
1313 if self.ui.debugflag:
1225 if self.ui.debugflag:
1314 # i18n: column positioning for "hg log"
1226 # i18n: column positioning for "hg log"
1315 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1227 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1316 label='log.phase')
1228 label='log.phase')
1317 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1229 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1318 label = 'log.parent changeset.%s' % pctx.phasestr()
1230 label = 'log.parent changeset.%s' % pctx.phasestr()
1319 # i18n: column positioning for "hg log"
1231 # i18n: column positioning for "hg log"
1320 self.ui.write(_("parent: %d:%s\n")
1232 self.ui.write(_("parent: %d:%s\n")
1321 % (pctx.rev(), hexfunc(pctx.node())),
1233 % (pctx.rev(), hexfunc(pctx.node())),
1322 label=label)
1234 label=label)
1323
1235
1324 if self.ui.debugflag and rev is not None:
1236 if self.ui.debugflag and rev is not None:
1325 mnode = ctx.manifestnode()
1237 mnode = ctx.manifestnode()
1326 # i18n: column positioning for "hg log"
1238 # i18n: column positioning for "hg log"
1327 self.ui.write(_("manifest: %d:%s\n") %
1239 self.ui.write(_("manifest: %d:%s\n") %
1328 (self.repo.manifestlog._revlog.rev(mnode),
1240 (self.repo.manifestlog._revlog.rev(mnode),
1329 hex(mnode)),
1241 hex(mnode)),
1330 label='ui.debug log.manifest')
1242 label='ui.debug log.manifest')
1331 # i18n: column positioning for "hg log"
1243 # i18n: column positioning for "hg log"
1332 self.ui.write(_("user: %s\n") % ctx.user(),
1244 self.ui.write(_("user: %s\n") % ctx.user(),
1333 label='log.user')
1245 label='log.user')
1334 # i18n: column positioning for "hg log"
1246 # i18n: column positioning for "hg log"
1335 self.ui.write(_("date: %s\n") % date,
1247 self.ui.write(_("date: %s\n") % date,
1336 label='log.date')
1248 label='log.date')
1337
1249
1338 if self.ui.debugflag:
1250 if self.ui.debugflag:
1339 files = ctx.p1().status(ctx)[:3]
1251 files = ctx.p1().status(ctx)[:3]
1340 for key, value in zip([# i18n: column positioning for "hg log"
1252 for key, value in zip([# i18n: column positioning for "hg log"
1341 _("files:"),
1253 _("files:"),
1342 # i18n: column positioning for "hg log"
1254 # i18n: column positioning for "hg log"
1343 _("files+:"),
1255 _("files+:"),
1344 # i18n: column positioning for "hg log"
1256 # i18n: column positioning for "hg log"
1345 _("files-:")], files):
1257 _("files-:")], files):
1346 if value:
1258 if value:
1347 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1259 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1348 label='ui.debug log.files')
1260 label='ui.debug log.files')
1349 elif ctx.files() and self.ui.verbose:
1261 elif ctx.files() and self.ui.verbose:
1350 # i18n: column positioning for "hg log"
1262 # i18n: column positioning for "hg log"
1351 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1263 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1352 label='ui.note log.files')
1264 label='ui.note log.files')
1353 if copies and self.ui.verbose:
1265 if copies and self.ui.verbose:
1354 copies = ['%s (%s)' % c for c in copies]
1266 copies = ['%s (%s)' % c for c in copies]
1355 # i18n: column positioning for "hg log"
1267 # i18n: column positioning for "hg log"
1356 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1268 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1357 label='ui.note log.copies')
1269 label='ui.note log.copies')
1358
1270
1359 extra = ctx.extra()
1271 extra = ctx.extra()
1360 if extra and self.ui.debugflag:
1272 if extra and self.ui.debugflag:
1361 for key, value in sorted(extra.items()):
1273 for key, value in sorted(extra.items()):
1362 # i18n: column positioning for "hg log"
1274 # i18n: column positioning for "hg log"
1363 self.ui.write(_("extra: %s=%s\n")
1275 self.ui.write(_("extra: %s=%s\n")
1364 % (key, value.encode('string_escape')),
1276 % (key, value.encode('string_escape')),
1365 label='ui.debug log.extra')
1277 label='ui.debug log.extra')
1366
1278
1367 description = ctx.description().strip()
1279 description = ctx.description().strip()
1368 if description:
1280 if description:
1369 if self.ui.verbose:
1281 if self.ui.verbose:
1370 self.ui.write(_("description:\n"),
1282 self.ui.write(_("description:\n"),
1371 label='ui.note log.description')
1283 label='ui.note log.description')
1372 self.ui.write(description,
1284 self.ui.write(description,
1373 label='ui.note log.description')
1285 label='ui.note log.description')
1374 self.ui.write("\n\n")
1286 self.ui.write("\n\n")
1375 else:
1287 else:
1376 # i18n: column positioning for "hg log"
1288 # i18n: column positioning for "hg log"
1377 self.ui.write(_("summary: %s\n") %
1289 self.ui.write(_("summary: %s\n") %
1378 description.splitlines()[0],
1290 description.splitlines()[0],
1379 label='log.summary')
1291 label='log.summary')
1380 self.ui.write("\n")
1292 self.ui.write("\n")
1381
1293
1382 self.showpatch(ctx, matchfn)
1294 self.showpatch(ctx, matchfn)
1383
1295
1384 def showpatch(self, ctx, matchfn):
1296 def showpatch(self, ctx, matchfn):
1385 if not matchfn:
1297 if not matchfn:
1386 matchfn = self.matchfn
1298 matchfn = self.matchfn
1387 if matchfn:
1299 if matchfn:
1388 stat = self.diffopts.get('stat')
1300 stat = self.diffopts.get('stat')
1389 diff = self.diffopts.get('patch')
1301 diff = self.diffopts.get('patch')
1390 diffopts = patch.diffallopts(self.ui, self.diffopts)
1302 diffopts = patch.diffallopts(self.ui, self.diffopts)
1391 node = ctx.node()
1303 node = ctx.node()
1392 prev = ctx.p1().node()
1304 prev = ctx.p1().node()
1393 if stat:
1305 if stat:
1394 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1306 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1395 match=matchfn, stat=True)
1307 match=matchfn, stat=True)
1396 if diff:
1308 if diff:
1397 if stat:
1309 if stat:
1398 self.ui.write("\n")
1310 self.ui.write("\n")
1399 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1311 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1400 match=matchfn, stat=False)
1312 match=matchfn, stat=False)
1401 self.ui.write("\n")
1313 self.ui.write("\n")
1402
1314
1403 class jsonchangeset(changeset_printer):
1315 class jsonchangeset(changeset_printer):
1404 '''format changeset information.'''
1316 '''format changeset information.'''
1405
1317
1406 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1318 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1407 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1319 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1408 self.cache = {}
1320 self.cache = {}
1409 self._first = True
1321 self._first = True
1410
1322
1411 def close(self):
1323 def close(self):
1412 if not self._first:
1324 if not self._first:
1413 self.ui.write("\n]\n")
1325 self.ui.write("\n]\n")
1414 else:
1326 else:
1415 self.ui.write("[]\n")
1327 self.ui.write("[]\n")
1416
1328
1417 def _show(self, ctx, copies, matchfn, props):
1329 def _show(self, ctx, copies, matchfn, props):
1418 '''show a single changeset or file revision'''
1330 '''show a single changeset or file revision'''
1419 rev = ctx.rev()
1331 rev = ctx.rev()
1420 if rev is None:
1332 if rev is None:
1421 jrev = jnode = 'null'
1333 jrev = jnode = 'null'
1422 else:
1334 else:
1423 jrev = str(rev)
1335 jrev = str(rev)
1424 jnode = '"%s"' % hex(ctx.node())
1336 jnode = '"%s"' % hex(ctx.node())
1425 j = encoding.jsonescape
1337 j = encoding.jsonescape
1426
1338
1427 if self._first:
1339 if self._first:
1428 self.ui.write("[\n {")
1340 self.ui.write("[\n {")
1429 self._first = False
1341 self._first = False
1430 else:
1342 else:
1431 self.ui.write(",\n {")
1343 self.ui.write(",\n {")
1432
1344
1433 if self.ui.quiet:
1345 if self.ui.quiet:
1434 self.ui.write(('\n "rev": %s') % jrev)
1346 self.ui.write(('\n "rev": %s') % jrev)
1435 self.ui.write((',\n "node": %s') % jnode)
1347 self.ui.write((',\n "node": %s') % jnode)
1436 self.ui.write('\n }')
1348 self.ui.write('\n }')
1437 return
1349 return
1438
1350
1439 self.ui.write(('\n "rev": %s') % jrev)
1351 self.ui.write(('\n "rev": %s') % jrev)
1440 self.ui.write((',\n "node": %s') % jnode)
1352 self.ui.write((',\n "node": %s') % jnode)
1441 self.ui.write((',\n "branch": "%s"') % j(ctx.branch()))
1353 self.ui.write((',\n "branch": "%s"') % j(ctx.branch()))
1442 self.ui.write((',\n "phase": "%s"') % ctx.phasestr())
1354 self.ui.write((',\n "phase": "%s"') % ctx.phasestr())
1443 self.ui.write((',\n "user": "%s"') % j(ctx.user()))
1355 self.ui.write((',\n "user": "%s"') % j(ctx.user()))
1444 self.ui.write((',\n "date": [%d, %d]') % ctx.date())
1356 self.ui.write((',\n "date": [%d, %d]') % ctx.date())
1445 self.ui.write((',\n "desc": "%s"') % j(ctx.description()))
1357 self.ui.write((',\n "desc": "%s"') % j(ctx.description()))
1446
1358
1447 self.ui.write((',\n "bookmarks": [%s]') %
1359 self.ui.write((',\n "bookmarks": [%s]') %
1448 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1360 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1449 self.ui.write((',\n "tags": [%s]') %
1361 self.ui.write((',\n "tags": [%s]') %
1450 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1362 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1451 self.ui.write((',\n "parents": [%s]') %
1363 self.ui.write((',\n "parents": [%s]') %
1452 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1364 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1453
1365
1454 if self.ui.debugflag:
1366 if self.ui.debugflag:
1455 if rev is None:
1367 if rev is None:
1456 jmanifestnode = 'null'
1368 jmanifestnode = 'null'
1457 else:
1369 else:
1458 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1370 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1459 self.ui.write((',\n "manifest": %s') % jmanifestnode)
1371 self.ui.write((',\n "manifest": %s') % jmanifestnode)
1460
1372
1461 self.ui.write((',\n "extra": {%s}') %
1373 self.ui.write((',\n "extra": {%s}') %
1462 ", ".join('"%s": "%s"' % (j(k), j(v))
1374 ", ".join('"%s": "%s"' % (j(k), j(v))
1463 for k, v in ctx.extra().items()))
1375 for k, v in ctx.extra().items()))
1464
1376
1465 files = ctx.p1().status(ctx)
1377 files = ctx.p1().status(ctx)
1466 self.ui.write((',\n "modified": [%s]') %
1378 self.ui.write((',\n "modified": [%s]') %
1467 ", ".join('"%s"' % j(f) for f in files[0]))
1379 ", ".join('"%s"' % j(f) for f in files[0]))
1468 self.ui.write((',\n "added": [%s]') %
1380 self.ui.write((',\n "added": [%s]') %
1469 ", ".join('"%s"' % j(f) for f in files[1]))
1381 ", ".join('"%s"' % j(f) for f in files[1]))
1470 self.ui.write((',\n "removed": [%s]') %
1382 self.ui.write((',\n "removed": [%s]') %
1471 ", ".join('"%s"' % j(f) for f in files[2]))
1383 ", ".join('"%s"' % j(f) for f in files[2]))
1472
1384
1473 elif self.ui.verbose:
1385 elif self.ui.verbose:
1474 self.ui.write((',\n "files": [%s]') %
1386 self.ui.write((',\n "files": [%s]') %
1475 ", ".join('"%s"' % j(f) for f in ctx.files()))
1387 ", ".join('"%s"' % j(f) for f in ctx.files()))
1476
1388
1477 if copies:
1389 if copies:
1478 self.ui.write((',\n "copies": {%s}') %
1390 self.ui.write((',\n "copies": {%s}') %
1479 ", ".join('"%s": "%s"' % (j(k), j(v))
1391 ", ".join('"%s": "%s"' % (j(k), j(v))
1480 for k, v in copies))
1392 for k, v in copies))
1481
1393
1482 matchfn = self.matchfn
1394 matchfn = self.matchfn
1483 if matchfn:
1395 if matchfn:
1484 stat = self.diffopts.get('stat')
1396 stat = self.diffopts.get('stat')
1485 diff = self.diffopts.get('patch')
1397 diff = self.diffopts.get('patch')
1486 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1398 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1487 node, prev = ctx.node(), ctx.p1().node()
1399 node, prev = ctx.node(), ctx.p1().node()
1488 if stat:
1400 if stat:
1489 self.ui.pushbuffer()
1401 self.ui.pushbuffer()
1490 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1402 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1491 match=matchfn, stat=True)
1403 match=matchfn, stat=True)
1492 self.ui.write((',\n "diffstat": "%s"')
1404 self.ui.write((',\n "diffstat": "%s"')
1493 % j(self.ui.popbuffer()))
1405 % j(self.ui.popbuffer()))
1494 if diff:
1406 if diff:
1495 self.ui.pushbuffer()
1407 self.ui.pushbuffer()
1496 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1408 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1497 match=matchfn, stat=False)
1409 match=matchfn, stat=False)
1498 self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer()))
1410 self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer()))
1499
1411
1500 self.ui.write("\n }")
1412 self.ui.write("\n }")
1501
1413
1502 class changeset_templater(changeset_printer):
1414 class changeset_templater(changeset_printer):
1503 '''format changeset information.'''
1415 '''format changeset information.'''
1504
1416
1505 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1417 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1506 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1418 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1507 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1419 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1508 filters = {'formatnode': formatnode}
1420 filters = {'formatnode': formatnode}
1509 defaulttempl = {
1421 defaulttempl = {
1510 'parent': '{rev}:{node|formatnode} ',
1422 'parent': '{rev}:{node|formatnode} ',
1511 'manifest': '{rev}:{node|formatnode}',
1423 'manifest': '{rev}:{node|formatnode}',
1512 'file_copy': '{name} ({source})',
1424 'file_copy': '{name} ({source})',
1513 'extra': '{key}={value|stringescape}'
1425 'extra': '{key}={value|stringescape}'
1514 }
1426 }
1515 # filecopy is preserved for compatibility reasons
1427 # filecopy is preserved for compatibility reasons
1516 defaulttempl['filecopy'] = defaulttempl['file_copy']
1428 defaulttempl['filecopy'] = defaulttempl['file_copy']
1517 assert not (tmpl and mapfile)
1429 assert not (tmpl and mapfile)
1518 if mapfile:
1430 if mapfile:
1519 self.t = templater.templater.frommapfile(mapfile, filters=filters,
1431 self.t = templater.templater.frommapfile(mapfile, filters=filters,
1520 cache=defaulttempl)
1432 cache=defaulttempl)
1521 else:
1433 else:
1522 self.t = formatter.maketemplater(ui, 'changeset', tmpl,
1434 self.t = formatter.maketemplater(ui, 'changeset', tmpl,
1523 filters=filters,
1435 filters=filters,
1524 cache=defaulttempl)
1436 cache=defaulttempl)
1525
1437
1526 self.cache = {}
1438 self.cache = {}
1527
1439
1528 # find correct templates for current mode
1440 # find correct templates for current mode
1529 tmplmodes = [
1441 tmplmodes = [
1530 (True, None),
1442 (True, None),
1531 (self.ui.verbose, 'verbose'),
1443 (self.ui.verbose, 'verbose'),
1532 (self.ui.quiet, 'quiet'),
1444 (self.ui.quiet, 'quiet'),
1533 (self.ui.debugflag, 'debug'),
1445 (self.ui.debugflag, 'debug'),
1534 ]
1446 ]
1535
1447
1536 self._parts = {'header': '', 'footer': '', 'changeset': 'changeset',
1448 self._parts = {'header': '', 'footer': '', 'changeset': 'changeset',
1537 'docheader': '', 'docfooter': ''}
1449 'docheader': '', 'docfooter': ''}
1538 for mode, postfix in tmplmodes:
1450 for mode, postfix in tmplmodes:
1539 for t in self._parts:
1451 for t in self._parts:
1540 cur = t
1452 cur = t
1541 if postfix:
1453 if postfix:
1542 cur += "_" + postfix
1454 cur += "_" + postfix
1543 if mode and cur in self.t:
1455 if mode and cur in self.t:
1544 self._parts[t] = cur
1456 self._parts[t] = cur
1545
1457
1546 if self._parts['docheader']:
1458 if self._parts['docheader']:
1547 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1459 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1548
1460
1549 def close(self):
1461 def close(self):
1550 if self._parts['docfooter']:
1462 if self._parts['docfooter']:
1551 if not self.footer:
1463 if not self.footer:
1552 self.footer = ""
1464 self.footer = ""
1553 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1465 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1554 return super(changeset_templater, self).close()
1466 return super(changeset_templater, self).close()
1555
1467
1556 def _show(self, ctx, copies, matchfn, props):
1468 def _show(self, ctx, copies, matchfn, props):
1557 '''show a single changeset or file revision'''
1469 '''show a single changeset or file revision'''
1558 props = props.copy()
1470 props = props.copy()
1559 props.update(templatekw.keywords)
1471 props.update(templatekw.keywords)
1560 props['templ'] = self.t
1472 props['templ'] = self.t
1561 props['ctx'] = ctx
1473 props['ctx'] = ctx
1562 props['repo'] = self.repo
1474 props['repo'] = self.repo
1563 props['ui'] = self.repo.ui
1475 props['ui'] = self.repo.ui
1564 props['revcache'] = {'copies': copies}
1476 props['revcache'] = {'copies': copies}
1565 props['cache'] = self.cache
1477 props['cache'] = self.cache
1566
1478
1567 # write header
1479 # write header
1568 if self._parts['header']:
1480 if self._parts['header']:
1569 h = templater.stringify(self.t(self._parts['header'], **props))
1481 h = templater.stringify(self.t(self._parts['header'], **props))
1570 if self.buffered:
1482 if self.buffered:
1571 self.header[ctx.rev()] = h
1483 self.header[ctx.rev()] = h
1572 else:
1484 else:
1573 if self.lastheader != h:
1485 if self.lastheader != h:
1574 self.lastheader = h
1486 self.lastheader = h
1575 self.ui.write(h)
1487 self.ui.write(h)
1576
1488
1577 # write changeset metadata, then patch if requested
1489 # write changeset metadata, then patch if requested
1578 key = self._parts['changeset']
1490 key = self._parts['changeset']
1579 self.ui.write(templater.stringify(self.t(key, **props)))
1491 self.ui.write(templater.stringify(self.t(key, **props)))
1580 self.showpatch(ctx, matchfn)
1492 self.showpatch(ctx, matchfn)
1581
1493
1582 if self._parts['footer']:
1494 if self._parts['footer']:
1583 if not self.footer:
1495 if not self.footer:
1584 self.footer = templater.stringify(
1496 self.footer = templater.stringify(
1585 self.t(self._parts['footer'], **props))
1497 self.t(self._parts['footer'], **props))
1586
1498
1587 def gettemplate(ui, tmpl, style):
1499 def gettemplate(ui, tmpl, style):
1588 """
1500 """
1589 Find the template matching the given template spec or style.
1501 Find the template matching the given template spec or style.
1590 """
1502 """
1591
1503
1592 # ui settings
1504 # ui settings
1593 if not tmpl and not style: # template are stronger than style
1505 if not tmpl and not style: # template are stronger than style
1594 tmpl = ui.config('ui', 'logtemplate')
1506 tmpl = ui.config('ui', 'logtemplate')
1595 if tmpl:
1507 if tmpl:
1596 return templater.unquotestring(tmpl), None
1508 return templater.unquotestring(tmpl), None
1597 else:
1509 else:
1598 style = util.expandpath(ui.config('ui', 'style', ''))
1510 style = util.expandpath(ui.config('ui', 'style', ''))
1599
1511
1600 if not tmpl and style:
1512 if not tmpl and style:
1601 mapfile = style
1513 mapfile = style
1602 if not os.path.split(mapfile)[0]:
1514 if not os.path.split(mapfile)[0]:
1603 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1515 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1604 or templater.templatepath(mapfile))
1516 or templater.templatepath(mapfile))
1605 if mapname:
1517 if mapname:
1606 mapfile = mapname
1518 mapfile = mapname
1607 return None, mapfile
1519 return None, mapfile
1608
1520
1609 if not tmpl:
1521 if not tmpl:
1610 return None, None
1522 return None, None
1611
1523
1612 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1524 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1613
1525
1614 def show_changeset(ui, repo, opts, buffered=False):
1526 def show_changeset(ui, repo, opts, buffered=False):
1615 """show one changeset using template or regular display.
1527 """show one changeset using template or regular display.
1616
1528
1617 Display format will be the first non-empty hit of:
1529 Display format will be the first non-empty hit of:
1618 1. option 'template'
1530 1. option 'template'
1619 2. option 'style'
1531 2. option 'style'
1620 3. [ui] setting 'logtemplate'
1532 3. [ui] setting 'logtemplate'
1621 4. [ui] setting 'style'
1533 4. [ui] setting 'style'
1622 If all of these values are either the unset or the empty string,
1534 If all of these values are either the unset or the empty string,
1623 regular display via changeset_printer() is done.
1535 regular display via changeset_printer() is done.
1624 """
1536 """
1625 # options
1537 # options
1626 matchfn = None
1538 matchfn = None
1627 if opts.get('patch') or opts.get('stat'):
1539 if opts.get('patch') or opts.get('stat'):
1628 matchfn = scmutil.matchall(repo)
1540 matchfn = scmutil.matchall(repo)
1629
1541
1630 if opts.get('template') == 'json':
1542 if opts.get('template') == 'json':
1631 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1543 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1632
1544
1633 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1545 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1634
1546
1635 if not tmpl and not mapfile:
1547 if not tmpl and not mapfile:
1636 return changeset_printer(ui, repo, matchfn, opts, buffered)
1548 return changeset_printer(ui, repo, matchfn, opts, buffered)
1637
1549
1638 return changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile, buffered)
1550 return changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile, buffered)
1639
1551
1640 def showmarker(fm, marker, index=None):
1552 def showmarker(fm, marker, index=None):
1641 """utility function to display obsolescence marker in a readable way
1553 """utility function to display obsolescence marker in a readable way
1642
1554
1643 To be used by debug function."""
1555 To be used by debug function."""
1644 if index is not None:
1556 if index is not None:
1645 fm.write('index', '%i ', index)
1557 fm.write('index', '%i ', index)
1646 fm.write('precnode', '%s ', hex(marker.precnode()))
1558 fm.write('precnode', '%s ', hex(marker.precnode()))
1647 succs = marker.succnodes()
1559 succs = marker.succnodes()
1648 fm.condwrite(succs, 'succnodes', '%s ',
1560 fm.condwrite(succs, 'succnodes', '%s ',
1649 fm.formatlist(map(hex, succs), name='node'))
1561 fm.formatlist(map(hex, succs), name='node'))
1650 fm.write('flag', '%X ', marker.flags())
1562 fm.write('flag', '%X ', marker.flags())
1651 parents = marker.parentnodes()
1563 parents = marker.parentnodes()
1652 if parents is not None:
1564 if parents is not None:
1653 fm.write('parentnodes', '{%s} ',
1565 fm.write('parentnodes', '{%s} ',
1654 fm.formatlist(map(hex, parents), name='node', sep=', '))
1566 fm.formatlist(map(hex, parents), name='node', sep=', '))
1655 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
1567 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
1656 meta = marker.metadata().copy()
1568 meta = marker.metadata().copy()
1657 meta.pop('date', None)
1569 meta.pop('date', None)
1658 fm.write('metadata', '{%s}', fm.formatdict(meta, fmt='%r: %r', sep=', '))
1570 fm.write('metadata', '{%s}', fm.formatdict(meta, fmt='%r: %r', sep=', '))
1659 fm.plain('\n')
1571 fm.plain('\n')
1660
1572
1661 def finddate(ui, repo, date):
1573 def finddate(ui, repo, date):
1662 """Find the tipmost changeset that matches the given date spec"""
1574 """Find the tipmost changeset that matches the given date spec"""
1663
1575
1664 df = util.matchdate(date)
1576 df = util.matchdate(date)
1665 m = scmutil.matchall(repo)
1577 m = scmutil.matchall(repo)
1666 results = {}
1578 results = {}
1667
1579
1668 def prep(ctx, fns):
1580 def prep(ctx, fns):
1669 d = ctx.date()
1581 d = ctx.date()
1670 if df(d[0]):
1582 if df(d[0]):
1671 results[ctx.rev()] = d
1583 results[ctx.rev()] = d
1672
1584
1673 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1585 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1674 rev = ctx.rev()
1586 rev = ctx.rev()
1675 if rev in results:
1587 if rev in results:
1676 ui.status(_("found revision %s from %s\n") %
1588 ui.status(_("found revision %s from %s\n") %
1677 (rev, util.datestr(results[rev])))
1589 (rev, util.datestr(results[rev])))
1678 return str(rev)
1590 return str(rev)
1679
1591
1680 raise error.Abort(_("revision matching date not found"))
1592 raise error.Abort(_("revision matching date not found"))
1681
1593
1682 def increasingwindows(windowsize=8, sizelimit=512):
1594 def increasingwindows(windowsize=8, sizelimit=512):
1683 while True:
1595 while True:
1684 yield windowsize
1596 yield windowsize
1685 if windowsize < sizelimit:
1597 if windowsize < sizelimit:
1686 windowsize *= 2
1598 windowsize *= 2
1687
1599
1688 class FileWalkError(Exception):
1600 class FileWalkError(Exception):
1689 pass
1601 pass
1690
1602
1691 def walkfilerevs(repo, match, follow, revs, fncache):
1603 def walkfilerevs(repo, match, follow, revs, fncache):
1692 '''Walks the file history for the matched files.
1604 '''Walks the file history for the matched files.
1693
1605
1694 Returns the changeset revs that are involved in the file history.
1606 Returns the changeset revs that are involved in the file history.
1695
1607
1696 Throws FileWalkError if the file history can't be walked using
1608 Throws FileWalkError if the file history can't be walked using
1697 filelogs alone.
1609 filelogs alone.
1698 '''
1610 '''
1699 wanted = set()
1611 wanted = set()
1700 copies = []
1612 copies = []
1701 minrev, maxrev = min(revs), max(revs)
1613 minrev, maxrev = min(revs), max(revs)
1702 def filerevgen(filelog, last):
1614 def filerevgen(filelog, last):
1703 """
1615 """
1704 Only files, no patterns. Check the history of each file.
1616 Only files, no patterns. Check the history of each file.
1705
1617
1706 Examines filelog entries within minrev, maxrev linkrev range
1618 Examines filelog entries within minrev, maxrev linkrev range
1707 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1619 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1708 tuples in backwards order
1620 tuples in backwards order
1709 """
1621 """
1710 cl_count = len(repo)
1622 cl_count = len(repo)
1711 revs = []
1623 revs = []
1712 for j in xrange(0, last + 1):
1624 for j in xrange(0, last + 1):
1713 linkrev = filelog.linkrev(j)
1625 linkrev = filelog.linkrev(j)
1714 if linkrev < minrev:
1626 if linkrev < minrev:
1715 continue
1627 continue
1716 # only yield rev for which we have the changelog, it can
1628 # only yield rev for which we have the changelog, it can
1717 # happen while doing "hg log" during a pull or commit
1629 # happen while doing "hg log" during a pull or commit
1718 if linkrev >= cl_count:
1630 if linkrev >= cl_count:
1719 break
1631 break
1720
1632
1721 parentlinkrevs = []
1633 parentlinkrevs = []
1722 for p in filelog.parentrevs(j):
1634 for p in filelog.parentrevs(j):
1723 if p != nullrev:
1635 if p != nullrev:
1724 parentlinkrevs.append(filelog.linkrev(p))
1636 parentlinkrevs.append(filelog.linkrev(p))
1725 n = filelog.node(j)
1637 n = filelog.node(j)
1726 revs.append((linkrev, parentlinkrevs,
1638 revs.append((linkrev, parentlinkrevs,
1727 follow and filelog.renamed(n)))
1639 follow and filelog.renamed(n)))
1728
1640
1729 return reversed(revs)
1641 return reversed(revs)
1730 def iterfiles():
1642 def iterfiles():
1731 pctx = repo['.']
1643 pctx = repo['.']
1732 for filename in match.files():
1644 for filename in match.files():
1733 if follow:
1645 if follow:
1734 if filename not in pctx:
1646 if filename not in pctx:
1735 raise error.Abort(_('cannot follow file not in parent '
1647 raise error.Abort(_('cannot follow file not in parent '
1736 'revision: "%s"') % filename)
1648 'revision: "%s"') % filename)
1737 yield filename, pctx[filename].filenode()
1649 yield filename, pctx[filename].filenode()
1738 else:
1650 else:
1739 yield filename, None
1651 yield filename, None
1740 for filename_node in copies:
1652 for filename_node in copies:
1741 yield filename_node
1653 yield filename_node
1742
1654
1743 for file_, node in iterfiles():
1655 for file_, node in iterfiles():
1744 filelog = repo.file(file_)
1656 filelog = repo.file(file_)
1745 if not len(filelog):
1657 if not len(filelog):
1746 if node is None:
1658 if node is None:
1747 # A zero count may be a directory or deleted file, so
1659 # A zero count may be a directory or deleted file, so
1748 # try to find matching entries on the slow path.
1660 # try to find matching entries on the slow path.
1749 if follow:
1661 if follow:
1750 raise error.Abort(
1662 raise error.Abort(
1751 _('cannot follow nonexistent file: "%s"') % file_)
1663 _('cannot follow nonexistent file: "%s"') % file_)
1752 raise FileWalkError("Cannot walk via filelog")
1664 raise FileWalkError("Cannot walk via filelog")
1753 else:
1665 else:
1754 continue
1666 continue
1755
1667
1756 if node is None:
1668 if node is None:
1757 last = len(filelog) - 1
1669 last = len(filelog) - 1
1758 else:
1670 else:
1759 last = filelog.rev(node)
1671 last = filelog.rev(node)
1760
1672
1761 # keep track of all ancestors of the file
1673 # keep track of all ancestors of the file
1762 ancestors = set([filelog.linkrev(last)])
1674 ancestors = set([filelog.linkrev(last)])
1763
1675
1764 # iterate from latest to oldest revision
1676 # iterate from latest to oldest revision
1765 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1677 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1766 if not follow:
1678 if not follow:
1767 if rev > maxrev:
1679 if rev > maxrev:
1768 continue
1680 continue
1769 else:
1681 else:
1770 # Note that last might not be the first interesting
1682 # Note that last might not be the first interesting
1771 # rev to us:
1683 # rev to us:
1772 # if the file has been changed after maxrev, we'll
1684 # if the file has been changed after maxrev, we'll
1773 # have linkrev(last) > maxrev, and we still need
1685 # have linkrev(last) > maxrev, and we still need
1774 # to explore the file graph
1686 # to explore the file graph
1775 if rev not in ancestors:
1687 if rev not in ancestors:
1776 continue
1688 continue
1777 # XXX insert 1327 fix here
1689 # XXX insert 1327 fix here
1778 if flparentlinkrevs:
1690 if flparentlinkrevs:
1779 ancestors.update(flparentlinkrevs)
1691 ancestors.update(flparentlinkrevs)
1780
1692
1781 fncache.setdefault(rev, []).append(file_)
1693 fncache.setdefault(rev, []).append(file_)
1782 wanted.add(rev)
1694 wanted.add(rev)
1783 if copied:
1695 if copied:
1784 copies.append(copied)
1696 copies.append(copied)
1785
1697
1786 return wanted
1698 return wanted
1787
1699
1788 class _followfilter(object):
1700 class _followfilter(object):
1789 def __init__(self, repo, onlyfirst=False):
1701 def __init__(self, repo, onlyfirst=False):
1790 self.repo = repo
1702 self.repo = repo
1791 self.startrev = nullrev
1703 self.startrev = nullrev
1792 self.roots = set()
1704 self.roots = set()
1793 self.onlyfirst = onlyfirst
1705 self.onlyfirst = onlyfirst
1794
1706
1795 def match(self, rev):
1707 def match(self, rev):
1796 def realparents(rev):
1708 def realparents(rev):
1797 if self.onlyfirst:
1709 if self.onlyfirst:
1798 return self.repo.changelog.parentrevs(rev)[0:1]
1710 return self.repo.changelog.parentrevs(rev)[0:1]
1799 else:
1711 else:
1800 return filter(lambda x: x != nullrev,
1712 return filter(lambda x: x != nullrev,
1801 self.repo.changelog.parentrevs(rev))
1713 self.repo.changelog.parentrevs(rev))
1802
1714
1803 if self.startrev == nullrev:
1715 if self.startrev == nullrev:
1804 self.startrev = rev
1716 self.startrev = rev
1805 return True
1717 return True
1806
1718
1807 if rev > self.startrev:
1719 if rev > self.startrev:
1808 # forward: all descendants
1720 # forward: all descendants
1809 if not self.roots:
1721 if not self.roots:
1810 self.roots.add(self.startrev)
1722 self.roots.add(self.startrev)
1811 for parent in realparents(rev):
1723 for parent in realparents(rev):
1812 if parent in self.roots:
1724 if parent in self.roots:
1813 self.roots.add(rev)
1725 self.roots.add(rev)
1814 return True
1726 return True
1815 else:
1727 else:
1816 # backwards: all parents
1728 # backwards: all parents
1817 if not self.roots:
1729 if not self.roots:
1818 self.roots.update(realparents(self.startrev))
1730 self.roots.update(realparents(self.startrev))
1819 if rev in self.roots:
1731 if rev in self.roots:
1820 self.roots.remove(rev)
1732 self.roots.remove(rev)
1821 self.roots.update(realparents(rev))
1733 self.roots.update(realparents(rev))
1822 return True
1734 return True
1823
1735
1824 return False
1736 return False
1825
1737
1826 def walkchangerevs(repo, match, opts, prepare):
1738 def walkchangerevs(repo, match, opts, prepare):
1827 '''Iterate over files and the revs in which they changed.
1739 '''Iterate over files and the revs in which they changed.
1828
1740
1829 Callers most commonly need to iterate backwards over the history
1741 Callers most commonly need to iterate backwards over the history
1830 in which they are interested. Doing so has awful (quadratic-looking)
1742 in which they are interested. Doing so has awful (quadratic-looking)
1831 performance, so we use iterators in a "windowed" way.
1743 performance, so we use iterators in a "windowed" way.
1832
1744
1833 We walk a window of revisions in the desired order. Within the
1745 We walk a window of revisions in the desired order. Within the
1834 window, we first walk forwards to gather data, then in the desired
1746 window, we first walk forwards to gather data, then in the desired
1835 order (usually backwards) to display it.
1747 order (usually backwards) to display it.
1836
1748
1837 This function returns an iterator yielding contexts. Before
1749 This function returns an iterator yielding contexts. Before
1838 yielding each context, the iterator will first call the prepare
1750 yielding each context, the iterator will first call the prepare
1839 function on each context in the window in forward order.'''
1751 function on each context in the window in forward order.'''
1840
1752
1841 follow = opts.get('follow') or opts.get('follow_first')
1753 follow = opts.get('follow') or opts.get('follow_first')
1842 revs = _logrevs(repo, opts)
1754 revs = _logrevs(repo, opts)
1843 if not revs:
1755 if not revs:
1844 return []
1756 return []
1845 wanted = set()
1757 wanted = set()
1846 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1758 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1847 opts.get('removed'))
1759 opts.get('removed'))
1848 fncache = {}
1760 fncache = {}
1849 change = repo.changectx
1761 change = repo.changectx
1850
1762
1851 # First step is to fill wanted, the set of revisions that we want to yield.
1763 # First step is to fill wanted, the set of revisions that we want to yield.
1852 # When it does not induce extra cost, we also fill fncache for revisions in
1764 # When it does not induce extra cost, we also fill fncache for revisions in
1853 # wanted: a cache of filenames that were changed (ctx.files()) and that
1765 # wanted: a cache of filenames that were changed (ctx.files()) and that
1854 # match the file filtering conditions.
1766 # match the file filtering conditions.
1855
1767
1856 if match.always():
1768 if match.always():
1857 # No files, no patterns. Display all revs.
1769 # No files, no patterns. Display all revs.
1858 wanted = revs
1770 wanted = revs
1859 elif not slowpath:
1771 elif not slowpath:
1860 # We only have to read through the filelog to find wanted revisions
1772 # We only have to read through the filelog to find wanted revisions
1861
1773
1862 try:
1774 try:
1863 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1775 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1864 except FileWalkError:
1776 except FileWalkError:
1865 slowpath = True
1777 slowpath = True
1866
1778
1867 # We decided to fall back to the slowpath because at least one
1779 # We decided to fall back to the slowpath because at least one
1868 # of the paths was not a file. Check to see if at least one of them
1780 # of the paths was not a file. Check to see if at least one of them
1869 # existed in history, otherwise simply return
1781 # existed in history, otherwise simply return
1870 for path in match.files():
1782 for path in match.files():
1871 if path == '.' or path in repo.store:
1783 if path == '.' or path in repo.store:
1872 break
1784 break
1873 else:
1785 else:
1874 return []
1786 return []
1875
1787
1876 if slowpath:
1788 if slowpath:
1877 # We have to read the changelog to match filenames against
1789 # We have to read the changelog to match filenames against
1878 # changed files
1790 # changed files
1879
1791
1880 if follow:
1792 if follow:
1881 raise error.Abort(_('can only follow copies/renames for explicit '
1793 raise error.Abort(_('can only follow copies/renames for explicit '
1882 'filenames'))
1794 'filenames'))
1883
1795
1884 # The slow path checks files modified in every changeset.
1796 # The slow path checks files modified in every changeset.
1885 # This is really slow on large repos, so compute the set lazily.
1797 # This is really slow on large repos, so compute the set lazily.
1886 class lazywantedset(object):
1798 class lazywantedset(object):
1887 def __init__(self):
1799 def __init__(self):
1888 self.set = set()
1800 self.set = set()
1889 self.revs = set(revs)
1801 self.revs = set(revs)
1890
1802
1891 # No need to worry about locality here because it will be accessed
1803 # No need to worry about locality here because it will be accessed
1892 # in the same order as the increasing window below.
1804 # in the same order as the increasing window below.
1893 def __contains__(self, value):
1805 def __contains__(self, value):
1894 if value in self.set:
1806 if value in self.set:
1895 return True
1807 return True
1896 elif not value in self.revs:
1808 elif not value in self.revs:
1897 return False
1809 return False
1898 else:
1810 else:
1899 self.revs.discard(value)
1811 self.revs.discard(value)
1900 ctx = change(value)
1812 ctx = change(value)
1901 matches = filter(match, ctx.files())
1813 matches = filter(match, ctx.files())
1902 if matches:
1814 if matches:
1903 fncache[value] = matches
1815 fncache[value] = matches
1904 self.set.add(value)
1816 self.set.add(value)
1905 return True
1817 return True
1906 return False
1818 return False
1907
1819
1908 def discard(self, value):
1820 def discard(self, value):
1909 self.revs.discard(value)
1821 self.revs.discard(value)
1910 self.set.discard(value)
1822 self.set.discard(value)
1911
1823
1912 wanted = lazywantedset()
1824 wanted = lazywantedset()
1913
1825
1914 # it might be worthwhile to do this in the iterator if the rev range
1826 # it might be worthwhile to do this in the iterator if the rev range
1915 # is descending and the prune args are all within that range
1827 # is descending and the prune args are all within that range
1916 for rev in opts.get('prune', ()):
1828 for rev in opts.get('prune', ()):
1917 rev = repo[rev].rev()
1829 rev = repo[rev].rev()
1918 ff = _followfilter(repo)
1830 ff = _followfilter(repo)
1919 stop = min(revs[0], revs[-1])
1831 stop = min(revs[0], revs[-1])
1920 for x in xrange(rev, stop - 1, -1):
1832 for x in xrange(rev, stop - 1, -1):
1921 if ff.match(x):
1833 if ff.match(x):
1922 wanted = wanted - [x]
1834 wanted = wanted - [x]
1923
1835
1924 # Now that wanted is correctly initialized, we can iterate over the
1836 # Now that wanted is correctly initialized, we can iterate over the
1925 # revision range, yielding only revisions in wanted.
1837 # revision range, yielding only revisions in wanted.
1926 def iterate():
1838 def iterate():
1927 if follow and match.always():
1839 if follow and match.always():
1928 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1840 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1929 def want(rev):
1841 def want(rev):
1930 return ff.match(rev) and rev in wanted
1842 return ff.match(rev) and rev in wanted
1931 else:
1843 else:
1932 def want(rev):
1844 def want(rev):
1933 return rev in wanted
1845 return rev in wanted
1934
1846
1935 it = iter(revs)
1847 it = iter(revs)
1936 stopiteration = False
1848 stopiteration = False
1937 for windowsize in increasingwindows():
1849 for windowsize in increasingwindows():
1938 nrevs = []
1850 nrevs = []
1939 for i in xrange(windowsize):
1851 for i in xrange(windowsize):
1940 rev = next(it, None)
1852 rev = next(it, None)
1941 if rev is None:
1853 if rev is None:
1942 stopiteration = True
1854 stopiteration = True
1943 break
1855 break
1944 elif want(rev):
1856 elif want(rev):
1945 nrevs.append(rev)
1857 nrevs.append(rev)
1946 for rev in sorted(nrevs):
1858 for rev in sorted(nrevs):
1947 fns = fncache.get(rev)
1859 fns = fncache.get(rev)
1948 ctx = change(rev)
1860 ctx = change(rev)
1949 if not fns:
1861 if not fns:
1950 def fns_generator():
1862 def fns_generator():
1951 for f in ctx.files():
1863 for f in ctx.files():
1952 if match(f):
1864 if match(f):
1953 yield f
1865 yield f
1954 fns = fns_generator()
1866 fns = fns_generator()
1955 prepare(ctx, fns)
1867 prepare(ctx, fns)
1956 for rev in nrevs:
1868 for rev in nrevs:
1957 yield change(rev)
1869 yield change(rev)
1958
1870
1959 if stopiteration:
1871 if stopiteration:
1960 break
1872 break
1961
1873
1962 return iterate()
1874 return iterate()
1963
1875
1964 def _makefollowlogfilematcher(repo, files, followfirst):
1876 def _makefollowlogfilematcher(repo, files, followfirst):
1965 # When displaying a revision with --patch --follow FILE, we have
1877 # When displaying a revision with --patch --follow FILE, we have
1966 # to know which file of the revision must be diffed. With
1878 # to know which file of the revision must be diffed. With
1967 # --follow, we want the names of the ancestors of FILE in the
1879 # --follow, we want the names of the ancestors of FILE in the
1968 # revision, stored in "fcache". "fcache" is populated by
1880 # revision, stored in "fcache". "fcache" is populated by
1969 # reproducing the graph traversal already done by --follow revset
1881 # reproducing the graph traversal already done by --follow revset
1970 # and relating revs to file names (which is not "correct" but
1882 # and relating revs to file names (which is not "correct" but
1971 # good enough).
1883 # good enough).
1972 fcache = {}
1884 fcache = {}
1973 fcacheready = [False]
1885 fcacheready = [False]
1974 pctx = repo['.']
1886 pctx = repo['.']
1975
1887
1976 def populate():
1888 def populate():
1977 for fn in files:
1889 for fn in files:
1978 fctx = pctx[fn]
1890 fctx = pctx[fn]
1979 fcache.setdefault(fctx.introrev(), set()).add(fctx.path())
1891 fcache.setdefault(fctx.introrev(), set()).add(fctx.path())
1980 for c in fctx.ancestors(followfirst=followfirst):
1892 for c in fctx.ancestors(followfirst=followfirst):
1981 fcache.setdefault(c.rev(), set()).add(c.path())
1893 fcache.setdefault(c.rev(), set()).add(c.path())
1982
1894
1983 def filematcher(rev):
1895 def filematcher(rev):
1984 if not fcacheready[0]:
1896 if not fcacheready[0]:
1985 # Lazy initialization
1897 # Lazy initialization
1986 fcacheready[0] = True
1898 fcacheready[0] = True
1987 populate()
1899 populate()
1988 return scmutil.matchfiles(repo, fcache.get(rev, []))
1900 return scmutil.matchfiles(repo, fcache.get(rev, []))
1989
1901
1990 return filematcher
1902 return filematcher
1991
1903
1992 def _makenofollowlogfilematcher(repo, pats, opts):
1904 def _makenofollowlogfilematcher(repo, pats, opts):
1993 '''hook for extensions to override the filematcher for non-follow cases'''
1905 '''hook for extensions to override the filematcher for non-follow cases'''
1994 return None
1906 return None
1995
1907
1996 def _makelogrevset(repo, pats, opts, revs):
1908 def _makelogrevset(repo, pats, opts, revs):
1997 """Return (expr, filematcher) where expr is a revset string built
1909 """Return (expr, filematcher) where expr is a revset string built
1998 from log options and file patterns or None. If --stat or --patch
1910 from log options and file patterns or None. If --stat or --patch
1999 are not passed filematcher is None. Otherwise it is a callable
1911 are not passed filematcher is None. Otherwise it is a callable
2000 taking a revision number and returning a match objects filtering
1912 taking a revision number and returning a match objects filtering
2001 the files to be detailed when displaying the revision.
1913 the files to be detailed when displaying the revision.
2002 """
1914 """
2003 opt2revset = {
1915 opt2revset = {
2004 'no_merges': ('not merge()', None),
1916 'no_merges': ('not merge()', None),
2005 'only_merges': ('merge()', None),
1917 'only_merges': ('merge()', None),
2006 '_ancestors': ('ancestors(%(val)s)', None),
1918 '_ancestors': ('ancestors(%(val)s)', None),
2007 '_fancestors': ('_firstancestors(%(val)s)', None),
1919 '_fancestors': ('_firstancestors(%(val)s)', None),
2008 '_descendants': ('descendants(%(val)s)', None),
1920 '_descendants': ('descendants(%(val)s)', None),
2009 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1921 '_fdescendants': ('_firstdescendants(%(val)s)', None),
2010 '_matchfiles': ('_matchfiles(%(val)s)', None),
1922 '_matchfiles': ('_matchfiles(%(val)s)', None),
2011 'date': ('date(%(val)r)', None),
1923 'date': ('date(%(val)r)', None),
2012 'branch': ('branch(%(val)r)', ' or '),
1924 'branch': ('branch(%(val)r)', ' or '),
2013 '_patslog': ('filelog(%(val)r)', ' or '),
1925 '_patslog': ('filelog(%(val)r)', ' or '),
2014 '_patsfollow': ('follow(%(val)r)', ' or '),
1926 '_patsfollow': ('follow(%(val)r)', ' or '),
2015 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1927 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
2016 'keyword': ('keyword(%(val)r)', ' or '),
1928 'keyword': ('keyword(%(val)r)', ' or '),
2017 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1929 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
2018 'user': ('user(%(val)r)', ' or '),
1930 'user': ('user(%(val)r)', ' or '),
2019 }
1931 }
2020
1932
2021 opts = dict(opts)
1933 opts = dict(opts)
2022 # follow or not follow?
1934 # follow or not follow?
2023 follow = opts.get('follow') or opts.get('follow_first')
1935 follow = opts.get('follow') or opts.get('follow_first')
2024 if opts.get('follow_first'):
1936 if opts.get('follow_first'):
2025 followfirst = 1
1937 followfirst = 1
2026 else:
1938 else:
2027 followfirst = 0
1939 followfirst = 0
2028 # --follow with FILE behavior depends on revs...
1940 # --follow with FILE behavior depends on revs...
2029 it = iter(revs)
1941 it = iter(revs)
2030 startrev = next(it)
1942 startrev = next(it)
2031 followdescendants = startrev < next(it, startrev)
1943 followdescendants = startrev < next(it, startrev)
2032
1944
2033 # branch and only_branch are really aliases and must be handled at
1945 # branch and only_branch are really aliases and must be handled at
2034 # the same time
1946 # the same time
2035 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1947 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
2036 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1948 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
2037 # pats/include/exclude are passed to match.match() directly in
1949 # pats/include/exclude are passed to match.match() directly in
2038 # _matchfiles() revset but walkchangerevs() builds its matcher with
1950 # _matchfiles() revset but walkchangerevs() builds its matcher with
2039 # scmutil.match(). The difference is input pats are globbed on
1951 # scmutil.match(). The difference is input pats are globbed on
2040 # platforms without shell expansion (windows).
1952 # platforms without shell expansion (windows).
2041 wctx = repo[None]
1953 wctx = repo[None]
2042 match, pats = scmutil.matchandpats(wctx, pats, opts)
1954 match, pats = scmutil.matchandpats(wctx, pats, opts)
2043 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1955 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
2044 opts.get('removed'))
1956 opts.get('removed'))
2045 if not slowpath:
1957 if not slowpath:
2046 for f in match.files():
1958 for f in match.files():
2047 if follow and f not in wctx:
1959 if follow and f not in wctx:
2048 # If the file exists, it may be a directory, so let it
1960 # If the file exists, it may be a directory, so let it
2049 # take the slow path.
1961 # take the slow path.
2050 if os.path.exists(repo.wjoin(f)):
1962 if os.path.exists(repo.wjoin(f)):
2051 slowpath = True
1963 slowpath = True
2052 continue
1964 continue
2053 else:
1965 else:
2054 raise error.Abort(_('cannot follow file not in parent '
1966 raise error.Abort(_('cannot follow file not in parent '
2055 'revision: "%s"') % f)
1967 'revision: "%s"') % f)
2056 filelog = repo.file(f)
1968 filelog = repo.file(f)
2057 if not filelog:
1969 if not filelog:
2058 # A zero count may be a directory or deleted file, so
1970 # A zero count may be a directory or deleted file, so
2059 # try to find matching entries on the slow path.
1971 # try to find matching entries on the slow path.
2060 if follow:
1972 if follow:
2061 raise error.Abort(
1973 raise error.Abort(
2062 _('cannot follow nonexistent file: "%s"') % f)
1974 _('cannot follow nonexistent file: "%s"') % f)
2063 slowpath = True
1975 slowpath = True
2064
1976
2065 # We decided to fall back to the slowpath because at least one
1977 # We decided to fall back to the slowpath because at least one
2066 # of the paths was not a file. Check to see if at least one of them
1978 # of the paths was not a file. Check to see if at least one of them
2067 # existed in history - in that case, we'll continue down the
1979 # existed in history - in that case, we'll continue down the
2068 # slowpath; otherwise, we can turn off the slowpath
1980 # slowpath; otherwise, we can turn off the slowpath
2069 if slowpath:
1981 if slowpath:
2070 for path in match.files():
1982 for path in match.files():
2071 if path == '.' or path in repo.store:
1983 if path == '.' or path in repo.store:
2072 break
1984 break
2073 else:
1985 else:
2074 slowpath = False
1986 slowpath = False
2075
1987
2076 fpats = ('_patsfollow', '_patsfollowfirst')
1988 fpats = ('_patsfollow', '_patsfollowfirst')
2077 fnopats = (('_ancestors', '_fancestors'),
1989 fnopats = (('_ancestors', '_fancestors'),
2078 ('_descendants', '_fdescendants'))
1990 ('_descendants', '_fdescendants'))
2079 if slowpath:
1991 if slowpath:
2080 # See walkchangerevs() slow path.
1992 # See walkchangerevs() slow path.
2081 #
1993 #
2082 # pats/include/exclude cannot be represented as separate
1994 # pats/include/exclude cannot be represented as separate
2083 # revset expressions as their filtering logic applies at file
1995 # revset expressions as their filtering logic applies at file
2084 # level. For instance "-I a -X a" matches a revision touching
1996 # level. For instance "-I a -X a" matches a revision touching
2085 # "a" and "b" while "file(a) and not file(b)" does
1997 # "a" and "b" while "file(a) and not file(b)" does
2086 # not. Besides, filesets are evaluated against the working
1998 # not. Besides, filesets are evaluated against the working
2087 # directory.
1999 # directory.
2088 matchargs = ['r:', 'd:relpath']
2000 matchargs = ['r:', 'd:relpath']
2089 for p in pats:
2001 for p in pats:
2090 matchargs.append('p:' + p)
2002 matchargs.append('p:' + p)
2091 for p in opts.get('include', []):
2003 for p in opts.get('include', []):
2092 matchargs.append('i:' + p)
2004 matchargs.append('i:' + p)
2093 for p in opts.get('exclude', []):
2005 for p in opts.get('exclude', []):
2094 matchargs.append('x:' + p)
2006 matchargs.append('x:' + p)
2095 matchargs = ','.join(('%r' % p) for p in matchargs)
2007 matchargs = ','.join(('%r' % p) for p in matchargs)
2096 opts['_matchfiles'] = matchargs
2008 opts['_matchfiles'] = matchargs
2097 if follow:
2009 if follow:
2098 opts[fnopats[0][followfirst]] = '.'
2010 opts[fnopats[0][followfirst]] = '.'
2099 else:
2011 else:
2100 if follow:
2012 if follow:
2101 if pats:
2013 if pats:
2102 # follow() revset interprets its file argument as a
2014 # follow() revset interprets its file argument as a
2103 # manifest entry, so use match.files(), not pats.
2015 # manifest entry, so use match.files(), not pats.
2104 opts[fpats[followfirst]] = list(match.files())
2016 opts[fpats[followfirst]] = list(match.files())
2105 else:
2017 else:
2106 op = fnopats[followdescendants][followfirst]
2018 op = fnopats[followdescendants][followfirst]
2107 opts[op] = 'rev(%d)' % startrev
2019 opts[op] = 'rev(%d)' % startrev
2108 else:
2020 else:
2109 opts['_patslog'] = list(pats)
2021 opts['_patslog'] = list(pats)
2110
2022
2111 filematcher = None
2023 filematcher = None
2112 if opts.get('patch') or opts.get('stat'):
2024 if opts.get('patch') or opts.get('stat'):
2113 # When following files, track renames via a special matcher.
2025 # When following files, track renames via a special matcher.
2114 # If we're forced to take the slowpath it means we're following
2026 # If we're forced to take the slowpath it means we're following
2115 # at least one pattern/directory, so don't bother with rename tracking.
2027 # at least one pattern/directory, so don't bother with rename tracking.
2116 if follow and not match.always() and not slowpath:
2028 if follow and not match.always() and not slowpath:
2117 # _makefollowlogfilematcher expects its files argument to be
2029 # _makefollowlogfilematcher expects its files argument to be
2118 # relative to the repo root, so use match.files(), not pats.
2030 # relative to the repo root, so use match.files(), not pats.
2119 filematcher = _makefollowlogfilematcher(repo, match.files(),
2031 filematcher = _makefollowlogfilematcher(repo, match.files(),
2120 followfirst)
2032 followfirst)
2121 else:
2033 else:
2122 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2034 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2123 if filematcher is None:
2035 if filematcher is None:
2124 filematcher = lambda rev: match
2036 filematcher = lambda rev: match
2125
2037
2126 expr = []
2038 expr = []
2127 for op, val in sorted(opts.iteritems()):
2039 for op, val in sorted(opts.iteritems()):
2128 if not val:
2040 if not val:
2129 continue
2041 continue
2130 if op not in opt2revset:
2042 if op not in opt2revset:
2131 continue
2043 continue
2132 revop, andor = opt2revset[op]
2044 revop, andor = opt2revset[op]
2133 if '%(val)' not in revop:
2045 if '%(val)' not in revop:
2134 expr.append(revop)
2046 expr.append(revop)
2135 else:
2047 else:
2136 if not isinstance(val, list):
2048 if not isinstance(val, list):
2137 e = revop % {'val': val}
2049 e = revop % {'val': val}
2138 else:
2050 else:
2139 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2051 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2140 expr.append(e)
2052 expr.append(e)
2141
2053
2142 if expr:
2054 if expr:
2143 expr = '(' + ' and '.join(expr) + ')'
2055 expr = '(' + ' and '.join(expr) + ')'
2144 else:
2056 else:
2145 expr = None
2057 expr = None
2146 return expr, filematcher
2058 return expr, filematcher
2147
2059
2148 def _logrevs(repo, opts):
2060 def _logrevs(repo, opts):
2149 # Default --rev value depends on --follow but --follow behavior
2061 # Default --rev value depends on --follow but --follow behavior
2150 # depends on revisions resolved from --rev...
2062 # depends on revisions resolved from --rev...
2151 follow = opts.get('follow') or opts.get('follow_first')
2063 follow = opts.get('follow') or opts.get('follow_first')
2152 if opts.get('rev'):
2064 if opts.get('rev'):
2153 revs = scmutil.revrange(repo, opts['rev'])
2065 revs = scmutil.revrange(repo, opts['rev'])
2154 elif follow and repo.dirstate.p1() == nullid:
2066 elif follow and repo.dirstate.p1() == nullid:
2155 revs = revset.baseset()
2067 revs = revset.baseset()
2156 elif follow:
2068 elif follow:
2157 revs = repo.revs('reverse(:.)')
2069 revs = repo.revs('reverse(:.)')
2158 else:
2070 else:
2159 revs = revset.spanset(repo)
2071 revs = revset.spanset(repo)
2160 revs.reverse()
2072 revs.reverse()
2161 return revs
2073 return revs
2162
2074
2163 def getgraphlogrevs(repo, pats, opts):
2075 def getgraphlogrevs(repo, pats, opts):
2164 """Return (revs, expr, filematcher) where revs is an iterable of
2076 """Return (revs, expr, filematcher) where revs is an iterable of
2165 revision numbers, expr is a revset string built from log options
2077 revision numbers, expr is a revset string built from log options
2166 and file patterns or None, and used to filter 'revs'. If --stat or
2078 and file patterns or None, and used to filter 'revs'. If --stat or
2167 --patch are not passed filematcher is None. Otherwise it is a
2079 --patch are not passed filematcher is None. Otherwise it is a
2168 callable taking a revision number and returning a match objects
2080 callable taking a revision number and returning a match objects
2169 filtering the files to be detailed when displaying the revision.
2081 filtering the files to be detailed when displaying the revision.
2170 """
2082 """
2171 limit = loglimit(opts)
2083 limit = loglimit(opts)
2172 revs = _logrevs(repo, opts)
2084 revs = _logrevs(repo, opts)
2173 if not revs:
2085 if not revs:
2174 return revset.baseset(), None, None
2086 return revset.baseset(), None, None
2175 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2087 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2176 if opts.get('rev'):
2088 if opts.get('rev'):
2177 # User-specified revs might be unsorted, but don't sort before
2089 # User-specified revs might be unsorted, but don't sort before
2178 # _makelogrevset because it might depend on the order of revs
2090 # _makelogrevset because it might depend on the order of revs
2179 if not (revs.isdescending() or revs.istopo()):
2091 if not (revs.isdescending() or revs.istopo()):
2180 revs.sort(reverse=True)
2092 revs.sort(reverse=True)
2181 if expr:
2093 if expr:
2182 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2094 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2183 revs = matcher(repo, revs)
2095 revs = matcher(repo, revs)
2184 if limit is not None:
2096 if limit is not None:
2185 limitedrevs = []
2097 limitedrevs = []
2186 for idx, rev in enumerate(revs):
2098 for idx, rev in enumerate(revs):
2187 if idx >= limit:
2099 if idx >= limit:
2188 break
2100 break
2189 limitedrevs.append(rev)
2101 limitedrevs.append(rev)
2190 revs = revset.baseset(limitedrevs)
2102 revs = revset.baseset(limitedrevs)
2191
2103
2192 return revs, expr, filematcher
2104 return revs, expr, filematcher
2193
2105
2194 def getlogrevs(repo, pats, opts):
2106 def getlogrevs(repo, pats, opts):
2195 """Return (revs, expr, filematcher) where revs is an iterable of
2107 """Return (revs, expr, filematcher) where revs is an iterable of
2196 revision numbers, expr is a revset string built from log options
2108 revision numbers, expr is a revset string built from log options
2197 and file patterns or None, and used to filter 'revs'. If --stat or
2109 and file patterns or None, and used to filter 'revs'. If --stat or
2198 --patch are not passed filematcher is None. Otherwise it is a
2110 --patch are not passed filematcher is None. Otherwise it is a
2199 callable taking a revision number and returning a match objects
2111 callable taking a revision number and returning a match objects
2200 filtering the files to be detailed when displaying the revision.
2112 filtering the files to be detailed when displaying the revision.
2201 """
2113 """
2202 limit = loglimit(opts)
2114 limit = loglimit(opts)
2203 revs = _logrevs(repo, opts)
2115 revs = _logrevs(repo, opts)
2204 if not revs:
2116 if not revs:
2205 return revset.baseset([]), None, None
2117 return revset.baseset([]), None, None
2206 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2118 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2207 if expr:
2119 if expr:
2208 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2120 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2209 revs = matcher(repo, revs)
2121 revs = matcher(repo, revs)
2210 if limit is not None:
2122 if limit is not None:
2211 limitedrevs = []
2123 limitedrevs = []
2212 for idx, r in enumerate(revs):
2124 for idx, r in enumerate(revs):
2213 if limit <= idx:
2125 if limit <= idx:
2214 break
2126 break
2215 limitedrevs.append(r)
2127 limitedrevs.append(r)
2216 revs = revset.baseset(limitedrevs)
2128 revs = revset.baseset(limitedrevs)
2217
2129
2218 return revs, expr, filematcher
2130 return revs, expr, filematcher
2219
2131
2220 def _graphnodeformatter(ui, displayer):
2132 def _graphnodeformatter(ui, displayer):
2221 spec = ui.config('ui', 'graphnodetemplate')
2133 spec = ui.config('ui', 'graphnodetemplate')
2222 if not spec:
2134 if not spec:
2223 return templatekw.showgraphnode # fast path for "{graphnode}"
2135 return templatekw.showgraphnode # fast path for "{graphnode}"
2224
2136
2225 templ = formatter.gettemplater(ui, 'graphnode', spec)
2137 templ = formatter.gettemplater(ui, 'graphnode', spec)
2226 cache = {}
2138 cache = {}
2227 if isinstance(displayer, changeset_templater):
2139 if isinstance(displayer, changeset_templater):
2228 cache = displayer.cache # reuse cache of slow templates
2140 cache = displayer.cache # reuse cache of slow templates
2229 props = templatekw.keywords.copy()
2141 props = templatekw.keywords.copy()
2230 props['templ'] = templ
2142 props['templ'] = templ
2231 props['cache'] = cache
2143 props['cache'] = cache
2232 def formatnode(repo, ctx):
2144 def formatnode(repo, ctx):
2233 props['ctx'] = ctx
2145 props['ctx'] = ctx
2234 props['repo'] = repo
2146 props['repo'] = repo
2235 props['ui'] = repo.ui
2147 props['ui'] = repo.ui
2236 props['revcache'] = {}
2148 props['revcache'] = {}
2237 return templater.stringify(templ('graphnode', **props))
2149 return templater.stringify(templ('graphnode', **props))
2238 return formatnode
2150 return formatnode
2239
2151
2240 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2152 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2241 filematcher=None):
2153 filematcher=None):
2242 formatnode = _graphnodeformatter(ui, displayer)
2154 formatnode = _graphnodeformatter(ui, displayer)
2243 state = graphmod.asciistate()
2155 state = graphmod.asciistate()
2244 styles = state['styles']
2156 styles = state['styles']
2245
2157
2246 # only set graph styling if HGPLAIN is not set.
2158 # only set graph styling if HGPLAIN is not set.
2247 if ui.plain('graph'):
2159 if ui.plain('graph'):
2248 # set all edge styles to |, the default pre-3.8 behaviour
2160 # set all edge styles to |, the default pre-3.8 behaviour
2249 styles.update(dict.fromkeys(styles, '|'))
2161 styles.update(dict.fromkeys(styles, '|'))
2250 else:
2162 else:
2251 edgetypes = {
2163 edgetypes = {
2252 'parent': graphmod.PARENT,
2164 'parent': graphmod.PARENT,
2253 'grandparent': graphmod.GRANDPARENT,
2165 'grandparent': graphmod.GRANDPARENT,
2254 'missing': graphmod.MISSINGPARENT
2166 'missing': graphmod.MISSINGPARENT
2255 }
2167 }
2256 for name, key in edgetypes.items():
2168 for name, key in edgetypes.items():
2257 # experimental config: experimental.graphstyle.*
2169 # experimental config: experimental.graphstyle.*
2258 styles[key] = ui.config('experimental', 'graphstyle.%s' % name,
2170 styles[key] = ui.config('experimental', 'graphstyle.%s' % name,
2259 styles[key])
2171 styles[key])
2260 if not styles[key]:
2172 if not styles[key]:
2261 styles[key] = None
2173 styles[key] = None
2262
2174
2263 # experimental config: experimental.graphshorten
2175 # experimental config: experimental.graphshorten
2264 state['graphshorten'] = ui.configbool('experimental', 'graphshorten')
2176 state['graphshorten'] = ui.configbool('experimental', 'graphshorten')
2265
2177
2266 for rev, type, ctx, parents in dag:
2178 for rev, type, ctx, parents in dag:
2267 char = formatnode(repo, ctx)
2179 char = formatnode(repo, ctx)
2268 copies = None
2180 copies = None
2269 if getrenamed and ctx.rev():
2181 if getrenamed and ctx.rev():
2270 copies = []
2182 copies = []
2271 for fn in ctx.files():
2183 for fn in ctx.files():
2272 rename = getrenamed(fn, ctx.rev())
2184 rename = getrenamed(fn, ctx.rev())
2273 if rename:
2185 if rename:
2274 copies.append((fn, rename[0]))
2186 copies.append((fn, rename[0]))
2275 revmatchfn = None
2187 revmatchfn = None
2276 if filematcher is not None:
2188 if filematcher is not None:
2277 revmatchfn = filematcher(ctx.rev())
2189 revmatchfn = filematcher(ctx.rev())
2278 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2190 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2279 lines = displayer.hunk.pop(rev).split('\n')
2191 lines = displayer.hunk.pop(rev).split('\n')
2280 if not lines[-1]:
2192 if not lines[-1]:
2281 del lines[-1]
2193 del lines[-1]
2282 displayer.flush(ctx)
2194 displayer.flush(ctx)
2283 edges = edgefn(type, char, lines, state, rev, parents)
2195 edges = edgefn(type, char, lines, state, rev, parents)
2284 for type, char, lines, coldata in edges:
2196 for type, char, lines, coldata in edges:
2285 graphmod.ascii(ui, state, type, char, lines, coldata)
2197 graphmod.ascii(ui, state, type, char, lines, coldata)
2286 displayer.close()
2198 displayer.close()
2287
2199
2288 def graphlog(ui, repo, *pats, **opts):
2200 def graphlog(ui, repo, *pats, **opts):
2289 # Parameters are identical to log command ones
2201 # Parameters are identical to log command ones
2290 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2202 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2291 revdag = graphmod.dagwalker(repo, revs)
2203 revdag = graphmod.dagwalker(repo, revs)
2292
2204
2293 getrenamed = None
2205 getrenamed = None
2294 if opts.get('copies'):
2206 if opts.get('copies'):
2295 endrev = None
2207 endrev = None
2296 if opts.get('rev'):
2208 if opts.get('rev'):
2297 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2209 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2298 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2210 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2299 displayer = show_changeset(ui, repo, opts, buffered=True)
2211 displayer = show_changeset(ui, repo, opts, buffered=True)
2300 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2212 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2301 filematcher)
2213 filematcher)
2302
2214
2303 def checkunsupportedgraphflags(pats, opts):
2215 def checkunsupportedgraphflags(pats, opts):
2304 for op in ["newest_first"]:
2216 for op in ["newest_first"]:
2305 if op in opts and opts[op]:
2217 if op in opts and opts[op]:
2306 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2218 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2307 % op.replace("_", "-"))
2219 % op.replace("_", "-"))
2308
2220
2309 def graphrevs(repo, nodes, opts):
2221 def graphrevs(repo, nodes, opts):
2310 limit = loglimit(opts)
2222 limit = loglimit(opts)
2311 nodes.reverse()
2223 nodes.reverse()
2312 if limit is not None:
2224 if limit is not None:
2313 nodes = nodes[:limit]
2225 nodes = nodes[:limit]
2314 return graphmod.nodes(repo, nodes)
2226 return graphmod.nodes(repo, nodes)
2315
2227
2316 def add(ui, repo, match, prefix, explicitonly, **opts):
2228 def add(ui, repo, match, prefix, explicitonly, **opts):
2317 join = lambda f: os.path.join(prefix, f)
2229 join = lambda f: os.path.join(prefix, f)
2318 bad = []
2230 bad = []
2319
2231
2320 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2232 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2321 names = []
2233 names = []
2322 wctx = repo[None]
2234 wctx = repo[None]
2323 cca = None
2235 cca = None
2324 abort, warn = scmutil.checkportabilityalert(ui)
2236 abort, warn = scmutil.checkportabilityalert(ui)
2325 if abort or warn:
2237 if abort or warn:
2326 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2238 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2327
2239
2328 badmatch = matchmod.badmatch(match, badfn)
2240 badmatch = matchmod.badmatch(match, badfn)
2329 dirstate = repo.dirstate
2241 dirstate = repo.dirstate
2330 # We don't want to just call wctx.walk here, since it would return a lot of
2242 # We don't want to just call wctx.walk here, since it would return a lot of
2331 # clean files, which we aren't interested in and takes time.
2243 # clean files, which we aren't interested in and takes time.
2332 for f in sorted(dirstate.walk(badmatch, sorted(wctx.substate),
2244 for f in sorted(dirstate.walk(badmatch, sorted(wctx.substate),
2333 True, False, full=False)):
2245 True, False, full=False)):
2334 exact = match.exact(f)
2246 exact = match.exact(f)
2335 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2247 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2336 if cca:
2248 if cca:
2337 cca(f)
2249 cca(f)
2338 names.append(f)
2250 names.append(f)
2339 if ui.verbose or not exact:
2251 if ui.verbose or not exact:
2340 ui.status(_('adding %s\n') % match.rel(f))
2252 ui.status(_('adding %s\n') % match.rel(f))
2341
2253
2342 for subpath in sorted(wctx.substate):
2254 for subpath in sorted(wctx.substate):
2343 sub = wctx.sub(subpath)
2255 sub = wctx.sub(subpath)
2344 try:
2256 try:
2345 submatch = matchmod.subdirmatcher(subpath, match)
2257 submatch = matchmod.subdirmatcher(subpath, match)
2346 if opts.get('subrepos'):
2258 if opts.get('subrepos'):
2347 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2259 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2348 else:
2260 else:
2349 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2261 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2350 except error.LookupError:
2262 except error.LookupError:
2351 ui.status(_("skipping missing subrepository: %s\n")
2263 ui.status(_("skipping missing subrepository: %s\n")
2352 % join(subpath))
2264 % join(subpath))
2353
2265
2354 if not opts.get('dry_run'):
2266 if not opts.get('dry_run'):
2355 rejected = wctx.add(names, prefix)
2267 rejected = wctx.add(names, prefix)
2356 bad.extend(f for f in rejected if f in match.files())
2268 bad.extend(f for f in rejected if f in match.files())
2357 return bad
2269 return bad
2358
2270
2359 def forget(ui, repo, match, prefix, explicitonly):
2271 def forget(ui, repo, match, prefix, explicitonly):
2360 join = lambda f: os.path.join(prefix, f)
2272 join = lambda f: os.path.join(prefix, f)
2361 bad = []
2273 bad = []
2362 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2274 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2363 wctx = repo[None]
2275 wctx = repo[None]
2364 forgot = []
2276 forgot = []
2365
2277
2366 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2278 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2367 forget = sorted(s[0] + s[1] + s[3] + s[6])
2279 forget = sorted(s[0] + s[1] + s[3] + s[6])
2368 if explicitonly:
2280 if explicitonly:
2369 forget = [f for f in forget if match.exact(f)]
2281 forget = [f for f in forget if match.exact(f)]
2370
2282
2371 for subpath in sorted(wctx.substate):
2283 for subpath in sorted(wctx.substate):
2372 sub = wctx.sub(subpath)
2284 sub = wctx.sub(subpath)
2373 try:
2285 try:
2374 submatch = matchmod.subdirmatcher(subpath, match)
2286 submatch = matchmod.subdirmatcher(subpath, match)
2375 subbad, subforgot = sub.forget(submatch, prefix)
2287 subbad, subforgot = sub.forget(submatch, prefix)
2376 bad.extend([subpath + '/' + f for f in subbad])
2288 bad.extend([subpath + '/' + f for f in subbad])
2377 forgot.extend([subpath + '/' + f for f in subforgot])
2289 forgot.extend([subpath + '/' + f for f in subforgot])
2378 except error.LookupError:
2290 except error.LookupError:
2379 ui.status(_("skipping missing subrepository: %s\n")
2291 ui.status(_("skipping missing subrepository: %s\n")
2380 % join(subpath))
2292 % join(subpath))
2381
2293
2382 if not explicitonly:
2294 if not explicitonly:
2383 for f in match.files():
2295 for f in match.files():
2384 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2296 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2385 if f not in forgot:
2297 if f not in forgot:
2386 if repo.wvfs.exists(f):
2298 if repo.wvfs.exists(f):
2387 # Don't complain if the exact case match wasn't given.
2299 # Don't complain if the exact case match wasn't given.
2388 # But don't do this until after checking 'forgot', so
2300 # But don't do this until after checking 'forgot', so
2389 # that subrepo files aren't normalized, and this op is
2301 # that subrepo files aren't normalized, and this op is
2390 # purely from data cached by the status walk above.
2302 # purely from data cached by the status walk above.
2391 if repo.dirstate.normalize(f) in repo.dirstate:
2303 if repo.dirstate.normalize(f) in repo.dirstate:
2392 continue
2304 continue
2393 ui.warn(_('not removing %s: '
2305 ui.warn(_('not removing %s: '
2394 'file is already untracked\n')
2306 'file is already untracked\n')
2395 % match.rel(f))
2307 % match.rel(f))
2396 bad.append(f)
2308 bad.append(f)
2397
2309
2398 for f in forget:
2310 for f in forget:
2399 if ui.verbose or not match.exact(f):
2311 if ui.verbose or not match.exact(f):
2400 ui.status(_('removing %s\n') % match.rel(f))
2312 ui.status(_('removing %s\n') % match.rel(f))
2401
2313
2402 rejected = wctx.forget(forget, prefix)
2314 rejected = wctx.forget(forget, prefix)
2403 bad.extend(f for f in rejected if f in match.files())
2315 bad.extend(f for f in rejected if f in match.files())
2404 forgot.extend(f for f in forget if f not in rejected)
2316 forgot.extend(f for f in forget if f not in rejected)
2405 return bad, forgot
2317 return bad, forgot
2406
2318
2407 def files(ui, ctx, m, fm, fmt, subrepos):
2319 def files(ui, ctx, m, fm, fmt, subrepos):
2408 rev = ctx.rev()
2320 rev = ctx.rev()
2409 ret = 1
2321 ret = 1
2410 ds = ctx.repo().dirstate
2322 ds = ctx.repo().dirstate
2411
2323
2412 for f in ctx.matches(m):
2324 for f in ctx.matches(m):
2413 if rev is None and ds[f] == 'r':
2325 if rev is None and ds[f] == 'r':
2414 continue
2326 continue
2415 fm.startitem()
2327 fm.startitem()
2416 if ui.verbose:
2328 if ui.verbose:
2417 fc = ctx[f]
2329 fc = ctx[f]
2418 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2330 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2419 fm.data(abspath=f)
2331 fm.data(abspath=f)
2420 fm.write('path', fmt, m.rel(f))
2332 fm.write('path', fmt, m.rel(f))
2421 ret = 0
2333 ret = 0
2422
2334
2423 for subpath in sorted(ctx.substate):
2335 for subpath in sorted(ctx.substate):
2424 submatch = matchmod.subdirmatcher(subpath, m)
2336 submatch = matchmod.subdirmatcher(subpath, m)
2425 if (subrepos or m.exact(subpath) or any(submatch.files())):
2337 if (subrepos or m.exact(subpath) or any(submatch.files())):
2426 sub = ctx.sub(subpath)
2338 sub = ctx.sub(subpath)
2427 try:
2339 try:
2428 recurse = m.exact(subpath) or subrepos
2340 recurse = m.exact(subpath) or subrepos
2429 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2341 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2430 ret = 0
2342 ret = 0
2431 except error.LookupError:
2343 except error.LookupError:
2432 ui.status(_("skipping missing subrepository: %s\n")
2344 ui.status(_("skipping missing subrepository: %s\n")
2433 % m.abs(subpath))
2345 % m.abs(subpath))
2434
2346
2435 return ret
2347 return ret
2436
2348
2437 def remove(ui, repo, m, prefix, after, force, subrepos, warnings=None):
2349 def remove(ui, repo, m, prefix, after, force, subrepos, warnings=None):
2438 join = lambda f: os.path.join(prefix, f)
2350 join = lambda f: os.path.join(prefix, f)
2439 ret = 0
2351 ret = 0
2440 s = repo.status(match=m, clean=True)
2352 s = repo.status(match=m, clean=True)
2441 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2353 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2442
2354
2443 wctx = repo[None]
2355 wctx = repo[None]
2444
2356
2445 if warnings is None:
2357 if warnings is None:
2446 warnings = []
2358 warnings = []
2447 warn = True
2359 warn = True
2448 else:
2360 else:
2449 warn = False
2361 warn = False
2450
2362
2451 subs = sorted(wctx.substate)
2363 subs = sorted(wctx.substate)
2452 total = len(subs)
2364 total = len(subs)
2453 count = 0
2365 count = 0
2454 for subpath in subs:
2366 for subpath in subs:
2455 count += 1
2367 count += 1
2456 submatch = matchmod.subdirmatcher(subpath, m)
2368 submatch = matchmod.subdirmatcher(subpath, m)
2457 if subrepos or m.exact(subpath) or any(submatch.files()):
2369 if subrepos or m.exact(subpath) or any(submatch.files()):
2458 ui.progress(_('searching'), count, total=total, unit=_('subrepos'))
2370 ui.progress(_('searching'), count, total=total, unit=_('subrepos'))
2459 sub = wctx.sub(subpath)
2371 sub = wctx.sub(subpath)
2460 try:
2372 try:
2461 if sub.removefiles(submatch, prefix, after, force, subrepos,
2373 if sub.removefiles(submatch, prefix, after, force, subrepos,
2462 warnings):
2374 warnings):
2463 ret = 1
2375 ret = 1
2464 except error.LookupError:
2376 except error.LookupError:
2465 warnings.append(_("skipping missing subrepository: %s\n")
2377 warnings.append(_("skipping missing subrepository: %s\n")
2466 % join(subpath))
2378 % join(subpath))
2467 ui.progress(_('searching'), None)
2379 ui.progress(_('searching'), None)
2468
2380
2469 # warn about failure to delete explicit files/dirs
2381 # warn about failure to delete explicit files/dirs
2470 deleteddirs = util.dirs(deleted)
2382 deleteddirs = util.dirs(deleted)
2471 files = m.files()
2383 files = m.files()
2472 total = len(files)
2384 total = len(files)
2473 count = 0
2385 count = 0
2474 for f in files:
2386 for f in files:
2475 def insubrepo():
2387 def insubrepo():
2476 for subpath in wctx.substate:
2388 for subpath in wctx.substate:
2477 if f.startswith(subpath + '/'):
2389 if f.startswith(subpath + '/'):
2478 return True
2390 return True
2479 return False
2391 return False
2480
2392
2481 count += 1
2393 count += 1
2482 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2394 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2483 isdir = f in deleteddirs or wctx.hasdir(f)
2395 isdir = f in deleteddirs or wctx.hasdir(f)
2484 if (f in repo.dirstate or isdir or f == '.'
2396 if (f in repo.dirstate or isdir or f == '.'
2485 or insubrepo() or f in subs):
2397 or insubrepo() or f in subs):
2486 continue
2398 continue
2487
2399
2488 if repo.wvfs.exists(f):
2400 if repo.wvfs.exists(f):
2489 if repo.wvfs.isdir(f):
2401 if repo.wvfs.isdir(f):
2490 warnings.append(_('not removing %s: no tracked files\n')
2402 warnings.append(_('not removing %s: no tracked files\n')
2491 % m.rel(f))
2403 % m.rel(f))
2492 else:
2404 else:
2493 warnings.append(_('not removing %s: file is untracked\n')
2405 warnings.append(_('not removing %s: file is untracked\n')
2494 % m.rel(f))
2406 % m.rel(f))
2495 # missing files will generate a warning elsewhere
2407 # missing files will generate a warning elsewhere
2496 ret = 1
2408 ret = 1
2497 ui.progress(_('deleting'), None)
2409 ui.progress(_('deleting'), None)
2498
2410
2499 if force:
2411 if force:
2500 list = modified + deleted + clean + added
2412 list = modified + deleted + clean + added
2501 elif after:
2413 elif after:
2502 list = deleted
2414 list = deleted
2503 remaining = modified + added + clean
2415 remaining = modified + added + clean
2504 total = len(remaining)
2416 total = len(remaining)
2505 count = 0
2417 count = 0
2506 for f in remaining:
2418 for f in remaining:
2507 count += 1
2419 count += 1
2508 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2420 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2509 warnings.append(_('not removing %s: file still exists\n')
2421 warnings.append(_('not removing %s: file still exists\n')
2510 % m.rel(f))
2422 % m.rel(f))
2511 ret = 1
2423 ret = 1
2512 ui.progress(_('skipping'), None)
2424 ui.progress(_('skipping'), None)
2513 else:
2425 else:
2514 list = deleted + clean
2426 list = deleted + clean
2515 total = len(modified) + len(added)
2427 total = len(modified) + len(added)
2516 count = 0
2428 count = 0
2517 for f in modified:
2429 for f in modified:
2518 count += 1
2430 count += 1
2519 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2431 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2520 warnings.append(_('not removing %s: file is modified (use -f'
2432 warnings.append(_('not removing %s: file is modified (use -f'
2521 ' to force removal)\n') % m.rel(f))
2433 ' to force removal)\n') % m.rel(f))
2522 ret = 1
2434 ret = 1
2523 for f in added:
2435 for f in added:
2524 count += 1
2436 count += 1
2525 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2437 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2526 warnings.append(_("not removing %s: file has been marked for add"
2438 warnings.append(_("not removing %s: file has been marked for add"
2527 " (use 'hg forget' to undo add)\n") % m.rel(f))
2439 " (use 'hg forget' to undo add)\n") % m.rel(f))
2528 ret = 1
2440 ret = 1
2529 ui.progress(_('skipping'), None)
2441 ui.progress(_('skipping'), None)
2530
2442
2531 list = sorted(list)
2443 list = sorted(list)
2532 total = len(list)
2444 total = len(list)
2533 count = 0
2445 count = 0
2534 for f in list:
2446 for f in list:
2535 count += 1
2447 count += 1
2536 if ui.verbose or not m.exact(f):
2448 if ui.verbose or not m.exact(f):
2537 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2449 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2538 ui.status(_('removing %s\n') % m.rel(f))
2450 ui.status(_('removing %s\n') % m.rel(f))
2539 ui.progress(_('deleting'), None)
2451 ui.progress(_('deleting'), None)
2540
2452
2541 with repo.wlock():
2453 with repo.wlock():
2542 if not after:
2454 if not after:
2543 for f in list:
2455 for f in list:
2544 if f in added:
2456 if f in added:
2545 continue # we never unlink added files on remove
2457 continue # we never unlink added files on remove
2546 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
2458 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
2547 repo[None].forget(list)
2459 repo[None].forget(list)
2548
2460
2549 if warn:
2461 if warn:
2550 for warning in warnings:
2462 for warning in warnings:
2551 ui.warn(warning)
2463 ui.warn(warning)
2552
2464
2553 return ret
2465 return ret
2554
2466
2555 def cat(ui, repo, ctx, matcher, prefix, **opts):
2467 def cat(ui, repo, ctx, matcher, prefix, **opts):
2556 err = 1
2468 err = 1
2557
2469
2558 def write(path):
2470 def write(path):
2559 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2471 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2560 pathname=os.path.join(prefix, path))
2472 pathname=os.path.join(prefix, path))
2561 data = ctx[path].data()
2473 data = ctx[path].data()
2562 if opts.get('decode'):
2474 if opts.get('decode'):
2563 data = repo.wwritedata(path, data)
2475 data = repo.wwritedata(path, data)
2564 fp.write(data)
2476 fp.write(data)
2565 fp.close()
2477 fp.close()
2566
2478
2567 # Automation often uses hg cat on single files, so special case it
2479 # Automation often uses hg cat on single files, so special case it
2568 # for performance to avoid the cost of parsing the manifest.
2480 # for performance to avoid the cost of parsing the manifest.
2569 if len(matcher.files()) == 1 and not matcher.anypats():
2481 if len(matcher.files()) == 1 and not matcher.anypats():
2570 file = matcher.files()[0]
2482 file = matcher.files()[0]
2571 mfl = repo.manifestlog
2483 mfl = repo.manifestlog
2572 mfnode = ctx.manifestnode()
2484 mfnode = ctx.manifestnode()
2573 try:
2485 try:
2574 if mfnode and mfl[mfnode].find(file)[0]:
2486 if mfnode and mfl[mfnode].find(file)[0]:
2575 write(file)
2487 write(file)
2576 return 0
2488 return 0
2577 except KeyError:
2489 except KeyError:
2578 pass
2490 pass
2579
2491
2580 for abs in ctx.walk(matcher):
2492 for abs in ctx.walk(matcher):
2581 write(abs)
2493 write(abs)
2582 err = 0
2494 err = 0
2583
2495
2584 for subpath in sorted(ctx.substate):
2496 for subpath in sorted(ctx.substate):
2585 sub = ctx.sub(subpath)
2497 sub = ctx.sub(subpath)
2586 try:
2498 try:
2587 submatch = matchmod.subdirmatcher(subpath, matcher)
2499 submatch = matchmod.subdirmatcher(subpath, matcher)
2588
2500
2589 if not sub.cat(submatch, os.path.join(prefix, sub._path),
2501 if not sub.cat(submatch, os.path.join(prefix, sub._path),
2590 **opts):
2502 **opts):
2591 err = 0
2503 err = 0
2592 except error.RepoLookupError:
2504 except error.RepoLookupError:
2593 ui.status(_("skipping missing subrepository: %s\n")
2505 ui.status(_("skipping missing subrepository: %s\n")
2594 % os.path.join(prefix, subpath))
2506 % os.path.join(prefix, subpath))
2595
2507
2596 return err
2508 return err
2597
2509
2598 def commit(ui, repo, commitfunc, pats, opts):
2510 def commit(ui, repo, commitfunc, pats, opts):
2599 '''commit the specified files or all outstanding changes'''
2511 '''commit the specified files or all outstanding changes'''
2600 date = opts.get('date')
2512 date = opts.get('date')
2601 if date:
2513 if date:
2602 opts['date'] = util.parsedate(date)
2514 opts['date'] = util.parsedate(date)
2603 message = logmessage(ui, opts)
2515 message = logmessage(ui, opts)
2604 matcher = scmutil.match(repo[None], pats, opts)
2516 matcher = scmutil.match(repo[None], pats, opts)
2605
2517
2606 # extract addremove carefully -- this function can be called from a command
2518 # extract addremove carefully -- this function can be called from a command
2607 # that doesn't support addremove
2519 # that doesn't support addremove
2608 if opts.get('addremove'):
2520 if opts.get('addremove'):
2609 if scmutil.addremove(repo, matcher, "", opts) != 0:
2521 if scmutil.addremove(repo, matcher, "", opts) != 0:
2610 raise error.Abort(
2522 raise error.Abort(
2611 _("failed to mark all new/missing files as added/removed"))
2523 _("failed to mark all new/missing files as added/removed"))
2612
2524
2613 return commitfunc(ui, repo, message, matcher, opts)
2525 return commitfunc(ui, repo, message, matcher, opts)
2614
2526
2615 def samefile(f, ctx1, ctx2):
2527 def samefile(f, ctx1, ctx2):
2616 if f in ctx1.manifest():
2528 if f in ctx1.manifest():
2617 a = ctx1.filectx(f)
2529 a = ctx1.filectx(f)
2618 if f in ctx2.manifest():
2530 if f in ctx2.manifest():
2619 b = ctx2.filectx(f)
2531 b = ctx2.filectx(f)
2620 return (not a.cmp(b)
2532 return (not a.cmp(b)
2621 and a.flags() == b.flags())
2533 and a.flags() == b.flags())
2622 else:
2534 else:
2623 return False
2535 return False
2624 else:
2536 else:
2625 return f not in ctx2.manifest()
2537 return f not in ctx2.manifest()
2626
2538
2627 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2539 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2628 # avoid cycle context -> subrepo -> cmdutil
2540 # avoid cycle context -> subrepo -> cmdutil
2629 from . import context
2541 from . import context
2630
2542
2631 # amend will reuse the existing user if not specified, but the obsolete
2543 # amend will reuse the existing user if not specified, but the obsolete
2632 # marker creation requires that the current user's name is specified.
2544 # marker creation requires that the current user's name is specified.
2633 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2545 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2634 ui.username() # raise exception if username not set
2546 ui.username() # raise exception if username not set
2635
2547
2636 ui.note(_('amending changeset %s\n') % old)
2548 ui.note(_('amending changeset %s\n') % old)
2637 base = old.p1()
2549 base = old.p1()
2638 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2550 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2639
2551
2640 wlock = lock = newid = None
2552 wlock = lock = newid = None
2641 try:
2553 try:
2642 wlock = repo.wlock()
2554 wlock = repo.wlock()
2643 lock = repo.lock()
2555 lock = repo.lock()
2644 with repo.transaction('amend') as tr:
2556 with repo.transaction('amend') as tr:
2645 # See if we got a message from -m or -l, if not, open the editor
2557 # See if we got a message from -m or -l, if not, open the editor
2646 # with the message of the changeset to amend
2558 # with the message of the changeset to amend
2647 message = logmessage(ui, opts)
2559 message = logmessage(ui, opts)
2648 # ensure logfile does not conflict with later enforcement of the
2560 # ensure logfile does not conflict with later enforcement of the
2649 # message. potential logfile content has been processed by
2561 # message. potential logfile content has been processed by
2650 # `logmessage` anyway.
2562 # `logmessage` anyway.
2651 opts.pop('logfile')
2563 opts.pop('logfile')
2652 # First, do a regular commit to record all changes in the working
2564 # First, do a regular commit to record all changes in the working
2653 # directory (if there are any)
2565 # directory (if there are any)
2654 ui.callhooks = False
2566 ui.callhooks = False
2655 activebookmark = repo._bookmarks.active
2567 activebookmark = repo._bookmarks.active
2656 try:
2568 try:
2657 repo._bookmarks.active = None
2569 repo._bookmarks.active = None
2658 opts['message'] = 'temporary amend commit for %s' % old
2570 opts['message'] = 'temporary amend commit for %s' % old
2659 node = commit(ui, repo, commitfunc, pats, opts)
2571 node = commit(ui, repo, commitfunc, pats, opts)
2660 finally:
2572 finally:
2661 repo._bookmarks.active = activebookmark
2573 repo._bookmarks.active = activebookmark
2662 repo._bookmarks.recordchange(tr)
2574 repo._bookmarks.recordchange(tr)
2663 ui.callhooks = True
2575 ui.callhooks = True
2664 ctx = repo[node]
2576 ctx = repo[node]
2665
2577
2666 # Participating changesets:
2578 # Participating changesets:
2667 #
2579 #
2668 # node/ctx o - new (intermediate) commit that contains changes
2580 # node/ctx o - new (intermediate) commit that contains changes
2669 # | from working dir to go into amending commit
2581 # | from working dir to go into amending commit
2670 # | (or a workingctx if there were no changes)
2582 # | (or a workingctx if there were no changes)
2671 # |
2583 # |
2672 # old o - changeset to amend
2584 # old o - changeset to amend
2673 # |
2585 # |
2674 # base o - parent of amending changeset
2586 # base o - parent of amending changeset
2675
2587
2676 # Update extra dict from amended commit (e.g. to preserve graft
2588 # Update extra dict from amended commit (e.g. to preserve graft
2677 # source)
2589 # source)
2678 extra.update(old.extra())
2590 extra.update(old.extra())
2679
2591
2680 # Also update it from the intermediate commit or from the wctx
2592 # Also update it from the intermediate commit or from the wctx
2681 extra.update(ctx.extra())
2593 extra.update(ctx.extra())
2682
2594
2683 if len(old.parents()) > 1:
2595 if len(old.parents()) > 1:
2684 # ctx.files() isn't reliable for merges, so fall back to the
2596 # ctx.files() isn't reliable for merges, so fall back to the
2685 # slower repo.status() method
2597 # slower repo.status() method
2686 files = set([fn for st in repo.status(base, old)[:3]
2598 files = set([fn for st in repo.status(base, old)[:3]
2687 for fn in st])
2599 for fn in st])
2688 else:
2600 else:
2689 files = set(old.files())
2601 files = set(old.files())
2690
2602
2691 # Second, we use either the commit we just did, or if there were no
2603 # Second, we use either the commit we just did, or if there were no
2692 # changes the parent of the working directory as the version of the
2604 # changes the parent of the working directory as the version of the
2693 # files in the final amend commit
2605 # files in the final amend commit
2694 if node:
2606 if node:
2695 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2607 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2696
2608
2697 user = ctx.user()
2609 user = ctx.user()
2698 date = ctx.date()
2610 date = ctx.date()
2699 # Recompute copies (avoid recording a -> b -> a)
2611 # Recompute copies (avoid recording a -> b -> a)
2700 copied = copies.pathcopies(base, ctx)
2612 copied = copies.pathcopies(base, ctx)
2701 if old.p2:
2613 if old.p2:
2702 copied.update(copies.pathcopies(old.p2(), ctx))
2614 copied.update(copies.pathcopies(old.p2(), ctx))
2703
2615
2704 # Prune files which were reverted by the updates: if old
2616 # Prune files which were reverted by the updates: if old
2705 # introduced file X and our intermediate commit, node,
2617 # introduced file X and our intermediate commit, node,
2706 # renamed that file, then those two files are the same and
2618 # renamed that file, then those two files are the same and
2707 # we can discard X from our list of files. Likewise if X
2619 # we can discard X from our list of files. Likewise if X
2708 # was deleted, it's no longer relevant
2620 # was deleted, it's no longer relevant
2709 files.update(ctx.files())
2621 files.update(ctx.files())
2710 files = [f for f in files if not samefile(f, ctx, base)]
2622 files = [f for f in files if not samefile(f, ctx, base)]
2711
2623
2712 def filectxfn(repo, ctx_, path):
2624 def filectxfn(repo, ctx_, path):
2713 try:
2625 try:
2714 fctx = ctx[path]
2626 fctx = ctx[path]
2715 flags = fctx.flags()
2627 flags = fctx.flags()
2716 mctx = context.memfilectx(repo,
2628 mctx = context.memfilectx(repo,
2717 fctx.path(), fctx.data(),
2629 fctx.path(), fctx.data(),
2718 islink='l' in flags,
2630 islink='l' in flags,
2719 isexec='x' in flags,
2631 isexec='x' in flags,
2720 copied=copied.get(path))
2632 copied=copied.get(path))
2721 return mctx
2633 return mctx
2722 except KeyError:
2634 except KeyError:
2723 return None
2635 return None
2724 else:
2636 else:
2725 ui.note(_('copying changeset %s to %s\n') % (old, base))
2637 ui.note(_('copying changeset %s to %s\n') % (old, base))
2726
2638
2727 # Use version of files as in the old cset
2639 # Use version of files as in the old cset
2728 def filectxfn(repo, ctx_, path):
2640 def filectxfn(repo, ctx_, path):
2729 try:
2641 try:
2730 return old.filectx(path)
2642 return old.filectx(path)
2731 except KeyError:
2643 except KeyError:
2732 return None
2644 return None
2733
2645
2734 user = opts.get('user') or old.user()
2646 user = opts.get('user') or old.user()
2735 date = opts.get('date') or old.date()
2647 date = opts.get('date') or old.date()
2736 editform = mergeeditform(old, 'commit.amend')
2648 editform = mergeeditform(old, 'commit.amend')
2737 editor = getcommiteditor(editform=editform, **opts)
2649 editor = getcommiteditor(editform=editform, **opts)
2738 if not message:
2650 if not message:
2739 editor = getcommiteditor(edit=True, editform=editform)
2651 editor = getcommiteditor(edit=True, editform=editform)
2740 message = old.description()
2652 message = old.description()
2741
2653
2742 pureextra = extra.copy()
2654 pureextra = extra.copy()
2743 extra['amend_source'] = old.hex()
2655 extra['amend_source'] = old.hex()
2744
2656
2745 new = context.memctx(repo,
2657 new = context.memctx(repo,
2746 parents=[base.node(), old.p2().node()],
2658 parents=[base.node(), old.p2().node()],
2747 text=message,
2659 text=message,
2748 files=files,
2660 files=files,
2749 filectxfn=filectxfn,
2661 filectxfn=filectxfn,
2750 user=user,
2662 user=user,
2751 date=date,
2663 date=date,
2752 extra=extra,
2664 extra=extra,
2753 editor=editor)
2665 editor=editor)
2754
2666
2755 newdesc = changelog.stripdesc(new.description())
2667 newdesc = changelog.stripdesc(new.description())
2756 if ((not node)
2668 if ((not node)
2757 and newdesc == old.description()
2669 and newdesc == old.description()
2758 and user == old.user()
2670 and user == old.user()
2759 and date == old.date()
2671 and date == old.date()
2760 and pureextra == old.extra()):
2672 and pureextra == old.extra()):
2761 # nothing changed. continuing here would create a new node
2673 # nothing changed. continuing here would create a new node
2762 # anyway because of the amend_source noise.
2674 # anyway because of the amend_source noise.
2763 #
2675 #
2764 # This not what we expect from amend.
2676 # This not what we expect from amend.
2765 return old.node()
2677 return old.node()
2766
2678
2767 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2679 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2768 try:
2680 try:
2769 if opts.get('secret'):
2681 if opts.get('secret'):
2770 commitphase = 'secret'
2682 commitphase = 'secret'
2771 else:
2683 else:
2772 commitphase = old.phase()
2684 commitphase = old.phase()
2773 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2685 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2774 newid = repo.commitctx(new)
2686 newid = repo.commitctx(new)
2775 finally:
2687 finally:
2776 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2688 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2777 if newid != old.node():
2689 if newid != old.node():
2778 # Reroute the working copy parent to the new changeset
2690 # Reroute the working copy parent to the new changeset
2779 repo.setparents(newid, nullid)
2691 repo.setparents(newid, nullid)
2780
2692
2781 # Move bookmarks from old parent to amend commit
2693 # Move bookmarks from old parent to amend commit
2782 bms = repo.nodebookmarks(old.node())
2694 bms = repo.nodebookmarks(old.node())
2783 if bms:
2695 if bms:
2784 marks = repo._bookmarks
2696 marks = repo._bookmarks
2785 for bm in bms:
2697 for bm in bms:
2786 ui.debug('moving bookmarks %r from %s to %s\n' %
2698 ui.debug('moving bookmarks %r from %s to %s\n' %
2787 (marks, old.hex(), hex(newid)))
2699 (marks, old.hex(), hex(newid)))
2788 marks[bm] = newid
2700 marks[bm] = newid
2789 marks.recordchange(tr)
2701 marks.recordchange(tr)
2790 #commit the whole amend process
2702 #commit the whole amend process
2791 if createmarkers:
2703 if createmarkers:
2792 # mark the new changeset as successor of the rewritten one
2704 # mark the new changeset as successor of the rewritten one
2793 new = repo[newid]
2705 new = repo[newid]
2794 obs = [(old, (new,))]
2706 obs = [(old, (new,))]
2795 if node:
2707 if node:
2796 obs.append((ctx, ()))
2708 obs.append((ctx, ()))
2797
2709
2798 obsolete.createmarkers(repo, obs)
2710 obsolete.createmarkers(repo, obs)
2799 if not createmarkers and newid != old.node():
2711 if not createmarkers and newid != old.node():
2800 # Strip the intermediate commit (if there was one) and the amended
2712 # Strip the intermediate commit (if there was one) and the amended
2801 # commit
2713 # commit
2802 if node:
2714 if node:
2803 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2715 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2804 ui.note(_('stripping amended changeset %s\n') % old)
2716 ui.note(_('stripping amended changeset %s\n') % old)
2805 repair.strip(ui, repo, old.node(), topic='amend-backup')
2717 repair.strip(ui, repo, old.node(), topic='amend-backup')
2806 finally:
2718 finally:
2807 lockmod.release(lock, wlock)
2719 lockmod.release(lock, wlock)
2808 return newid
2720 return newid
2809
2721
2810 def commiteditor(repo, ctx, subs, editform=''):
2722 def commiteditor(repo, ctx, subs, editform=''):
2811 if ctx.description():
2723 if ctx.description():
2812 return ctx.description()
2724 return ctx.description()
2813 return commitforceeditor(repo, ctx, subs, editform=editform,
2725 return commitforceeditor(repo, ctx, subs, editform=editform,
2814 unchangedmessagedetection=True)
2726 unchangedmessagedetection=True)
2815
2727
2816 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2728 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2817 editform='', unchangedmessagedetection=False):
2729 editform='', unchangedmessagedetection=False):
2818 if not extramsg:
2730 if not extramsg:
2819 extramsg = _("Leave message empty to abort commit.")
2731 extramsg = _("Leave message empty to abort commit.")
2820
2732
2821 forms = [e for e in editform.split('.') if e]
2733 forms = [e for e in editform.split('.') if e]
2822 forms.insert(0, 'changeset')
2734 forms.insert(0, 'changeset')
2823 templatetext = None
2735 templatetext = None
2824 while forms:
2736 while forms:
2825 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2737 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2826 if tmpl:
2738 if tmpl:
2827 templatetext = committext = buildcommittemplate(
2739 templatetext = committext = buildcommittemplate(
2828 repo, ctx, subs, extramsg, tmpl)
2740 repo, ctx, subs, extramsg, tmpl)
2829 break
2741 break
2830 forms.pop()
2742 forms.pop()
2831 else:
2743 else:
2832 committext = buildcommittext(repo, ctx, subs, extramsg)
2744 committext = buildcommittext(repo, ctx, subs, extramsg)
2833
2745
2834 # run editor in the repository root
2746 # run editor in the repository root
2835 olddir = os.getcwd()
2747 olddir = os.getcwd()
2836 os.chdir(repo.root)
2748 os.chdir(repo.root)
2837
2749
2838 # make in-memory changes visible to external process
2750 # make in-memory changes visible to external process
2839 tr = repo.currenttransaction()
2751 tr = repo.currenttransaction()
2840 repo.dirstate.write(tr)
2752 repo.dirstate.write(tr)
2841 pending = tr and tr.writepending() and repo.root
2753 pending = tr and tr.writepending() and repo.root
2842
2754
2843 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
2755 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
2844 editform=editform, pending=pending)
2756 editform=editform, pending=pending)
2845 text = re.sub("(?m)^HG:.*(\n|$)", "", editortext)
2757 text = re.sub("(?m)^HG:.*(\n|$)", "", editortext)
2846 os.chdir(olddir)
2758 os.chdir(olddir)
2847
2759
2848 if finishdesc:
2760 if finishdesc:
2849 text = finishdesc(text)
2761 text = finishdesc(text)
2850 if not text.strip():
2762 if not text.strip():
2851 raise error.Abort(_("empty commit message"))
2763 raise error.Abort(_("empty commit message"))
2852 if unchangedmessagedetection and editortext == templatetext:
2764 if unchangedmessagedetection and editortext == templatetext:
2853 raise error.Abort(_("commit message unchanged"))
2765 raise error.Abort(_("commit message unchanged"))
2854
2766
2855 return text
2767 return text
2856
2768
2857 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2769 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2858 ui = repo.ui
2770 ui = repo.ui
2859 tmpl, mapfile = gettemplate(ui, tmpl, None)
2771 tmpl, mapfile = gettemplate(ui, tmpl, None)
2860
2772
2861 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2773 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2862
2774
2863 for k, v in repo.ui.configitems('committemplate'):
2775 for k, v in repo.ui.configitems('committemplate'):
2864 if k != 'changeset':
2776 if k != 'changeset':
2865 t.t.cache[k] = v
2777 t.t.cache[k] = v
2866
2778
2867 if not extramsg:
2779 if not extramsg:
2868 extramsg = '' # ensure that extramsg is string
2780 extramsg = '' # ensure that extramsg is string
2869
2781
2870 ui.pushbuffer()
2782 ui.pushbuffer()
2871 t.show(ctx, extramsg=extramsg)
2783 t.show(ctx, extramsg=extramsg)
2872 return ui.popbuffer()
2784 return ui.popbuffer()
2873
2785
2874 def hgprefix(msg):
2786 def hgprefix(msg):
2875 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
2787 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
2876
2788
2877 def buildcommittext(repo, ctx, subs, extramsg):
2789 def buildcommittext(repo, ctx, subs, extramsg):
2878 edittext = []
2790 edittext = []
2879 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2791 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2880 if ctx.description():
2792 if ctx.description():
2881 edittext.append(ctx.description())
2793 edittext.append(ctx.description())
2882 edittext.append("")
2794 edittext.append("")
2883 edittext.append("") # Empty line between message and comments.
2795 edittext.append("") # Empty line between message and comments.
2884 edittext.append(hgprefix(_("Enter commit message."
2796 edittext.append(hgprefix(_("Enter commit message."
2885 " Lines beginning with 'HG:' are removed.")))
2797 " Lines beginning with 'HG:' are removed.")))
2886 edittext.append(hgprefix(extramsg))
2798 edittext.append(hgprefix(extramsg))
2887 edittext.append("HG: --")
2799 edittext.append("HG: --")
2888 edittext.append(hgprefix(_("user: %s") % ctx.user()))
2800 edittext.append(hgprefix(_("user: %s") % ctx.user()))
2889 if ctx.p2():
2801 if ctx.p2():
2890 edittext.append(hgprefix(_("branch merge")))
2802 edittext.append(hgprefix(_("branch merge")))
2891 if ctx.branch():
2803 if ctx.branch():
2892 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
2804 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
2893 if bookmarks.isactivewdirparent(repo):
2805 if bookmarks.isactivewdirparent(repo):
2894 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
2806 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
2895 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
2807 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
2896 edittext.extend([hgprefix(_("added %s") % f) for f in added])
2808 edittext.extend([hgprefix(_("added %s") % f) for f in added])
2897 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
2809 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
2898 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
2810 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
2899 if not added and not modified and not removed:
2811 if not added and not modified and not removed:
2900 edittext.append(hgprefix(_("no files changed")))
2812 edittext.append(hgprefix(_("no files changed")))
2901 edittext.append("")
2813 edittext.append("")
2902
2814
2903 return "\n".join(edittext)
2815 return "\n".join(edittext)
2904
2816
2905 def commitstatus(repo, node, branch, bheads=None, opts=None):
2817 def commitstatus(repo, node, branch, bheads=None, opts=None):
2906 if opts is None:
2818 if opts is None:
2907 opts = {}
2819 opts = {}
2908 ctx = repo[node]
2820 ctx = repo[node]
2909 parents = ctx.parents()
2821 parents = ctx.parents()
2910
2822
2911 if (not opts.get('amend') and bheads and node not in bheads and not
2823 if (not opts.get('amend') and bheads and node not in bheads and not
2912 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2824 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2913 repo.ui.status(_('created new head\n'))
2825 repo.ui.status(_('created new head\n'))
2914 # The message is not printed for initial roots. For the other
2826 # The message is not printed for initial roots. For the other
2915 # changesets, it is printed in the following situations:
2827 # changesets, it is printed in the following situations:
2916 #
2828 #
2917 # Par column: for the 2 parents with ...
2829 # Par column: for the 2 parents with ...
2918 # N: null or no parent
2830 # N: null or no parent
2919 # B: parent is on another named branch
2831 # B: parent is on another named branch
2920 # C: parent is a regular non head changeset
2832 # C: parent is a regular non head changeset
2921 # H: parent was a branch head of the current branch
2833 # H: parent was a branch head of the current branch
2922 # Msg column: whether we print "created new head" message
2834 # Msg column: whether we print "created new head" message
2923 # In the following, it is assumed that there already exists some
2835 # In the following, it is assumed that there already exists some
2924 # initial branch heads of the current branch, otherwise nothing is
2836 # initial branch heads of the current branch, otherwise nothing is
2925 # printed anyway.
2837 # printed anyway.
2926 #
2838 #
2927 # Par Msg Comment
2839 # Par Msg Comment
2928 # N N y additional topo root
2840 # N N y additional topo root
2929 #
2841 #
2930 # B N y additional branch root
2842 # B N y additional branch root
2931 # C N y additional topo head
2843 # C N y additional topo head
2932 # H N n usual case
2844 # H N n usual case
2933 #
2845 #
2934 # B B y weird additional branch root
2846 # B B y weird additional branch root
2935 # C B y branch merge
2847 # C B y branch merge
2936 # H B n merge with named branch
2848 # H B n merge with named branch
2937 #
2849 #
2938 # C C y additional head from merge
2850 # C C y additional head from merge
2939 # C H n merge with a head
2851 # C H n merge with a head
2940 #
2852 #
2941 # H H n head merge: head count decreases
2853 # H H n head merge: head count decreases
2942
2854
2943 if not opts.get('close_branch'):
2855 if not opts.get('close_branch'):
2944 for r in parents:
2856 for r in parents:
2945 if r.closesbranch() and r.branch() == branch:
2857 if r.closesbranch() and r.branch() == branch:
2946 repo.ui.status(_('reopening closed branch head %d\n') % r)
2858 repo.ui.status(_('reopening closed branch head %d\n') % r)
2947
2859
2948 if repo.ui.debugflag:
2860 if repo.ui.debugflag:
2949 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2861 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2950 elif repo.ui.verbose:
2862 elif repo.ui.verbose:
2951 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2863 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2952
2864
2953 def postcommitstatus(repo, pats, opts):
2865 def postcommitstatus(repo, pats, opts):
2954 return repo.status(match=scmutil.match(repo[None], pats, opts))
2866 return repo.status(match=scmutil.match(repo[None], pats, opts))
2955
2867
2956 def revert(ui, repo, ctx, parents, *pats, **opts):
2868 def revert(ui, repo, ctx, parents, *pats, **opts):
2957 parent, p2 = parents
2869 parent, p2 = parents
2958 node = ctx.node()
2870 node = ctx.node()
2959
2871
2960 mf = ctx.manifest()
2872 mf = ctx.manifest()
2961 if node == p2:
2873 if node == p2:
2962 parent = p2
2874 parent = p2
2963
2875
2964 # need all matching names in dirstate and manifest of target rev,
2876 # need all matching names in dirstate and manifest of target rev,
2965 # so have to walk both. do not print errors if files exist in one
2877 # so have to walk both. do not print errors if files exist in one
2966 # but not other. in both cases, filesets should be evaluated against
2878 # but not other. in both cases, filesets should be evaluated against
2967 # workingctx to get consistent result (issue4497). this means 'set:**'
2879 # workingctx to get consistent result (issue4497). this means 'set:**'
2968 # cannot be used to select missing files from target rev.
2880 # cannot be used to select missing files from target rev.
2969
2881
2970 # `names` is a mapping for all elements in working copy and target revision
2882 # `names` is a mapping for all elements in working copy and target revision
2971 # The mapping is in the form:
2883 # The mapping is in the form:
2972 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2884 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2973 names = {}
2885 names = {}
2974
2886
2975 with repo.wlock():
2887 with repo.wlock():
2976 ## filling of the `names` mapping
2888 ## filling of the `names` mapping
2977 # walk dirstate to fill `names`
2889 # walk dirstate to fill `names`
2978
2890
2979 interactive = opts.get('interactive', False)
2891 interactive = opts.get('interactive', False)
2980 wctx = repo[None]
2892 wctx = repo[None]
2981 m = scmutil.match(wctx, pats, opts)
2893 m = scmutil.match(wctx, pats, opts)
2982
2894
2983 # we'll need this later
2895 # we'll need this later
2984 targetsubs = sorted(s for s in wctx.substate if m(s))
2896 targetsubs = sorted(s for s in wctx.substate if m(s))
2985
2897
2986 if not m.always():
2898 if not m.always():
2987 for abs in repo.walk(matchmod.badmatch(m, lambda x, y: False)):
2899 for abs in repo.walk(matchmod.badmatch(m, lambda x, y: False)):
2988 names[abs] = m.rel(abs), m.exact(abs)
2900 names[abs] = m.rel(abs), m.exact(abs)
2989
2901
2990 # walk target manifest to fill `names`
2902 # walk target manifest to fill `names`
2991
2903
2992 def badfn(path, msg):
2904 def badfn(path, msg):
2993 if path in names:
2905 if path in names:
2994 return
2906 return
2995 if path in ctx.substate:
2907 if path in ctx.substate:
2996 return
2908 return
2997 path_ = path + '/'
2909 path_ = path + '/'
2998 for f in names:
2910 for f in names:
2999 if f.startswith(path_):
2911 if f.startswith(path_):
3000 return
2912 return
3001 ui.warn("%s: %s\n" % (m.rel(path), msg))
2913 ui.warn("%s: %s\n" % (m.rel(path), msg))
3002
2914
3003 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
2915 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3004 if abs not in names:
2916 if abs not in names:
3005 names[abs] = m.rel(abs), m.exact(abs)
2917 names[abs] = m.rel(abs), m.exact(abs)
3006
2918
3007 # Find status of all file in `names`.
2919 # Find status of all file in `names`.
3008 m = scmutil.matchfiles(repo, names)
2920 m = scmutil.matchfiles(repo, names)
3009
2921
3010 changes = repo.status(node1=node, match=m,
2922 changes = repo.status(node1=node, match=m,
3011 unknown=True, ignored=True, clean=True)
2923 unknown=True, ignored=True, clean=True)
3012 else:
2924 else:
3013 changes = repo.status(node1=node, match=m)
2925 changes = repo.status(node1=node, match=m)
3014 for kind in changes:
2926 for kind in changes:
3015 for abs in kind:
2927 for abs in kind:
3016 names[abs] = m.rel(abs), m.exact(abs)
2928 names[abs] = m.rel(abs), m.exact(abs)
3017
2929
3018 m = scmutil.matchfiles(repo, names)
2930 m = scmutil.matchfiles(repo, names)
3019
2931
3020 modified = set(changes.modified)
2932 modified = set(changes.modified)
3021 added = set(changes.added)
2933 added = set(changes.added)
3022 removed = set(changes.removed)
2934 removed = set(changes.removed)
3023 _deleted = set(changes.deleted)
2935 _deleted = set(changes.deleted)
3024 unknown = set(changes.unknown)
2936 unknown = set(changes.unknown)
3025 unknown.update(changes.ignored)
2937 unknown.update(changes.ignored)
3026 clean = set(changes.clean)
2938 clean = set(changes.clean)
3027 modadded = set()
2939 modadded = set()
3028
2940
3029 # split between files known in target manifest and the others
2941 # split between files known in target manifest and the others
3030 smf = set(mf)
2942 smf = set(mf)
3031
2943
3032 # determine the exact nature of the deleted changesets
2944 # determine the exact nature of the deleted changesets
3033 deladded = _deleted - smf
2945 deladded = _deleted - smf
3034 deleted = _deleted - deladded
2946 deleted = _deleted - deladded
3035
2947
3036 # We need to account for the state of the file in the dirstate,
2948 # We need to account for the state of the file in the dirstate,
3037 # even when we revert against something else than parent. This will
2949 # even when we revert against something else than parent. This will
3038 # slightly alter the behavior of revert (doing back up or not, delete
2950 # slightly alter the behavior of revert (doing back up or not, delete
3039 # or just forget etc).
2951 # or just forget etc).
3040 if parent == node:
2952 if parent == node:
3041 dsmodified = modified
2953 dsmodified = modified
3042 dsadded = added
2954 dsadded = added
3043 dsremoved = removed
2955 dsremoved = removed
3044 # store all local modifications, useful later for rename detection
2956 # store all local modifications, useful later for rename detection
3045 localchanges = dsmodified | dsadded
2957 localchanges = dsmodified | dsadded
3046 modified, added, removed = set(), set(), set()
2958 modified, added, removed = set(), set(), set()
3047 else:
2959 else:
3048 changes = repo.status(node1=parent, match=m)
2960 changes = repo.status(node1=parent, match=m)
3049 dsmodified = set(changes.modified)
2961 dsmodified = set(changes.modified)
3050 dsadded = set(changes.added)
2962 dsadded = set(changes.added)
3051 dsremoved = set(changes.removed)
2963 dsremoved = set(changes.removed)
3052 # store all local modifications, useful later for rename detection
2964 # store all local modifications, useful later for rename detection
3053 localchanges = dsmodified | dsadded
2965 localchanges = dsmodified | dsadded
3054
2966
3055 # only take into account for removes between wc and target
2967 # only take into account for removes between wc and target
3056 clean |= dsremoved - removed
2968 clean |= dsremoved - removed
3057 dsremoved &= removed
2969 dsremoved &= removed
3058 # distinct between dirstate remove and other
2970 # distinct between dirstate remove and other
3059 removed -= dsremoved
2971 removed -= dsremoved
3060
2972
3061 modadded = added & dsmodified
2973 modadded = added & dsmodified
3062 added -= modadded
2974 added -= modadded
3063
2975
3064 # tell newly modified apart.
2976 # tell newly modified apart.
3065 dsmodified &= modified
2977 dsmodified &= modified
3066 dsmodified |= modified & dsadded # dirstate added may need backup
2978 dsmodified |= modified & dsadded # dirstate added may need backup
3067 modified -= dsmodified
2979 modified -= dsmodified
3068
2980
3069 # We need to wait for some post-processing to update this set
2981 # We need to wait for some post-processing to update this set
3070 # before making the distinction. The dirstate will be used for
2982 # before making the distinction. The dirstate will be used for
3071 # that purpose.
2983 # that purpose.
3072 dsadded = added
2984 dsadded = added
3073
2985
3074 # in case of merge, files that are actually added can be reported as
2986 # in case of merge, files that are actually added can be reported as
3075 # modified, we need to post process the result
2987 # modified, we need to post process the result
3076 if p2 != nullid:
2988 if p2 != nullid:
3077 mergeadd = dsmodified - smf
2989 mergeadd = dsmodified - smf
3078 dsadded |= mergeadd
2990 dsadded |= mergeadd
3079 dsmodified -= mergeadd
2991 dsmodified -= mergeadd
3080
2992
3081 # if f is a rename, update `names` to also revert the source
2993 # if f is a rename, update `names` to also revert the source
3082 cwd = repo.getcwd()
2994 cwd = repo.getcwd()
3083 for f in localchanges:
2995 for f in localchanges:
3084 src = repo.dirstate.copied(f)
2996 src = repo.dirstate.copied(f)
3085 # XXX should we check for rename down to target node?
2997 # XXX should we check for rename down to target node?
3086 if src and src not in names and repo.dirstate[src] == 'r':
2998 if src and src not in names and repo.dirstate[src] == 'r':
3087 dsremoved.add(src)
2999 dsremoved.add(src)
3088 names[src] = (repo.pathto(src, cwd), True)
3000 names[src] = (repo.pathto(src, cwd), True)
3089
3001
3090 # distinguish between file to forget and the other
3002 # distinguish between file to forget and the other
3091 added = set()
3003 added = set()
3092 for abs in dsadded:
3004 for abs in dsadded:
3093 if repo.dirstate[abs] != 'a':
3005 if repo.dirstate[abs] != 'a':
3094 added.add(abs)
3006 added.add(abs)
3095 dsadded -= added
3007 dsadded -= added
3096
3008
3097 for abs in deladded:
3009 for abs in deladded:
3098 if repo.dirstate[abs] == 'a':
3010 if repo.dirstate[abs] == 'a':
3099 dsadded.add(abs)
3011 dsadded.add(abs)
3100 deladded -= dsadded
3012 deladded -= dsadded
3101
3013
3102 # For files marked as removed, we check if an unknown file is present at
3014 # For files marked as removed, we check if an unknown file is present at
3103 # the same path. If a such file exists it may need to be backed up.
3015 # the same path. If a such file exists it may need to be backed up.
3104 # Making the distinction at this stage helps have simpler backup
3016 # Making the distinction at this stage helps have simpler backup
3105 # logic.
3017 # logic.
3106 removunk = set()
3018 removunk = set()
3107 for abs in removed:
3019 for abs in removed:
3108 target = repo.wjoin(abs)
3020 target = repo.wjoin(abs)
3109 if os.path.lexists(target):
3021 if os.path.lexists(target):
3110 removunk.add(abs)
3022 removunk.add(abs)
3111 removed -= removunk
3023 removed -= removunk
3112
3024
3113 dsremovunk = set()
3025 dsremovunk = set()
3114 for abs in dsremoved:
3026 for abs in dsremoved:
3115 target = repo.wjoin(abs)
3027 target = repo.wjoin(abs)
3116 if os.path.lexists(target):
3028 if os.path.lexists(target):
3117 dsremovunk.add(abs)
3029 dsremovunk.add(abs)
3118 dsremoved -= dsremovunk
3030 dsremoved -= dsremovunk
3119
3031
3120 # action to be actually performed by revert
3032 # action to be actually performed by revert
3121 # (<list of file>, message>) tuple
3033 # (<list of file>, message>) tuple
3122 actions = {'revert': ([], _('reverting %s\n')),
3034 actions = {'revert': ([], _('reverting %s\n')),
3123 'add': ([], _('adding %s\n')),
3035 'add': ([], _('adding %s\n')),
3124 'remove': ([], _('removing %s\n')),
3036 'remove': ([], _('removing %s\n')),
3125 'drop': ([], _('removing %s\n')),
3037 'drop': ([], _('removing %s\n')),
3126 'forget': ([], _('forgetting %s\n')),
3038 'forget': ([], _('forgetting %s\n')),
3127 'undelete': ([], _('undeleting %s\n')),
3039 'undelete': ([], _('undeleting %s\n')),
3128 'noop': (None, _('no changes needed to %s\n')),
3040 'noop': (None, _('no changes needed to %s\n')),
3129 'unknown': (None, _('file not managed: %s\n')),
3041 'unknown': (None, _('file not managed: %s\n')),
3130 }
3042 }
3131
3043
3132 # "constant" that convey the backup strategy.
3044 # "constant" that convey the backup strategy.
3133 # All set to `discard` if `no-backup` is set do avoid checking
3045 # All set to `discard` if `no-backup` is set do avoid checking
3134 # no_backup lower in the code.
3046 # no_backup lower in the code.
3135 # These values are ordered for comparison purposes
3047 # These values are ordered for comparison purposes
3136 backupinteractive = 3 # do backup if interactively modified
3048 backupinteractive = 3 # do backup if interactively modified
3137 backup = 2 # unconditionally do backup
3049 backup = 2 # unconditionally do backup
3138 check = 1 # check if the existing file differs from target
3050 check = 1 # check if the existing file differs from target
3139 discard = 0 # never do backup
3051 discard = 0 # never do backup
3140 if opts.get('no_backup'):
3052 if opts.get('no_backup'):
3141 backupinteractive = backup = check = discard
3053 backupinteractive = backup = check = discard
3142 if interactive:
3054 if interactive:
3143 dsmodifiedbackup = backupinteractive
3055 dsmodifiedbackup = backupinteractive
3144 else:
3056 else:
3145 dsmodifiedbackup = backup
3057 dsmodifiedbackup = backup
3146 tobackup = set()
3058 tobackup = set()
3147
3059
3148 backupanddel = actions['remove']
3060 backupanddel = actions['remove']
3149 if not opts.get('no_backup'):
3061 if not opts.get('no_backup'):
3150 backupanddel = actions['drop']
3062 backupanddel = actions['drop']
3151
3063
3152 disptable = (
3064 disptable = (
3153 # dispatch table:
3065 # dispatch table:
3154 # file state
3066 # file state
3155 # action
3067 # action
3156 # make backup
3068 # make backup
3157
3069
3158 ## Sets that results that will change file on disk
3070 ## Sets that results that will change file on disk
3159 # Modified compared to target, no local change
3071 # Modified compared to target, no local change
3160 (modified, actions['revert'], discard),
3072 (modified, actions['revert'], discard),
3161 # Modified compared to target, but local file is deleted
3073 # Modified compared to target, but local file is deleted
3162 (deleted, actions['revert'], discard),
3074 (deleted, actions['revert'], discard),
3163 # Modified compared to target, local change
3075 # Modified compared to target, local change
3164 (dsmodified, actions['revert'], dsmodifiedbackup),
3076 (dsmodified, actions['revert'], dsmodifiedbackup),
3165 # Added since target
3077 # Added since target
3166 (added, actions['remove'], discard),
3078 (added, actions['remove'], discard),
3167 # Added in working directory
3079 # Added in working directory
3168 (dsadded, actions['forget'], discard),
3080 (dsadded, actions['forget'], discard),
3169 # Added since target, have local modification
3081 # Added since target, have local modification
3170 (modadded, backupanddel, backup),
3082 (modadded, backupanddel, backup),
3171 # Added since target but file is missing in working directory
3083 # Added since target but file is missing in working directory
3172 (deladded, actions['drop'], discard),
3084 (deladded, actions['drop'], discard),
3173 # Removed since target, before working copy parent
3085 # Removed since target, before working copy parent
3174 (removed, actions['add'], discard),
3086 (removed, actions['add'], discard),
3175 # Same as `removed` but an unknown file exists at the same path
3087 # Same as `removed` but an unknown file exists at the same path
3176 (removunk, actions['add'], check),
3088 (removunk, actions['add'], check),
3177 # Removed since targe, marked as such in working copy parent
3089 # Removed since targe, marked as such in working copy parent
3178 (dsremoved, actions['undelete'], discard),
3090 (dsremoved, actions['undelete'], discard),
3179 # Same as `dsremoved` but an unknown file exists at the same path
3091 # Same as `dsremoved` but an unknown file exists at the same path
3180 (dsremovunk, actions['undelete'], check),
3092 (dsremovunk, actions['undelete'], check),
3181 ## the following sets does not result in any file changes
3093 ## the following sets does not result in any file changes
3182 # File with no modification
3094 # File with no modification
3183 (clean, actions['noop'], discard),
3095 (clean, actions['noop'], discard),
3184 # Existing file, not tracked anywhere
3096 # Existing file, not tracked anywhere
3185 (unknown, actions['unknown'], discard),
3097 (unknown, actions['unknown'], discard),
3186 )
3098 )
3187
3099
3188 for abs, (rel, exact) in sorted(names.items()):
3100 for abs, (rel, exact) in sorted(names.items()):
3189 # target file to be touch on disk (relative to cwd)
3101 # target file to be touch on disk (relative to cwd)
3190 target = repo.wjoin(abs)
3102 target = repo.wjoin(abs)
3191 # search the entry in the dispatch table.
3103 # search the entry in the dispatch table.
3192 # if the file is in any of these sets, it was touched in the working
3104 # if the file is in any of these sets, it was touched in the working
3193 # directory parent and we are sure it needs to be reverted.
3105 # directory parent and we are sure it needs to be reverted.
3194 for table, (xlist, msg), dobackup in disptable:
3106 for table, (xlist, msg), dobackup in disptable:
3195 if abs not in table:
3107 if abs not in table:
3196 continue
3108 continue
3197 if xlist is not None:
3109 if xlist is not None:
3198 xlist.append(abs)
3110 xlist.append(abs)
3199 if dobackup:
3111 if dobackup:
3200 # If in interactive mode, don't automatically create
3112 # If in interactive mode, don't automatically create
3201 # .orig files (issue4793)
3113 # .orig files (issue4793)
3202 if dobackup == backupinteractive:
3114 if dobackup == backupinteractive:
3203 tobackup.add(abs)
3115 tobackup.add(abs)
3204 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
3116 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
3205 bakname = scmutil.origpath(ui, repo, rel)
3117 bakname = scmutil.origpath(ui, repo, rel)
3206 ui.note(_('saving current version of %s as %s\n') %
3118 ui.note(_('saving current version of %s as %s\n') %
3207 (rel, bakname))
3119 (rel, bakname))
3208 if not opts.get('dry_run'):
3120 if not opts.get('dry_run'):
3209 if interactive:
3121 if interactive:
3210 util.copyfile(target, bakname)
3122 util.copyfile(target, bakname)
3211 else:
3123 else:
3212 util.rename(target, bakname)
3124 util.rename(target, bakname)
3213 if ui.verbose or not exact:
3125 if ui.verbose or not exact:
3214 if not isinstance(msg, basestring):
3126 if not isinstance(msg, basestring):
3215 msg = msg(abs)
3127 msg = msg(abs)
3216 ui.status(msg % rel)
3128 ui.status(msg % rel)
3217 elif exact:
3129 elif exact:
3218 ui.warn(msg % rel)
3130 ui.warn(msg % rel)
3219 break
3131 break
3220
3132
3221 if not opts.get('dry_run'):
3133 if not opts.get('dry_run'):
3222 needdata = ('revert', 'add', 'undelete')
3134 needdata = ('revert', 'add', 'undelete')
3223 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3135 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3224 _performrevert(repo, parents, ctx, actions, interactive, tobackup)
3136 _performrevert(repo, parents, ctx, actions, interactive, tobackup)
3225
3137
3226 if targetsubs:
3138 if targetsubs:
3227 # Revert the subrepos on the revert list
3139 # Revert the subrepos on the revert list
3228 for sub in targetsubs:
3140 for sub in targetsubs:
3229 try:
3141 try:
3230 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3142 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3231 except KeyError:
3143 except KeyError:
3232 raise error.Abort("subrepository '%s' does not exist in %s!"
3144 raise error.Abort("subrepository '%s' does not exist in %s!"
3233 % (sub, short(ctx.node())))
3145 % (sub, short(ctx.node())))
3234
3146
3235 def _revertprefetch(repo, ctx, *files):
3147 def _revertprefetch(repo, ctx, *files):
3236 """Let extension changing the storage layer prefetch content"""
3148 """Let extension changing the storage layer prefetch content"""
3237 pass
3149 pass
3238
3150
3239 def _performrevert(repo, parents, ctx, actions, interactive=False,
3151 def _performrevert(repo, parents, ctx, actions, interactive=False,
3240 tobackup=None):
3152 tobackup=None):
3241 """function that actually perform all the actions computed for revert
3153 """function that actually perform all the actions computed for revert
3242
3154
3243 This is an independent function to let extension to plug in and react to
3155 This is an independent function to let extension to plug in and react to
3244 the imminent revert.
3156 the imminent revert.
3245
3157
3246 Make sure you have the working directory locked when calling this function.
3158 Make sure you have the working directory locked when calling this function.
3247 """
3159 """
3248 parent, p2 = parents
3160 parent, p2 = parents
3249 node = ctx.node()
3161 node = ctx.node()
3250 excluded_files = []
3162 excluded_files = []
3251 matcher_opts = {"exclude": excluded_files}
3163 matcher_opts = {"exclude": excluded_files}
3252
3164
3253 def checkout(f):
3165 def checkout(f):
3254 fc = ctx[f]
3166 fc = ctx[f]
3255 repo.wwrite(f, fc.data(), fc.flags())
3167 repo.wwrite(f, fc.data(), fc.flags())
3256
3168
3257 audit_path = pathutil.pathauditor(repo.root)
3169 audit_path = pathutil.pathauditor(repo.root)
3258 for f in actions['forget'][0]:
3170 for f in actions['forget'][0]:
3259 if interactive:
3171 if interactive:
3260 choice = \
3172 choice = \
3261 repo.ui.promptchoice(
3173 repo.ui.promptchoice(
3262 _("forget added file %s (yn)?$$ &Yes $$ &No")
3174 _("forget added file %s (yn)?$$ &Yes $$ &No")
3263 % f)
3175 % f)
3264 if choice == 0:
3176 if choice == 0:
3265 repo.dirstate.drop(f)
3177 repo.dirstate.drop(f)
3266 else:
3178 else:
3267 excluded_files.append(repo.wjoin(f))
3179 excluded_files.append(repo.wjoin(f))
3268 else:
3180 else:
3269 repo.dirstate.drop(f)
3181 repo.dirstate.drop(f)
3270 for f in actions['remove'][0]:
3182 for f in actions['remove'][0]:
3271 audit_path(f)
3183 audit_path(f)
3272 try:
3184 try:
3273 util.unlinkpath(repo.wjoin(f))
3185 util.unlinkpath(repo.wjoin(f))
3274 except OSError:
3186 except OSError:
3275 pass
3187 pass
3276 repo.dirstate.remove(f)
3188 repo.dirstate.remove(f)
3277 for f in actions['drop'][0]:
3189 for f in actions['drop'][0]:
3278 audit_path(f)
3190 audit_path(f)
3279 repo.dirstate.remove(f)
3191 repo.dirstate.remove(f)
3280
3192
3281 normal = None
3193 normal = None
3282 if node == parent:
3194 if node == parent:
3283 # We're reverting to our parent. If possible, we'd like status
3195 # We're reverting to our parent. If possible, we'd like status
3284 # to report the file as clean. We have to use normallookup for
3196 # to report the file as clean. We have to use normallookup for
3285 # merges to avoid losing information about merged/dirty files.
3197 # merges to avoid losing information about merged/dirty files.
3286 if p2 != nullid:
3198 if p2 != nullid:
3287 normal = repo.dirstate.normallookup
3199 normal = repo.dirstate.normallookup
3288 else:
3200 else:
3289 normal = repo.dirstate.normal
3201 normal = repo.dirstate.normal
3290
3202
3291 newlyaddedandmodifiedfiles = set()
3203 newlyaddedandmodifiedfiles = set()
3292 if interactive:
3204 if interactive:
3293 # Prompt the user for changes to revert
3205 # Prompt the user for changes to revert
3294 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3206 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3295 m = scmutil.match(ctx, torevert, matcher_opts)
3207 m = scmutil.match(ctx, torevert, matcher_opts)
3296 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3208 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3297 diffopts.nodates = True
3209 diffopts.nodates = True
3298 diffopts.git = True
3210 diffopts.git = True
3299 reversehunks = repo.ui.configbool('experimental',
3211 reversehunks = repo.ui.configbool('experimental',
3300 'revertalternateinteractivemode',
3212 'revertalternateinteractivemode',
3301 True)
3213 True)
3302 if reversehunks:
3214 if reversehunks:
3303 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3215 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3304 else:
3216 else:
3305 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3217 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3306 originalchunks = patch.parsepatch(diff)
3218 originalchunks = patch.parsepatch(diff)
3307 operation = 'discard' if node == parent else 'revert'
3219 operation = 'discard' if node == parent else 'revert'
3308
3220
3309 try:
3221 try:
3310
3222
3311 chunks, opts = recordfilter(repo.ui, originalchunks,
3223 chunks, opts = recordfilter(repo.ui, originalchunks,
3312 operation=operation)
3224 operation=operation)
3313 if reversehunks:
3225 if reversehunks:
3314 chunks = patch.reversehunks(chunks)
3226 chunks = patch.reversehunks(chunks)
3315
3227
3316 except patch.PatchError as err:
3228 except patch.PatchError as err:
3317 raise error.Abort(_('error parsing patch: %s') % err)
3229 raise error.Abort(_('error parsing patch: %s') % err)
3318
3230
3319 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3231 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3320 if tobackup is None:
3232 if tobackup is None:
3321 tobackup = set()
3233 tobackup = set()
3322 # Apply changes
3234 # Apply changes
3323 fp = stringio()
3235 fp = stringio()
3324 for c in chunks:
3236 for c in chunks:
3325 # Create a backup file only if this hunk should be backed up
3237 # Create a backup file only if this hunk should be backed up
3326 if ishunk(c) and c.header.filename() in tobackup:
3238 if ishunk(c) and c.header.filename() in tobackup:
3327 abs = c.header.filename()
3239 abs = c.header.filename()
3328 target = repo.wjoin(abs)
3240 target = repo.wjoin(abs)
3329 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3241 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3330 util.copyfile(target, bakname)
3242 util.copyfile(target, bakname)
3331 tobackup.remove(abs)
3243 tobackup.remove(abs)
3332 c.write(fp)
3244 c.write(fp)
3333 dopatch = fp.tell()
3245 dopatch = fp.tell()
3334 fp.seek(0)
3246 fp.seek(0)
3335 if dopatch:
3247 if dopatch:
3336 try:
3248 try:
3337 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3249 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3338 except patch.PatchError as err:
3250 except patch.PatchError as err:
3339 raise error.Abort(str(err))
3251 raise error.Abort(str(err))
3340 del fp
3252 del fp
3341 else:
3253 else:
3342 for f in actions['revert'][0]:
3254 for f in actions['revert'][0]:
3343 checkout(f)
3255 checkout(f)
3344 if normal:
3256 if normal:
3345 normal(f)
3257 normal(f)
3346
3258
3347 for f in actions['add'][0]:
3259 for f in actions['add'][0]:
3348 # Don't checkout modified files, they are already created by the diff
3260 # Don't checkout modified files, they are already created by the diff
3349 if f not in newlyaddedandmodifiedfiles:
3261 if f not in newlyaddedandmodifiedfiles:
3350 checkout(f)
3262 checkout(f)
3351 repo.dirstate.add(f)
3263 repo.dirstate.add(f)
3352
3264
3353 normal = repo.dirstate.normallookup
3265 normal = repo.dirstate.normallookup
3354 if node == parent and p2 == nullid:
3266 if node == parent and p2 == nullid:
3355 normal = repo.dirstate.normal
3267 normal = repo.dirstate.normal
3356 for f in actions['undelete'][0]:
3268 for f in actions['undelete'][0]:
3357 checkout(f)
3269 checkout(f)
3358 normal(f)
3270 normal(f)
3359
3271
3360 copied = copies.pathcopies(repo[parent], ctx)
3272 copied = copies.pathcopies(repo[parent], ctx)
3361
3273
3362 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3274 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3363 if f in copied:
3275 if f in copied:
3364 repo.dirstate.copy(copied[f], f)
3276 repo.dirstate.copy(copied[f], f)
3365
3277
3366 def command(table):
3278 def command(table):
3367 """Returns a function object to be used as a decorator for making commands.
3279 """Returns a function object to be used as a decorator for making commands.
3368
3280
3369 This function receives a command table as its argument. The table should
3281 This function receives a command table as its argument. The table should
3370 be a dict.
3282 be a dict.
3371
3283
3372 The returned function can be used as a decorator for adding commands
3284 The returned function can be used as a decorator for adding commands
3373 to that command table. This function accepts multiple arguments to define
3285 to that command table. This function accepts multiple arguments to define
3374 a command.
3286 a command.
3375
3287
3376 The first argument is the command name.
3288 The first argument is the command name.
3377
3289
3378 The options argument is an iterable of tuples defining command arguments.
3290 The options argument is an iterable of tuples defining command arguments.
3379 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
3291 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
3380
3292
3381 The synopsis argument defines a short, one line summary of how to use the
3293 The synopsis argument defines a short, one line summary of how to use the
3382 command. This shows up in the help output.
3294 command. This shows up in the help output.
3383
3295
3384 The norepo argument defines whether the command does not require a
3296 The norepo argument defines whether the command does not require a
3385 local repository. Most commands operate against a repository, thus the
3297 local repository. Most commands operate against a repository, thus the
3386 default is False.
3298 default is False.
3387
3299
3388 The optionalrepo argument defines whether the command optionally requires
3300 The optionalrepo argument defines whether the command optionally requires
3389 a local repository.
3301 a local repository.
3390
3302
3391 The inferrepo argument defines whether to try to find a repository from the
3303 The inferrepo argument defines whether to try to find a repository from the
3392 command line arguments. If True, arguments will be examined for potential
3304 command line arguments. If True, arguments will be examined for potential
3393 repository locations. See ``findrepo()``. If a repository is found, it
3305 repository locations. See ``findrepo()``. If a repository is found, it
3394 will be used.
3306 will be used.
3395 """
3307 """
3396 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
3308 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
3397 inferrepo=False):
3309 inferrepo=False):
3398 def decorator(func):
3310 def decorator(func):
3399 func.norepo = norepo
3311 func.norepo = norepo
3400 func.optionalrepo = optionalrepo
3312 func.optionalrepo = optionalrepo
3401 func.inferrepo = inferrepo
3313 func.inferrepo = inferrepo
3402 if synopsis:
3314 if synopsis:
3403 table[name] = func, list(options), synopsis
3315 table[name] = func, list(options), synopsis
3404 else:
3316 else:
3405 table[name] = func, list(options)
3317 table[name] = func, list(options)
3406 return func
3318 return func
3407 return decorator
3319 return decorator
3408
3320
3409 return cmd
3321 return cmd
3410
3322
3411 def checkunresolved(ms):
3323 def checkunresolved(ms):
3412 ms._repo.ui.deprecwarn('checkunresolved moved from cmdutil to mergeutil',
3324 ms._repo.ui.deprecwarn('checkunresolved moved from cmdutil to mergeutil',
3413 '4.1')
3325 '4.1')
3414 return mergeutil.checkunresolved(ms)
3326 return mergeutil.checkunresolved(ms)
3415
3327
3416 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3328 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3417 # commands.outgoing. "missing" is "missing" of the result of
3329 # commands.outgoing. "missing" is "missing" of the result of
3418 # "findcommonoutgoing()"
3330 # "findcommonoutgoing()"
3419 outgoinghooks = util.hooks()
3331 outgoinghooks = util.hooks()
3420
3332
3421 # a list of (ui, repo) functions called by commands.summary
3333 # a list of (ui, repo) functions called by commands.summary
3422 summaryhooks = util.hooks()
3334 summaryhooks = util.hooks()
3423
3335
3424 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3336 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3425 #
3337 #
3426 # functions should return tuple of booleans below, if 'changes' is None:
3338 # functions should return tuple of booleans below, if 'changes' is None:
3427 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3339 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3428 #
3340 #
3429 # otherwise, 'changes' is a tuple of tuples below:
3341 # otherwise, 'changes' is a tuple of tuples below:
3430 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3342 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3431 # - (desturl, destbranch, destpeer, outgoing)
3343 # - (desturl, destbranch, destpeer, outgoing)
3432 summaryremotehooks = util.hooks()
3344 summaryremotehooks = util.hooks()
3433
3345
3434 # A list of state files kept by multistep operations like graft.
3346 # A list of state files kept by multistep operations like graft.
3435 # Since graft cannot be aborted, it is considered 'clearable' by update.
3347 # Since graft cannot be aborted, it is considered 'clearable' by update.
3436 # note: bisect is intentionally excluded
3348 # note: bisect is intentionally excluded
3437 # (state file, clearable, allowcommit, error, hint)
3349 # (state file, clearable, allowcommit, error, hint)
3438 unfinishedstates = [
3350 unfinishedstates = [
3439 ('graftstate', True, False, _('graft in progress'),
3351 ('graftstate', True, False, _('graft in progress'),
3440 _("use 'hg graft --continue' or 'hg update' to abort")),
3352 _("use 'hg graft --continue' or 'hg update' to abort")),
3441 ('updatestate', True, False, _('last update was interrupted'),
3353 ('updatestate', True, False, _('last update was interrupted'),
3442 _("use 'hg update' to get a consistent checkout"))
3354 _("use 'hg update' to get a consistent checkout"))
3443 ]
3355 ]
3444
3356
3445 def checkunfinished(repo, commit=False):
3357 def checkunfinished(repo, commit=False):
3446 '''Look for an unfinished multistep operation, like graft, and abort
3358 '''Look for an unfinished multistep operation, like graft, and abort
3447 if found. It's probably good to check this right before
3359 if found. It's probably good to check this right before
3448 bailifchanged().
3360 bailifchanged().
3449 '''
3361 '''
3450 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3362 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3451 if commit and allowcommit:
3363 if commit and allowcommit:
3452 continue
3364 continue
3453 if repo.vfs.exists(f):
3365 if repo.vfs.exists(f):
3454 raise error.Abort(msg, hint=hint)
3366 raise error.Abort(msg, hint=hint)
3455
3367
3456 def clearunfinished(repo):
3368 def clearunfinished(repo):
3457 '''Check for unfinished operations (as above), and clear the ones
3369 '''Check for unfinished operations (as above), and clear the ones
3458 that are clearable.
3370 that are clearable.
3459 '''
3371 '''
3460 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3372 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3461 if not clearable and repo.vfs.exists(f):
3373 if not clearable and repo.vfs.exists(f):
3462 raise error.Abort(msg, hint=hint)
3374 raise error.Abort(msg, hint=hint)
3463 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3375 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3464 if clearable and repo.vfs.exists(f):
3376 if clearable and repo.vfs.exists(f):
3465 util.unlink(repo.join(f))
3377 util.unlink(repo.join(f))
3466
3378
3467 afterresolvedstates = [
3379 afterresolvedstates = [
3468 ('graftstate',
3380 ('graftstate',
3469 _('hg graft --continue')),
3381 _('hg graft --continue')),
3470 ]
3382 ]
3471
3383
3472 def howtocontinue(repo):
3384 def howtocontinue(repo):
3473 '''Check for an unfinished operation and return the command to finish
3385 '''Check for an unfinished operation and return the command to finish
3474 it.
3386 it.
3475
3387
3476 afterresolvedstates tuples define a .hg/{file} and the corresponding
3388 afterresolvedstates tuples define a .hg/{file} and the corresponding
3477 command needed to finish it.
3389 command needed to finish it.
3478
3390
3479 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3391 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3480 a boolean.
3392 a boolean.
3481 '''
3393 '''
3482 contmsg = _("continue: %s")
3394 contmsg = _("continue: %s")
3483 for f, msg in afterresolvedstates:
3395 for f, msg in afterresolvedstates:
3484 if repo.vfs.exists(f):
3396 if repo.vfs.exists(f):
3485 return contmsg % msg, True
3397 return contmsg % msg, True
3486 workingctx = repo[None]
3398 workingctx = repo[None]
3487 dirty = any(repo.status()) or any(workingctx.sub(s).dirty()
3399 dirty = any(repo.status()) or any(workingctx.sub(s).dirty()
3488 for s in workingctx.substate)
3400 for s in workingctx.substate)
3489 if dirty:
3401 if dirty:
3490 return contmsg % _("hg commit"), False
3402 return contmsg % _("hg commit"), False
3491 return None, None
3403 return None, None
3492
3404
3493 def checkafterresolved(repo):
3405 def checkafterresolved(repo):
3494 '''Inform the user about the next action after completing hg resolve
3406 '''Inform the user about the next action after completing hg resolve
3495
3407
3496 If there's a matching afterresolvedstates, howtocontinue will yield
3408 If there's a matching afterresolvedstates, howtocontinue will yield
3497 repo.ui.warn as the reporter.
3409 repo.ui.warn as the reporter.
3498
3410
3499 Otherwise, it will yield repo.ui.note.
3411 Otherwise, it will yield repo.ui.note.
3500 '''
3412 '''
3501 msg, warning = howtocontinue(repo)
3413 msg, warning = howtocontinue(repo)
3502 if msg is not None:
3414 if msg is not None:
3503 if warning:
3415 if warning:
3504 repo.ui.warn("%s\n" % msg)
3416 repo.ui.warn("%s\n" % msg)
3505 else:
3417 else:
3506 repo.ui.note("%s\n" % msg)
3418 repo.ui.note("%s\n" % msg)
3507
3419
3508 def wrongtooltocontinue(repo, task):
3420 def wrongtooltocontinue(repo, task):
3509 '''Raise an abort suggesting how to properly continue if there is an
3421 '''Raise an abort suggesting how to properly continue if there is an
3510 active task.
3422 active task.
3511
3423
3512 Uses howtocontinue() to find the active task.
3424 Uses howtocontinue() to find the active task.
3513
3425
3514 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3426 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3515 a hint.
3427 a hint.
3516 '''
3428 '''
3517 after = howtocontinue(repo)
3429 after = howtocontinue(repo)
3518 hint = None
3430 hint = None
3519 if after[1]:
3431 if after[1]:
3520 hint = after[0]
3432 hint = after[0]
3521 raise error.Abort(_('no %s in progress') % task, hint=hint)
3433 raise error.Abort(_('no %s in progress') % task, hint=hint)
3522
3434
3523 class dirstateguard(dirstateguardmod.dirstateguard):
3435 class dirstateguard(dirstateguardmod.dirstateguard):
3524 def __init__(self, repo, name):
3436 def __init__(self, repo, name):
3525 dirstateguardmod.dirstateguard.__init__(self, repo, name)
3437 dirstateguardmod.dirstateguard.__init__(self, repo, name)
3526 repo.ui.deprecwarn(
3438 repo.ui.deprecwarn(
3527 'dirstateguard has moved from cmdutil to dirstateguard',
3439 'dirstateguard has moved from cmdutil to dirstateguard',
3528 '4.1')
3440 '4.1')
@@ -1,7092 +1,7093 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 string
18 import string
19 import sys
19 import sys
20 import tempfile
20 import tempfile
21 import time
21 import time
22
22
23 from .i18n import _
23 from .i18n import _
24 from .node import (
24 from .node import (
25 bin,
25 bin,
26 hex,
26 hex,
27 nullhex,
27 nullhex,
28 nullid,
28 nullid,
29 nullrev,
29 nullrev,
30 short,
30 short,
31 )
31 )
32 from . import (
32 from . import (
33 archival,
33 archival,
34 bookmarks,
34 bookmarks,
35 bundle2,
35 bundle2,
36 changegroup,
36 changegroup,
37 cmdutil,
37 cmdutil,
38 commandserver,
38 commandserver,
39 copies,
39 copies,
40 dagparser,
40 dagparser,
41 dagutil,
41 dagutil,
42 destutil,
42 destutil,
43 dirstateguard,
43 dirstateguard,
44 discovery,
44 discovery,
45 encoding,
45 encoding,
46 error,
46 error,
47 exchange,
47 exchange,
48 extensions,
48 extensions,
49 fileset,
49 fileset,
50 formatter,
50 formatter,
51 graphmod,
51 graphmod,
52 hbisect,
52 hbisect,
53 help,
53 help,
54 hg,
54 hg,
55 hgweb,
55 hgweb,
56 localrepo,
56 localrepo,
57 lock as lockmod,
57 lock as lockmod,
58 merge as mergemod,
58 merge as mergemod,
59 minirst,
59 minirst,
60 obsolete,
60 obsolete,
61 patch,
61 patch,
62 phases,
62 phases,
63 policy,
63 policy,
64 pvec,
64 pvec,
65 pycompat,
65 pycompat,
66 repair,
66 repair,
67 revlog,
67 revlog,
68 revset,
68 revset,
69 scmutil,
69 scmutil,
70 server,
70 setdiscovery,
71 setdiscovery,
71 sshserver,
72 sshserver,
72 sslutil,
73 sslutil,
73 streamclone,
74 streamclone,
74 templatekw,
75 templatekw,
75 templater,
76 templater,
76 treediscovery,
77 treediscovery,
77 ui as uimod,
78 ui as uimod,
78 util,
79 util,
79 )
80 )
80
81
81 release = lockmod.release
82 release = lockmod.release
82
83
83 table = {}
84 table = {}
84
85
85 command = cmdutil.command(table)
86 command = cmdutil.command(table)
86
87
87 # label constants
88 # label constants
88 # until 3.5, bookmarks.current was the advertised name, not
89 # until 3.5, bookmarks.current was the advertised name, not
89 # bookmarks.active, so we must use both to avoid breaking old
90 # bookmarks.active, so we must use both to avoid breaking old
90 # custom styles
91 # custom styles
91 activebookmarklabel = 'bookmarks.active bookmarks.current'
92 activebookmarklabel = 'bookmarks.active bookmarks.current'
92
93
93 # common command options
94 # common command options
94
95
95 globalopts = [
96 globalopts = [
96 ('R', 'repository', '',
97 ('R', 'repository', '',
97 _('repository root directory or name of overlay bundle file'),
98 _('repository root directory or name of overlay bundle file'),
98 _('REPO')),
99 _('REPO')),
99 ('', 'cwd', '',
100 ('', 'cwd', '',
100 _('change working directory'), _('DIR')),
101 _('change working directory'), _('DIR')),
101 ('y', 'noninteractive', None,
102 ('y', 'noninteractive', None,
102 _('do not prompt, automatically pick the first choice for all prompts')),
103 _('do not prompt, automatically pick the first choice for all prompts')),
103 ('q', 'quiet', None, _('suppress output')),
104 ('q', 'quiet', None, _('suppress output')),
104 ('v', 'verbose', None, _('enable additional output')),
105 ('v', 'verbose', None, _('enable additional output')),
105 ('', 'config', [],
106 ('', 'config', [],
106 _('set/override config option (use \'section.name=value\')'),
107 _('set/override config option (use \'section.name=value\')'),
107 _('CONFIG')),
108 _('CONFIG')),
108 ('', 'debug', None, _('enable debugging output')),
109 ('', 'debug', None, _('enable debugging output')),
109 ('', 'debugger', None, _('start debugger')),
110 ('', 'debugger', None, _('start debugger')),
110 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
111 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
111 _('ENCODE')),
112 _('ENCODE')),
112 ('', 'encodingmode', encoding.encodingmode,
113 ('', 'encodingmode', encoding.encodingmode,
113 _('set the charset encoding mode'), _('MODE')),
114 _('set the charset encoding mode'), _('MODE')),
114 ('', 'traceback', None, _('always print a traceback on exception')),
115 ('', 'traceback', None, _('always print a traceback on exception')),
115 ('', 'time', None, _('time how long the command takes')),
116 ('', 'time', None, _('time how long the command takes')),
116 ('', 'profile', None, _('print command execution profile')),
117 ('', 'profile', None, _('print command execution profile')),
117 ('', 'version', None, _('output version information and exit')),
118 ('', 'version', None, _('output version information and exit')),
118 ('h', 'help', None, _('display help and exit')),
119 ('h', 'help', None, _('display help and exit')),
119 ('', 'hidden', False, _('consider hidden changesets')),
120 ('', 'hidden', False, _('consider hidden changesets')),
120 ]
121 ]
121
122
122 dryrunopts = [('n', 'dry-run', None,
123 dryrunopts = [('n', 'dry-run', None,
123 _('do not perform actions, just print output'))]
124 _('do not perform actions, just print output'))]
124
125
125 remoteopts = [
126 remoteopts = [
126 ('e', 'ssh', '',
127 ('e', 'ssh', '',
127 _('specify ssh command to use'), _('CMD')),
128 _('specify ssh command to use'), _('CMD')),
128 ('', 'remotecmd', '',
129 ('', 'remotecmd', '',
129 _('specify hg command to run on the remote side'), _('CMD')),
130 _('specify hg command to run on the remote side'), _('CMD')),
130 ('', 'insecure', None,
131 ('', 'insecure', None,
131 _('do not verify server certificate (ignoring web.cacerts config)')),
132 _('do not verify server certificate (ignoring web.cacerts config)')),
132 ]
133 ]
133
134
134 walkopts = [
135 walkopts = [
135 ('I', 'include', [],
136 ('I', 'include', [],
136 _('include names matching the given patterns'), _('PATTERN')),
137 _('include names matching the given patterns'), _('PATTERN')),
137 ('X', 'exclude', [],
138 ('X', 'exclude', [],
138 _('exclude names matching the given patterns'), _('PATTERN')),
139 _('exclude names matching the given patterns'), _('PATTERN')),
139 ]
140 ]
140
141
141 commitopts = [
142 commitopts = [
142 ('m', 'message', '',
143 ('m', 'message', '',
143 _('use text as commit message'), _('TEXT')),
144 _('use text as commit message'), _('TEXT')),
144 ('l', 'logfile', '',
145 ('l', 'logfile', '',
145 _('read commit message from file'), _('FILE')),
146 _('read commit message from file'), _('FILE')),
146 ]
147 ]
147
148
148 commitopts2 = [
149 commitopts2 = [
149 ('d', 'date', '',
150 ('d', 'date', '',
150 _('record the specified date as commit date'), _('DATE')),
151 _('record the specified date as commit date'), _('DATE')),
151 ('u', 'user', '',
152 ('u', 'user', '',
152 _('record the specified user as committer'), _('USER')),
153 _('record the specified user as committer'), _('USER')),
153 ]
154 ]
154
155
155 # hidden for now
156 # hidden for now
156 formatteropts = [
157 formatteropts = [
157 ('T', 'template', '',
158 ('T', 'template', '',
158 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
159 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
159 ]
160 ]
160
161
161 templateopts = [
162 templateopts = [
162 ('', 'style', '',
163 ('', 'style', '',
163 _('display using template map file (DEPRECATED)'), _('STYLE')),
164 _('display using template map file (DEPRECATED)'), _('STYLE')),
164 ('T', 'template', '',
165 ('T', 'template', '',
165 _('display with template'), _('TEMPLATE')),
166 _('display with template'), _('TEMPLATE')),
166 ]
167 ]
167
168
168 logopts = [
169 logopts = [
169 ('p', 'patch', None, _('show patch')),
170 ('p', 'patch', None, _('show patch')),
170 ('g', 'git', None, _('use git extended diff format')),
171 ('g', 'git', None, _('use git extended diff format')),
171 ('l', 'limit', '',
172 ('l', 'limit', '',
172 _('limit number of changes displayed'), _('NUM')),
173 _('limit number of changes displayed'), _('NUM')),
173 ('M', 'no-merges', None, _('do not show merges')),
174 ('M', 'no-merges', None, _('do not show merges')),
174 ('', 'stat', None, _('output diffstat-style summary of changes')),
175 ('', 'stat', None, _('output diffstat-style summary of changes')),
175 ('G', 'graph', None, _("show the revision DAG")),
176 ('G', 'graph', None, _("show the revision DAG")),
176 ] + templateopts
177 ] + templateopts
177
178
178 diffopts = [
179 diffopts = [
179 ('a', 'text', None, _('treat all files as text')),
180 ('a', 'text', None, _('treat all files as text')),
180 ('g', 'git', None, _('use git extended diff format')),
181 ('g', 'git', None, _('use git extended diff format')),
181 ('', 'nodates', None, _('omit dates from diff headers'))
182 ('', 'nodates', None, _('omit dates from diff headers'))
182 ]
183 ]
183
184
184 diffwsopts = [
185 diffwsopts = [
185 ('w', 'ignore-all-space', None,
186 ('w', 'ignore-all-space', None,
186 _('ignore white space when comparing lines')),
187 _('ignore white space when comparing lines')),
187 ('b', 'ignore-space-change', None,
188 ('b', 'ignore-space-change', None,
188 _('ignore changes in the amount of white space')),
189 _('ignore changes in the amount of white space')),
189 ('B', 'ignore-blank-lines', None,
190 ('B', 'ignore-blank-lines', None,
190 _('ignore changes whose lines are all blank')),
191 _('ignore changes whose lines are all blank')),
191 ]
192 ]
192
193
193 diffopts2 = [
194 diffopts2 = [
194 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
195 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
195 ('p', 'show-function', None, _('show which function each change is in')),
196 ('p', 'show-function', None, _('show which function each change is in')),
196 ('', 'reverse', None, _('produce a diff that undoes the changes')),
197 ('', 'reverse', None, _('produce a diff that undoes the changes')),
197 ] + diffwsopts + [
198 ] + diffwsopts + [
198 ('U', 'unified', '',
199 ('U', 'unified', '',
199 _('number of lines of context to show'), _('NUM')),
200 _('number of lines of context to show'), _('NUM')),
200 ('', 'stat', None, _('output diffstat-style summary of changes')),
201 ('', 'stat', None, _('output diffstat-style summary of changes')),
201 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
202 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
202 ]
203 ]
203
204
204 mergetoolopts = [
205 mergetoolopts = [
205 ('t', 'tool', '', _('specify merge tool')),
206 ('t', 'tool', '', _('specify merge tool')),
206 ]
207 ]
207
208
208 similarityopts = [
209 similarityopts = [
209 ('s', 'similarity', '',
210 ('s', 'similarity', '',
210 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
211 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
211 ]
212 ]
212
213
213 subrepoopts = [
214 subrepoopts = [
214 ('S', 'subrepos', None,
215 ('S', 'subrepos', None,
215 _('recurse into subrepositories'))
216 _('recurse into subrepositories'))
216 ]
217 ]
217
218
218 debugrevlogopts = [
219 debugrevlogopts = [
219 ('c', 'changelog', False, _('open changelog')),
220 ('c', 'changelog', False, _('open changelog')),
220 ('m', 'manifest', False, _('open manifest')),
221 ('m', 'manifest', False, _('open manifest')),
221 ('', 'dir', '', _('open directory manifest')),
222 ('', 'dir', '', _('open directory manifest')),
222 ]
223 ]
223
224
224 # Commands start here, listed alphabetically
225 # Commands start here, listed alphabetically
225
226
226 @command('^add',
227 @command('^add',
227 walkopts + subrepoopts + dryrunopts,
228 walkopts + subrepoopts + dryrunopts,
228 _('[OPTION]... [FILE]...'),
229 _('[OPTION]... [FILE]...'),
229 inferrepo=True)
230 inferrepo=True)
230 def add(ui, repo, *pats, **opts):
231 def add(ui, repo, *pats, **opts):
231 """add the specified files on the next commit
232 """add the specified files on the next commit
232
233
233 Schedule files to be version controlled and added to the
234 Schedule files to be version controlled and added to the
234 repository.
235 repository.
235
236
236 The files will be added to the repository at the next commit. To
237 The files will be added to the repository at the next commit. To
237 undo an add before that, see :hg:`forget`.
238 undo an add before that, see :hg:`forget`.
238
239
239 If no names are given, add all files to the repository (except
240 If no names are given, add all files to the repository (except
240 files matching ``.hgignore``).
241 files matching ``.hgignore``).
241
242
242 .. container:: verbose
243 .. container:: verbose
243
244
244 Examples:
245 Examples:
245
246
246 - New (unknown) files are added
247 - New (unknown) files are added
247 automatically by :hg:`add`::
248 automatically by :hg:`add`::
248
249
249 $ ls
250 $ ls
250 foo.c
251 foo.c
251 $ hg status
252 $ hg status
252 ? foo.c
253 ? foo.c
253 $ hg add
254 $ hg add
254 adding foo.c
255 adding foo.c
255 $ hg status
256 $ hg status
256 A foo.c
257 A foo.c
257
258
258 - Specific files to be added can be specified::
259 - Specific files to be added can be specified::
259
260
260 $ ls
261 $ ls
261 bar.c foo.c
262 bar.c foo.c
262 $ hg status
263 $ hg status
263 ? bar.c
264 ? bar.c
264 ? foo.c
265 ? foo.c
265 $ hg add bar.c
266 $ hg add bar.c
266 $ hg status
267 $ hg status
267 A bar.c
268 A bar.c
268 ? foo.c
269 ? foo.c
269
270
270 Returns 0 if all files are successfully added.
271 Returns 0 if all files are successfully added.
271 """
272 """
272
273
273 m = scmutil.match(repo[None], pats, opts)
274 m = scmutil.match(repo[None], pats, opts)
274 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
275 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
275 return rejected and 1 or 0
276 return rejected and 1 or 0
276
277
277 @command('addremove',
278 @command('addremove',
278 similarityopts + subrepoopts + walkopts + dryrunopts,
279 similarityopts + subrepoopts + walkopts + dryrunopts,
279 _('[OPTION]... [FILE]...'),
280 _('[OPTION]... [FILE]...'),
280 inferrepo=True)
281 inferrepo=True)
281 def addremove(ui, repo, *pats, **opts):
282 def addremove(ui, repo, *pats, **opts):
282 """add all new files, delete all missing files
283 """add all new files, delete all missing files
283
284
284 Add all new files and remove all missing files from the
285 Add all new files and remove all missing files from the
285 repository.
286 repository.
286
287
287 Unless names are given, new files are ignored if they match any of
288 Unless names are given, new files are ignored if they match any of
288 the patterns in ``.hgignore``. As with add, these changes take
289 the patterns in ``.hgignore``. As with add, these changes take
289 effect at the next commit.
290 effect at the next commit.
290
291
291 Use the -s/--similarity option to detect renamed files. This
292 Use the -s/--similarity option to detect renamed files. This
292 option takes a percentage between 0 (disabled) and 100 (files must
293 option takes a percentage between 0 (disabled) and 100 (files must
293 be identical) as its parameter. With a parameter greater than 0,
294 be identical) as its parameter. With a parameter greater than 0,
294 this compares every removed file with every added file and records
295 this compares every removed file with every added file and records
295 those similar enough as renames. Detecting renamed files this way
296 those similar enough as renames. Detecting renamed files this way
296 can be expensive. After using this option, :hg:`status -C` can be
297 can be expensive. After using this option, :hg:`status -C` can be
297 used to check which files were identified as moved or renamed. If
298 used to check which files were identified as moved or renamed. If
298 not specified, -s/--similarity defaults to 100 and only renames of
299 not specified, -s/--similarity defaults to 100 and only renames of
299 identical files are detected.
300 identical files are detected.
300
301
301 .. container:: verbose
302 .. container:: verbose
302
303
303 Examples:
304 Examples:
304
305
305 - A number of files (bar.c and foo.c) are new,
306 - A number of files (bar.c and foo.c) are new,
306 while foobar.c has been removed (without using :hg:`remove`)
307 while foobar.c has been removed (without using :hg:`remove`)
307 from the repository::
308 from the repository::
308
309
309 $ ls
310 $ ls
310 bar.c foo.c
311 bar.c foo.c
311 $ hg status
312 $ hg status
312 ! foobar.c
313 ! foobar.c
313 ? bar.c
314 ? bar.c
314 ? foo.c
315 ? foo.c
315 $ hg addremove
316 $ hg addremove
316 adding bar.c
317 adding bar.c
317 adding foo.c
318 adding foo.c
318 removing foobar.c
319 removing foobar.c
319 $ hg status
320 $ hg status
320 A bar.c
321 A bar.c
321 A foo.c
322 A foo.c
322 R foobar.c
323 R foobar.c
323
324
324 - A file foobar.c was moved to foo.c without using :hg:`rename`.
325 - A file foobar.c was moved to foo.c without using :hg:`rename`.
325 Afterwards, it was edited slightly::
326 Afterwards, it was edited slightly::
326
327
327 $ ls
328 $ ls
328 foo.c
329 foo.c
329 $ hg status
330 $ hg status
330 ! foobar.c
331 ! foobar.c
331 ? foo.c
332 ? foo.c
332 $ hg addremove --similarity 90
333 $ hg addremove --similarity 90
333 removing foobar.c
334 removing foobar.c
334 adding foo.c
335 adding foo.c
335 recording removal of foobar.c as rename to foo.c (94% similar)
336 recording removal of foobar.c as rename to foo.c (94% similar)
336 $ hg status -C
337 $ hg status -C
337 A foo.c
338 A foo.c
338 foobar.c
339 foobar.c
339 R foobar.c
340 R foobar.c
340
341
341 Returns 0 if all files are successfully added.
342 Returns 0 if all files are successfully added.
342 """
343 """
343 try:
344 try:
344 sim = float(opts.get('similarity') or 100)
345 sim = float(opts.get('similarity') or 100)
345 except ValueError:
346 except ValueError:
346 raise error.Abort(_('similarity must be a number'))
347 raise error.Abort(_('similarity must be a number'))
347 if sim < 0 or sim > 100:
348 if sim < 0 or sim > 100:
348 raise error.Abort(_('similarity must be between 0 and 100'))
349 raise error.Abort(_('similarity must be between 0 and 100'))
349 matcher = scmutil.match(repo[None], pats, opts)
350 matcher = scmutil.match(repo[None], pats, opts)
350 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
351 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
351
352
352 @command('^annotate|blame',
353 @command('^annotate|blame',
353 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
354 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
354 ('', 'follow', None,
355 ('', 'follow', None,
355 _('follow copies/renames and list the filename (DEPRECATED)')),
356 _('follow copies/renames and list the filename (DEPRECATED)')),
356 ('', 'no-follow', None, _("don't follow copies and renames")),
357 ('', 'no-follow', None, _("don't follow copies and renames")),
357 ('a', 'text', None, _('treat all files as text')),
358 ('a', 'text', None, _('treat all files as text')),
358 ('u', 'user', None, _('list the author (long with -v)')),
359 ('u', 'user', None, _('list the author (long with -v)')),
359 ('f', 'file', None, _('list the filename')),
360 ('f', 'file', None, _('list the filename')),
360 ('d', 'date', None, _('list the date (short with -q)')),
361 ('d', 'date', None, _('list the date (short with -q)')),
361 ('n', 'number', None, _('list the revision number (default)')),
362 ('n', 'number', None, _('list the revision number (default)')),
362 ('c', 'changeset', None, _('list the changeset')),
363 ('c', 'changeset', None, _('list the changeset')),
363 ('l', 'line-number', None, _('show line number at the first appearance'))
364 ('l', 'line-number', None, _('show line number at the first appearance'))
364 ] + diffwsopts + walkopts + formatteropts,
365 ] + diffwsopts + walkopts + formatteropts,
365 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
366 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
366 inferrepo=True)
367 inferrepo=True)
367 def annotate(ui, repo, *pats, **opts):
368 def annotate(ui, repo, *pats, **opts):
368 """show changeset information by line for each file
369 """show changeset information by line for each file
369
370
370 List changes in files, showing the revision id responsible for
371 List changes in files, showing the revision id responsible for
371 each line.
372 each line.
372
373
373 This command is useful for discovering when a change was made and
374 This command is useful for discovering when a change was made and
374 by whom.
375 by whom.
375
376
376 If you include --file, --user, or --date, the revision number is
377 If you include --file, --user, or --date, the revision number is
377 suppressed unless you also include --number.
378 suppressed unless you also include --number.
378
379
379 Without the -a/--text option, annotate will avoid processing files
380 Without the -a/--text option, annotate will avoid processing files
380 it detects as binary. With -a, annotate will annotate the file
381 it detects as binary. With -a, annotate will annotate the file
381 anyway, although the results will probably be neither useful
382 anyway, although the results will probably be neither useful
382 nor desirable.
383 nor desirable.
383
384
384 Returns 0 on success.
385 Returns 0 on success.
385 """
386 """
386 if not pats:
387 if not pats:
387 raise error.Abort(_('at least one filename or pattern is required'))
388 raise error.Abort(_('at least one filename or pattern is required'))
388
389
389 if opts.get('follow'):
390 if opts.get('follow'):
390 # --follow is deprecated and now just an alias for -f/--file
391 # --follow is deprecated and now just an alias for -f/--file
391 # to mimic the behavior of Mercurial before version 1.5
392 # to mimic the behavior of Mercurial before version 1.5
392 opts['file'] = True
393 opts['file'] = True
393
394
394 ctx = scmutil.revsingle(repo, opts.get('rev'))
395 ctx = scmutil.revsingle(repo, opts.get('rev'))
395
396
396 fm = ui.formatter('annotate', opts)
397 fm = ui.formatter('annotate', opts)
397 if ui.quiet:
398 if ui.quiet:
398 datefunc = util.shortdate
399 datefunc = util.shortdate
399 else:
400 else:
400 datefunc = util.datestr
401 datefunc = util.datestr
401 if ctx.rev() is None:
402 if ctx.rev() is None:
402 def hexfn(node):
403 def hexfn(node):
403 if node is None:
404 if node is None:
404 return None
405 return None
405 else:
406 else:
406 return fm.hexfunc(node)
407 return fm.hexfunc(node)
407 if opts.get('changeset'):
408 if opts.get('changeset'):
408 # omit "+" suffix which is appended to node hex
409 # omit "+" suffix which is appended to node hex
409 def formatrev(rev):
410 def formatrev(rev):
410 if rev is None:
411 if rev is None:
411 return '%d' % ctx.p1().rev()
412 return '%d' % ctx.p1().rev()
412 else:
413 else:
413 return '%d' % rev
414 return '%d' % rev
414 else:
415 else:
415 def formatrev(rev):
416 def formatrev(rev):
416 if rev is None:
417 if rev is None:
417 return '%d+' % ctx.p1().rev()
418 return '%d+' % ctx.p1().rev()
418 else:
419 else:
419 return '%d ' % rev
420 return '%d ' % rev
420 def formathex(hex):
421 def formathex(hex):
421 if hex is None:
422 if hex is None:
422 return '%s+' % fm.hexfunc(ctx.p1().node())
423 return '%s+' % fm.hexfunc(ctx.p1().node())
423 else:
424 else:
424 return '%s ' % hex
425 return '%s ' % hex
425 else:
426 else:
426 hexfn = fm.hexfunc
427 hexfn = fm.hexfunc
427 formatrev = formathex = str
428 formatrev = formathex = str
428
429
429 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
430 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
430 ('number', ' ', lambda x: x[0].rev(), formatrev),
431 ('number', ' ', lambda x: x[0].rev(), formatrev),
431 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
432 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
432 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
433 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
433 ('file', ' ', lambda x: x[0].path(), str),
434 ('file', ' ', lambda x: x[0].path(), str),
434 ('line_number', ':', lambda x: x[1], str),
435 ('line_number', ':', lambda x: x[1], str),
435 ]
436 ]
436 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
437 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
437
438
438 if (not opts.get('user') and not opts.get('changeset')
439 if (not opts.get('user') and not opts.get('changeset')
439 and not opts.get('date') and not opts.get('file')):
440 and not opts.get('date') and not opts.get('file')):
440 opts['number'] = True
441 opts['number'] = True
441
442
442 linenumber = opts.get('line_number') is not None
443 linenumber = opts.get('line_number') is not None
443 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
444 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
444 raise error.Abort(_('at least one of -n/-c is required for -l'))
445 raise error.Abort(_('at least one of -n/-c is required for -l'))
445
446
446 if fm.isplain():
447 if fm.isplain():
447 def makefunc(get, fmt):
448 def makefunc(get, fmt):
448 return lambda x: fmt(get(x))
449 return lambda x: fmt(get(x))
449 else:
450 else:
450 def makefunc(get, fmt):
451 def makefunc(get, fmt):
451 return get
452 return get
452 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
453 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
453 if opts.get(op)]
454 if opts.get(op)]
454 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
455 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
455 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
456 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
456 if opts.get(op))
457 if opts.get(op))
457
458
458 def bad(x, y):
459 def bad(x, y):
459 raise error.Abort("%s: %s" % (x, y))
460 raise error.Abort("%s: %s" % (x, y))
460
461
461 m = scmutil.match(ctx, pats, opts, badfn=bad)
462 m = scmutil.match(ctx, pats, opts, badfn=bad)
462
463
463 follow = not opts.get('no_follow')
464 follow = not opts.get('no_follow')
464 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
465 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
465 whitespace=True)
466 whitespace=True)
466 for abs in ctx.walk(m):
467 for abs in ctx.walk(m):
467 fctx = ctx[abs]
468 fctx = ctx[abs]
468 if not opts.get('text') and util.binary(fctx.data()):
469 if not opts.get('text') and util.binary(fctx.data()):
469 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
470 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
470 continue
471 continue
471
472
472 lines = fctx.annotate(follow=follow, linenumber=linenumber,
473 lines = fctx.annotate(follow=follow, linenumber=linenumber,
473 diffopts=diffopts)
474 diffopts=diffopts)
474 if not lines:
475 if not lines:
475 continue
476 continue
476 formats = []
477 formats = []
477 pieces = []
478 pieces = []
478
479
479 for f, sep in funcmap:
480 for f, sep in funcmap:
480 l = [f(n) for n, dummy in lines]
481 l = [f(n) for n, dummy in lines]
481 if fm.isplain():
482 if fm.isplain():
482 sizes = [encoding.colwidth(x) for x in l]
483 sizes = [encoding.colwidth(x) for x in l]
483 ml = max(sizes)
484 ml = max(sizes)
484 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
485 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
485 else:
486 else:
486 formats.append(['%s' for x in l])
487 formats.append(['%s' for x in l])
487 pieces.append(l)
488 pieces.append(l)
488
489
489 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
490 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
490 fm.startitem()
491 fm.startitem()
491 fm.write(fields, "".join(f), *p)
492 fm.write(fields, "".join(f), *p)
492 fm.write('line', ": %s", l[1])
493 fm.write('line', ": %s", l[1])
493
494
494 if not lines[-1][1].endswith('\n'):
495 if not lines[-1][1].endswith('\n'):
495 fm.plain('\n')
496 fm.plain('\n')
496
497
497 fm.end()
498 fm.end()
498
499
499 @command('archive',
500 @command('archive',
500 [('', 'no-decode', None, _('do not pass files through decoders')),
501 [('', 'no-decode', None, _('do not pass files through decoders')),
501 ('p', 'prefix', '', _('directory prefix for files in archive'),
502 ('p', 'prefix', '', _('directory prefix for files in archive'),
502 _('PREFIX')),
503 _('PREFIX')),
503 ('r', 'rev', '', _('revision to distribute'), _('REV')),
504 ('r', 'rev', '', _('revision to distribute'), _('REV')),
504 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
505 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
505 ] + subrepoopts + walkopts,
506 ] + subrepoopts + walkopts,
506 _('[OPTION]... DEST'))
507 _('[OPTION]... DEST'))
507 def archive(ui, repo, dest, **opts):
508 def archive(ui, repo, dest, **opts):
508 '''create an unversioned archive of a repository revision
509 '''create an unversioned archive of a repository revision
509
510
510 By default, the revision used is the parent of the working
511 By default, the revision used is the parent of the working
511 directory; use -r/--rev to specify a different revision.
512 directory; use -r/--rev to specify a different revision.
512
513
513 The archive type is automatically detected based on file
514 The archive type is automatically detected based on file
514 extension (to override, use -t/--type).
515 extension (to override, use -t/--type).
515
516
516 .. container:: verbose
517 .. container:: verbose
517
518
518 Examples:
519 Examples:
519
520
520 - create a zip file containing the 1.0 release::
521 - create a zip file containing the 1.0 release::
521
522
522 hg archive -r 1.0 project-1.0.zip
523 hg archive -r 1.0 project-1.0.zip
523
524
524 - create a tarball excluding .hg files::
525 - create a tarball excluding .hg files::
525
526
526 hg archive project.tar.gz -X ".hg*"
527 hg archive project.tar.gz -X ".hg*"
527
528
528 Valid types are:
529 Valid types are:
529
530
530 :``files``: a directory full of files (default)
531 :``files``: a directory full of files (default)
531 :``tar``: tar archive, uncompressed
532 :``tar``: tar archive, uncompressed
532 :``tbz2``: tar archive, compressed using bzip2
533 :``tbz2``: tar archive, compressed using bzip2
533 :``tgz``: tar archive, compressed using gzip
534 :``tgz``: tar archive, compressed using gzip
534 :``uzip``: zip archive, uncompressed
535 :``uzip``: zip archive, uncompressed
535 :``zip``: zip archive, compressed using deflate
536 :``zip``: zip archive, compressed using deflate
536
537
537 The exact name of the destination archive or directory is given
538 The exact name of the destination archive or directory is given
538 using a format string; see :hg:`help export` for details.
539 using a format string; see :hg:`help export` for details.
539
540
540 Each member added to an archive file has a directory prefix
541 Each member added to an archive file has a directory prefix
541 prepended. Use -p/--prefix to specify a format string for the
542 prepended. Use -p/--prefix to specify a format string for the
542 prefix. The default is the basename of the archive, with suffixes
543 prefix. The default is the basename of the archive, with suffixes
543 removed.
544 removed.
544
545
545 Returns 0 on success.
546 Returns 0 on success.
546 '''
547 '''
547
548
548 ctx = scmutil.revsingle(repo, opts.get('rev'))
549 ctx = scmutil.revsingle(repo, opts.get('rev'))
549 if not ctx:
550 if not ctx:
550 raise error.Abort(_('no working directory: please specify a revision'))
551 raise error.Abort(_('no working directory: please specify a revision'))
551 node = ctx.node()
552 node = ctx.node()
552 dest = cmdutil.makefilename(repo, dest, node)
553 dest = cmdutil.makefilename(repo, dest, node)
553 if os.path.realpath(dest) == repo.root:
554 if os.path.realpath(dest) == repo.root:
554 raise error.Abort(_('repository root cannot be destination'))
555 raise error.Abort(_('repository root cannot be destination'))
555
556
556 kind = opts.get('type') or archival.guesskind(dest) or 'files'
557 kind = opts.get('type') or archival.guesskind(dest) or 'files'
557 prefix = opts.get('prefix')
558 prefix = opts.get('prefix')
558
559
559 if dest == '-':
560 if dest == '-':
560 if kind == 'files':
561 if kind == 'files':
561 raise error.Abort(_('cannot archive plain files to stdout'))
562 raise error.Abort(_('cannot archive plain files to stdout'))
562 dest = cmdutil.makefileobj(repo, dest)
563 dest = cmdutil.makefileobj(repo, dest)
563 if not prefix:
564 if not prefix:
564 prefix = os.path.basename(repo.root) + '-%h'
565 prefix = os.path.basename(repo.root) + '-%h'
565
566
566 prefix = cmdutil.makefilename(repo, prefix, node)
567 prefix = cmdutil.makefilename(repo, prefix, node)
567 matchfn = scmutil.match(ctx, [], opts)
568 matchfn = scmutil.match(ctx, [], opts)
568 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
569 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
569 matchfn, prefix, subrepos=opts.get('subrepos'))
570 matchfn, prefix, subrepos=opts.get('subrepos'))
570
571
571 @command('backout',
572 @command('backout',
572 [('', 'merge', None, _('merge with old dirstate parent after backout')),
573 [('', 'merge', None, _('merge with old dirstate parent after backout')),
573 ('', 'commit', None,
574 ('', 'commit', None,
574 _('commit if no conflicts were encountered (DEPRECATED)')),
575 _('commit if no conflicts were encountered (DEPRECATED)')),
575 ('', 'no-commit', None, _('do not commit')),
576 ('', 'no-commit', None, _('do not commit')),
576 ('', 'parent', '',
577 ('', 'parent', '',
577 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
578 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
578 ('r', 'rev', '', _('revision to backout'), _('REV')),
579 ('r', 'rev', '', _('revision to backout'), _('REV')),
579 ('e', 'edit', False, _('invoke editor on commit messages')),
580 ('e', 'edit', False, _('invoke editor on commit messages')),
580 ] + mergetoolopts + walkopts + commitopts + commitopts2,
581 ] + mergetoolopts + walkopts + commitopts + commitopts2,
581 _('[OPTION]... [-r] REV'))
582 _('[OPTION]... [-r] REV'))
582 def backout(ui, repo, node=None, rev=None, **opts):
583 def backout(ui, repo, node=None, rev=None, **opts):
583 '''reverse effect of earlier changeset
584 '''reverse effect of earlier changeset
584
585
585 Prepare a new changeset with the effect of REV undone in the
586 Prepare a new changeset with the effect of REV undone in the
586 current working directory. If no conflicts were encountered,
587 current working directory. If no conflicts were encountered,
587 it will be committed immediately.
588 it will be committed immediately.
588
589
589 If REV is the parent of the working directory, then this new changeset
590 If REV is the parent of the working directory, then this new changeset
590 is committed automatically (unless --no-commit is specified).
591 is committed automatically (unless --no-commit is specified).
591
592
592 .. note::
593 .. note::
593
594
594 :hg:`backout` cannot be used to fix either an unwanted or
595 :hg:`backout` cannot be used to fix either an unwanted or
595 incorrect merge.
596 incorrect merge.
596
597
597 .. container:: verbose
598 .. container:: verbose
598
599
599 Examples:
600 Examples:
600
601
601 - Reverse the effect of the parent of the working directory.
602 - Reverse the effect of the parent of the working directory.
602 This backout will be committed immediately::
603 This backout will be committed immediately::
603
604
604 hg backout -r .
605 hg backout -r .
605
606
606 - Reverse the effect of previous bad revision 23::
607 - Reverse the effect of previous bad revision 23::
607
608
608 hg backout -r 23
609 hg backout -r 23
609
610
610 - Reverse the effect of previous bad revision 23 and
611 - Reverse the effect of previous bad revision 23 and
611 leave changes uncommitted::
612 leave changes uncommitted::
612
613
613 hg backout -r 23 --no-commit
614 hg backout -r 23 --no-commit
614 hg commit -m "Backout revision 23"
615 hg commit -m "Backout revision 23"
615
616
616 By default, the pending changeset will have one parent,
617 By default, the pending changeset will have one parent,
617 maintaining a linear history. With --merge, the pending
618 maintaining a linear history. With --merge, the pending
618 changeset will instead have two parents: the old parent of the
619 changeset will instead have two parents: the old parent of the
619 working directory and a new child of REV that simply undoes REV.
620 working directory and a new child of REV that simply undoes REV.
620
621
621 Before version 1.7, the behavior without --merge was equivalent
622 Before version 1.7, the behavior without --merge was equivalent
622 to specifying --merge followed by :hg:`update --clean .` to
623 to specifying --merge followed by :hg:`update --clean .` to
623 cancel the merge and leave the child of REV as a head to be
624 cancel the merge and leave the child of REV as a head to be
624 merged separately.
625 merged separately.
625
626
626 See :hg:`help dates` for a list of formats valid for -d/--date.
627 See :hg:`help dates` for a list of formats valid for -d/--date.
627
628
628 See :hg:`help revert` for a way to restore files to the state
629 See :hg:`help revert` for a way to restore files to the state
629 of another revision.
630 of another revision.
630
631
631 Returns 0 on success, 1 if nothing to backout or there are unresolved
632 Returns 0 on success, 1 if nothing to backout or there are unresolved
632 files.
633 files.
633 '''
634 '''
634 wlock = lock = None
635 wlock = lock = None
635 try:
636 try:
636 wlock = repo.wlock()
637 wlock = repo.wlock()
637 lock = repo.lock()
638 lock = repo.lock()
638 return _dobackout(ui, repo, node, rev, **opts)
639 return _dobackout(ui, repo, node, rev, **opts)
639 finally:
640 finally:
640 release(lock, wlock)
641 release(lock, wlock)
641
642
642 def _dobackout(ui, repo, node=None, rev=None, **opts):
643 def _dobackout(ui, repo, node=None, rev=None, **opts):
643 if opts.get('commit') and opts.get('no_commit'):
644 if opts.get('commit') and opts.get('no_commit'):
644 raise error.Abort(_("cannot use --commit with --no-commit"))
645 raise error.Abort(_("cannot use --commit with --no-commit"))
645 if opts.get('merge') and opts.get('no_commit'):
646 if opts.get('merge') and opts.get('no_commit'):
646 raise error.Abort(_("cannot use --merge with --no-commit"))
647 raise error.Abort(_("cannot use --merge with --no-commit"))
647
648
648 if rev and node:
649 if rev and node:
649 raise error.Abort(_("please specify just one revision"))
650 raise error.Abort(_("please specify just one revision"))
650
651
651 if not rev:
652 if not rev:
652 rev = node
653 rev = node
653
654
654 if not rev:
655 if not rev:
655 raise error.Abort(_("please specify a revision to backout"))
656 raise error.Abort(_("please specify a revision to backout"))
656
657
657 date = opts.get('date')
658 date = opts.get('date')
658 if date:
659 if date:
659 opts['date'] = util.parsedate(date)
660 opts['date'] = util.parsedate(date)
660
661
661 cmdutil.checkunfinished(repo)
662 cmdutil.checkunfinished(repo)
662 cmdutil.bailifchanged(repo)
663 cmdutil.bailifchanged(repo)
663 node = scmutil.revsingle(repo, rev).node()
664 node = scmutil.revsingle(repo, rev).node()
664
665
665 op1, op2 = repo.dirstate.parents()
666 op1, op2 = repo.dirstate.parents()
666 if not repo.changelog.isancestor(node, op1):
667 if not repo.changelog.isancestor(node, op1):
667 raise error.Abort(_('cannot backout change that is not an ancestor'))
668 raise error.Abort(_('cannot backout change that is not an ancestor'))
668
669
669 p1, p2 = repo.changelog.parents(node)
670 p1, p2 = repo.changelog.parents(node)
670 if p1 == nullid:
671 if p1 == nullid:
671 raise error.Abort(_('cannot backout a change with no parents'))
672 raise error.Abort(_('cannot backout a change with no parents'))
672 if p2 != nullid:
673 if p2 != nullid:
673 if not opts.get('parent'):
674 if not opts.get('parent'):
674 raise error.Abort(_('cannot backout a merge changeset'))
675 raise error.Abort(_('cannot backout a merge changeset'))
675 p = repo.lookup(opts['parent'])
676 p = repo.lookup(opts['parent'])
676 if p not in (p1, p2):
677 if p not in (p1, p2):
677 raise error.Abort(_('%s is not a parent of %s') %
678 raise error.Abort(_('%s is not a parent of %s') %
678 (short(p), short(node)))
679 (short(p), short(node)))
679 parent = p
680 parent = p
680 else:
681 else:
681 if opts.get('parent'):
682 if opts.get('parent'):
682 raise error.Abort(_('cannot use --parent on non-merge changeset'))
683 raise error.Abort(_('cannot use --parent on non-merge changeset'))
683 parent = p1
684 parent = p1
684
685
685 # the backout should appear on the same branch
686 # the backout should appear on the same branch
686 branch = repo.dirstate.branch()
687 branch = repo.dirstate.branch()
687 bheads = repo.branchheads(branch)
688 bheads = repo.branchheads(branch)
688 rctx = scmutil.revsingle(repo, hex(parent))
689 rctx = scmutil.revsingle(repo, hex(parent))
689 if not opts.get('merge') and op1 != node:
690 if not opts.get('merge') and op1 != node:
690 dsguard = dirstateguard.dirstateguard(repo, 'backout')
691 dsguard = dirstateguard.dirstateguard(repo, 'backout')
691 try:
692 try:
692 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
693 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
693 'backout')
694 'backout')
694 stats = mergemod.update(repo, parent, True, True, node, False)
695 stats = mergemod.update(repo, parent, True, True, node, False)
695 repo.setparents(op1, op2)
696 repo.setparents(op1, op2)
696 dsguard.close()
697 dsguard.close()
697 hg._showstats(repo, stats)
698 hg._showstats(repo, stats)
698 if stats[3]:
699 if stats[3]:
699 repo.ui.status(_("use 'hg resolve' to retry unresolved "
700 repo.ui.status(_("use 'hg resolve' to retry unresolved "
700 "file merges\n"))
701 "file merges\n"))
701 return 1
702 return 1
702 finally:
703 finally:
703 ui.setconfig('ui', 'forcemerge', '', '')
704 ui.setconfig('ui', 'forcemerge', '', '')
704 lockmod.release(dsguard)
705 lockmod.release(dsguard)
705 else:
706 else:
706 hg.clean(repo, node, show_stats=False)
707 hg.clean(repo, node, show_stats=False)
707 repo.dirstate.setbranch(branch)
708 repo.dirstate.setbranch(branch)
708 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
709 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
709
710
710 if opts.get('no_commit'):
711 if opts.get('no_commit'):
711 msg = _("changeset %s backed out, "
712 msg = _("changeset %s backed out, "
712 "don't forget to commit.\n")
713 "don't forget to commit.\n")
713 ui.status(msg % short(node))
714 ui.status(msg % short(node))
714 return 0
715 return 0
715
716
716 def commitfunc(ui, repo, message, match, opts):
717 def commitfunc(ui, repo, message, match, opts):
717 editform = 'backout'
718 editform = 'backout'
718 e = cmdutil.getcommiteditor(editform=editform, **opts)
719 e = cmdutil.getcommiteditor(editform=editform, **opts)
719 if not message:
720 if not message:
720 # we don't translate commit messages
721 # we don't translate commit messages
721 message = "Backed out changeset %s" % short(node)
722 message = "Backed out changeset %s" % short(node)
722 e = cmdutil.getcommiteditor(edit=True, editform=editform)
723 e = cmdutil.getcommiteditor(edit=True, editform=editform)
723 return repo.commit(message, opts.get('user'), opts.get('date'),
724 return repo.commit(message, opts.get('user'), opts.get('date'),
724 match, editor=e)
725 match, editor=e)
725 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
726 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
726 if not newnode:
727 if not newnode:
727 ui.status(_("nothing changed\n"))
728 ui.status(_("nothing changed\n"))
728 return 1
729 return 1
729 cmdutil.commitstatus(repo, newnode, branch, bheads)
730 cmdutil.commitstatus(repo, newnode, branch, bheads)
730
731
731 def nice(node):
732 def nice(node):
732 return '%d:%s' % (repo.changelog.rev(node), short(node))
733 return '%d:%s' % (repo.changelog.rev(node), short(node))
733 ui.status(_('changeset %s backs out changeset %s\n') %
734 ui.status(_('changeset %s backs out changeset %s\n') %
734 (nice(repo.changelog.tip()), nice(node)))
735 (nice(repo.changelog.tip()), nice(node)))
735 if opts.get('merge') and op1 != node:
736 if opts.get('merge') and op1 != node:
736 hg.clean(repo, op1, show_stats=False)
737 hg.clean(repo, op1, show_stats=False)
737 ui.status(_('merging with changeset %s\n')
738 ui.status(_('merging with changeset %s\n')
738 % nice(repo.changelog.tip()))
739 % nice(repo.changelog.tip()))
739 try:
740 try:
740 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
741 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
741 'backout')
742 'backout')
742 return hg.merge(repo, hex(repo.changelog.tip()))
743 return hg.merge(repo, hex(repo.changelog.tip()))
743 finally:
744 finally:
744 ui.setconfig('ui', 'forcemerge', '', '')
745 ui.setconfig('ui', 'forcemerge', '', '')
745 return 0
746 return 0
746
747
747 @command('bisect',
748 @command('bisect',
748 [('r', 'reset', False, _('reset bisect state')),
749 [('r', 'reset', False, _('reset bisect state')),
749 ('g', 'good', False, _('mark changeset good')),
750 ('g', 'good', False, _('mark changeset good')),
750 ('b', 'bad', False, _('mark changeset bad')),
751 ('b', 'bad', False, _('mark changeset bad')),
751 ('s', 'skip', False, _('skip testing changeset')),
752 ('s', 'skip', False, _('skip testing changeset')),
752 ('e', 'extend', False, _('extend the bisect range')),
753 ('e', 'extend', False, _('extend the bisect range')),
753 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
754 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
754 ('U', 'noupdate', False, _('do not update to target'))],
755 ('U', 'noupdate', False, _('do not update to target'))],
755 _("[-gbsr] [-U] [-c CMD] [REV]"))
756 _("[-gbsr] [-U] [-c CMD] [REV]"))
756 def bisect(ui, repo, rev=None, extra=None, command=None,
757 def bisect(ui, repo, rev=None, extra=None, command=None,
757 reset=None, good=None, bad=None, skip=None, extend=None,
758 reset=None, good=None, bad=None, skip=None, extend=None,
758 noupdate=None):
759 noupdate=None):
759 """subdivision search of changesets
760 """subdivision search of changesets
760
761
761 This command helps to find changesets which introduce problems. To
762 This command helps to find changesets which introduce problems. To
762 use, mark the earliest changeset you know exhibits the problem as
763 use, mark the earliest changeset you know exhibits the problem as
763 bad, then mark the latest changeset which is free from the problem
764 bad, then mark the latest changeset which is free from the problem
764 as good. Bisect will update your working directory to a revision
765 as good. Bisect will update your working directory to a revision
765 for testing (unless the -U/--noupdate option is specified). Once
766 for testing (unless the -U/--noupdate option is specified). Once
766 you have performed tests, mark the working directory as good or
767 you have performed tests, mark the working directory as good or
767 bad, and bisect will either update to another candidate changeset
768 bad, and bisect will either update to another candidate changeset
768 or announce that it has found the bad revision.
769 or announce that it has found the bad revision.
769
770
770 As a shortcut, you can also use the revision argument to mark a
771 As a shortcut, you can also use the revision argument to mark a
771 revision as good or bad without checking it out first.
772 revision as good or bad without checking it out first.
772
773
773 If you supply a command, it will be used for automatic bisection.
774 If you supply a command, it will be used for automatic bisection.
774 The environment variable HG_NODE will contain the ID of the
775 The environment variable HG_NODE will contain the ID of the
775 changeset being tested. The exit status of the command will be
776 changeset being tested. The exit status of the command will be
776 used to mark revisions as good or bad: status 0 means good, 125
777 used to mark revisions as good or bad: status 0 means good, 125
777 means to skip the revision, 127 (command not found) will abort the
778 means to skip the revision, 127 (command not found) will abort the
778 bisection, and any other non-zero exit status means the revision
779 bisection, and any other non-zero exit status means the revision
779 is bad.
780 is bad.
780
781
781 .. container:: verbose
782 .. container:: verbose
782
783
783 Some examples:
784 Some examples:
784
785
785 - start a bisection with known bad revision 34, and good revision 12::
786 - start a bisection with known bad revision 34, and good revision 12::
786
787
787 hg bisect --bad 34
788 hg bisect --bad 34
788 hg bisect --good 12
789 hg bisect --good 12
789
790
790 - advance the current bisection by marking current revision as good or
791 - advance the current bisection by marking current revision as good or
791 bad::
792 bad::
792
793
793 hg bisect --good
794 hg bisect --good
794 hg bisect --bad
795 hg bisect --bad
795
796
796 - mark the current revision, or a known revision, to be skipped (e.g. if
797 - mark the current revision, or a known revision, to be skipped (e.g. if
797 that revision is not usable because of another issue)::
798 that revision is not usable because of another issue)::
798
799
799 hg bisect --skip
800 hg bisect --skip
800 hg bisect --skip 23
801 hg bisect --skip 23
801
802
802 - skip all revisions that do not touch directories ``foo`` or ``bar``::
803 - skip all revisions that do not touch directories ``foo`` or ``bar``::
803
804
804 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
805 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
805
806
806 - forget the current bisection::
807 - forget the current bisection::
807
808
808 hg bisect --reset
809 hg bisect --reset
809
810
810 - use 'make && make tests' to automatically find the first broken
811 - use 'make && make tests' to automatically find the first broken
811 revision::
812 revision::
812
813
813 hg bisect --reset
814 hg bisect --reset
814 hg bisect --bad 34
815 hg bisect --bad 34
815 hg bisect --good 12
816 hg bisect --good 12
816 hg bisect --command "make && make tests"
817 hg bisect --command "make && make tests"
817
818
818 - see all changesets whose states are already known in the current
819 - see all changesets whose states are already known in the current
819 bisection::
820 bisection::
820
821
821 hg log -r "bisect(pruned)"
822 hg log -r "bisect(pruned)"
822
823
823 - see the changeset currently being bisected (especially useful
824 - see the changeset currently being bisected (especially useful
824 if running with -U/--noupdate)::
825 if running with -U/--noupdate)::
825
826
826 hg log -r "bisect(current)"
827 hg log -r "bisect(current)"
827
828
828 - see all changesets that took part in the current bisection::
829 - see all changesets that took part in the current bisection::
829
830
830 hg log -r "bisect(range)"
831 hg log -r "bisect(range)"
831
832
832 - you can even get a nice graph::
833 - you can even get a nice graph::
833
834
834 hg log --graph -r "bisect(range)"
835 hg log --graph -r "bisect(range)"
835
836
836 See :hg:`help revsets` for more about the `bisect()` keyword.
837 See :hg:`help revsets` for more about the `bisect()` keyword.
837
838
838 Returns 0 on success.
839 Returns 0 on success.
839 """
840 """
840 # backward compatibility
841 # backward compatibility
841 if rev in "good bad reset init".split():
842 if rev in "good bad reset init".split():
842 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
843 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
843 cmd, rev, extra = rev, extra, None
844 cmd, rev, extra = rev, extra, None
844 if cmd == "good":
845 if cmd == "good":
845 good = True
846 good = True
846 elif cmd == "bad":
847 elif cmd == "bad":
847 bad = True
848 bad = True
848 else:
849 else:
849 reset = True
850 reset = True
850 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
851 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
851 raise error.Abort(_('incompatible arguments'))
852 raise error.Abort(_('incompatible arguments'))
852
853
853 cmdutil.checkunfinished(repo)
854 cmdutil.checkunfinished(repo)
854
855
855 if reset:
856 if reset:
856 hbisect.resetstate(repo)
857 hbisect.resetstate(repo)
857 return
858 return
858
859
859 state = hbisect.load_state(repo)
860 state = hbisect.load_state(repo)
860
861
861 # update state
862 # update state
862 if good or bad or skip:
863 if good or bad or skip:
863 if rev:
864 if rev:
864 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
865 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
865 else:
866 else:
866 nodes = [repo.lookup('.')]
867 nodes = [repo.lookup('.')]
867 if good:
868 if good:
868 state['good'] += nodes
869 state['good'] += nodes
869 elif bad:
870 elif bad:
870 state['bad'] += nodes
871 state['bad'] += nodes
871 elif skip:
872 elif skip:
872 state['skip'] += nodes
873 state['skip'] += nodes
873 hbisect.save_state(repo, state)
874 hbisect.save_state(repo, state)
874 if not (state['good'] and state['bad']):
875 if not (state['good'] and state['bad']):
875 return
876 return
876
877
877 def mayupdate(repo, node, show_stats=True):
878 def mayupdate(repo, node, show_stats=True):
878 """common used update sequence"""
879 """common used update sequence"""
879 if noupdate:
880 if noupdate:
880 return
881 return
881 cmdutil.bailifchanged(repo)
882 cmdutil.bailifchanged(repo)
882 return hg.clean(repo, node, show_stats=show_stats)
883 return hg.clean(repo, node, show_stats=show_stats)
883
884
884 displayer = cmdutil.show_changeset(ui, repo, {})
885 displayer = cmdutil.show_changeset(ui, repo, {})
885
886
886 if command:
887 if command:
887 changesets = 1
888 changesets = 1
888 if noupdate:
889 if noupdate:
889 try:
890 try:
890 node = state['current'][0]
891 node = state['current'][0]
891 except LookupError:
892 except LookupError:
892 raise error.Abort(_('current bisect revision is unknown - '
893 raise error.Abort(_('current bisect revision is unknown - '
893 'start a new bisect to fix'))
894 'start a new bisect to fix'))
894 else:
895 else:
895 node, p2 = repo.dirstate.parents()
896 node, p2 = repo.dirstate.parents()
896 if p2 != nullid:
897 if p2 != nullid:
897 raise error.Abort(_('current bisect revision is a merge'))
898 raise error.Abort(_('current bisect revision is a merge'))
898 if rev:
899 if rev:
899 node = repo[scmutil.revsingle(repo, rev, node)].node()
900 node = repo[scmutil.revsingle(repo, rev, node)].node()
900 try:
901 try:
901 while changesets:
902 while changesets:
902 # update state
903 # update state
903 state['current'] = [node]
904 state['current'] = [node]
904 hbisect.save_state(repo, state)
905 hbisect.save_state(repo, state)
905 status = ui.system(command, environ={'HG_NODE': hex(node)})
906 status = ui.system(command, environ={'HG_NODE': hex(node)})
906 if status == 125:
907 if status == 125:
907 transition = "skip"
908 transition = "skip"
908 elif status == 0:
909 elif status == 0:
909 transition = "good"
910 transition = "good"
910 # status < 0 means process was killed
911 # status < 0 means process was killed
911 elif status == 127:
912 elif status == 127:
912 raise error.Abort(_("failed to execute %s") % command)
913 raise error.Abort(_("failed to execute %s") % command)
913 elif status < 0:
914 elif status < 0:
914 raise error.Abort(_("%s killed") % command)
915 raise error.Abort(_("%s killed") % command)
915 else:
916 else:
916 transition = "bad"
917 transition = "bad"
917 state[transition].append(node)
918 state[transition].append(node)
918 ctx = repo[node]
919 ctx = repo[node]
919 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
920 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
920 hbisect.checkstate(state)
921 hbisect.checkstate(state)
921 # bisect
922 # bisect
922 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
923 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
923 # update to next check
924 # update to next check
924 node = nodes[0]
925 node = nodes[0]
925 mayupdate(repo, node, show_stats=False)
926 mayupdate(repo, node, show_stats=False)
926 finally:
927 finally:
927 state['current'] = [node]
928 state['current'] = [node]
928 hbisect.save_state(repo, state)
929 hbisect.save_state(repo, state)
929 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
930 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
930 return
931 return
931
932
932 hbisect.checkstate(state)
933 hbisect.checkstate(state)
933
934
934 # actually bisect
935 # actually bisect
935 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
936 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
936 if extend:
937 if extend:
937 if not changesets:
938 if not changesets:
938 extendnode = hbisect.extendrange(repo, state, nodes, good)
939 extendnode = hbisect.extendrange(repo, state, nodes, good)
939 if extendnode is not None:
940 if extendnode is not None:
940 ui.write(_("Extending search to changeset %d:%s\n")
941 ui.write(_("Extending search to changeset %d:%s\n")
941 % (extendnode.rev(), extendnode))
942 % (extendnode.rev(), extendnode))
942 state['current'] = [extendnode.node()]
943 state['current'] = [extendnode.node()]
943 hbisect.save_state(repo, state)
944 hbisect.save_state(repo, state)
944 return mayupdate(repo, extendnode.node())
945 return mayupdate(repo, extendnode.node())
945 raise error.Abort(_("nothing to extend"))
946 raise error.Abort(_("nothing to extend"))
946
947
947 if changesets == 0:
948 if changesets == 0:
948 hbisect.printresult(ui, repo, state, displayer, nodes, good)
949 hbisect.printresult(ui, repo, state, displayer, nodes, good)
949 else:
950 else:
950 assert len(nodes) == 1 # only a single node can be tested next
951 assert len(nodes) == 1 # only a single node can be tested next
951 node = nodes[0]
952 node = nodes[0]
952 # compute the approximate number of remaining tests
953 # compute the approximate number of remaining tests
953 tests, size = 0, 2
954 tests, size = 0, 2
954 while size <= changesets:
955 while size <= changesets:
955 tests, size = tests + 1, size * 2
956 tests, size = tests + 1, size * 2
956 rev = repo.changelog.rev(node)
957 rev = repo.changelog.rev(node)
957 ui.write(_("Testing changeset %d:%s "
958 ui.write(_("Testing changeset %d:%s "
958 "(%d changesets remaining, ~%d tests)\n")
959 "(%d changesets remaining, ~%d tests)\n")
959 % (rev, short(node), changesets, tests))
960 % (rev, short(node), changesets, tests))
960 state['current'] = [node]
961 state['current'] = [node]
961 hbisect.save_state(repo, state)
962 hbisect.save_state(repo, state)
962 return mayupdate(repo, node)
963 return mayupdate(repo, node)
963
964
964 @command('bookmarks|bookmark',
965 @command('bookmarks|bookmark',
965 [('f', 'force', False, _('force')),
966 [('f', 'force', False, _('force')),
966 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
967 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
967 ('d', 'delete', False, _('delete a given bookmark')),
968 ('d', 'delete', False, _('delete a given bookmark')),
968 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
969 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
969 ('i', 'inactive', False, _('mark a bookmark inactive')),
970 ('i', 'inactive', False, _('mark a bookmark inactive')),
970 ] + formatteropts,
971 ] + formatteropts,
971 _('hg bookmarks [OPTIONS]... [NAME]...'))
972 _('hg bookmarks [OPTIONS]... [NAME]...'))
972 def bookmark(ui, repo, *names, **opts):
973 def bookmark(ui, repo, *names, **opts):
973 '''create a new bookmark or list existing bookmarks
974 '''create a new bookmark or list existing bookmarks
974
975
975 Bookmarks are labels on changesets to help track lines of development.
976 Bookmarks are labels on changesets to help track lines of development.
976 Bookmarks are unversioned and can be moved, renamed and deleted.
977 Bookmarks are unversioned and can be moved, renamed and deleted.
977 Deleting or moving a bookmark has no effect on the associated changesets.
978 Deleting or moving a bookmark has no effect on the associated changesets.
978
979
979 Creating or updating to a bookmark causes it to be marked as 'active'.
980 Creating or updating to a bookmark causes it to be marked as 'active'.
980 The active bookmark is indicated with a '*'.
981 The active bookmark is indicated with a '*'.
981 When a commit is made, the active bookmark will advance to the new commit.
982 When a commit is made, the active bookmark will advance to the new commit.
982 A plain :hg:`update` will also advance an active bookmark, if possible.
983 A plain :hg:`update` will also advance an active bookmark, if possible.
983 Updating away from a bookmark will cause it to be deactivated.
984 Updating away from a bookmark will cause it to be deactivated.
984
985
985 Bookmarks can be pushed and pulled between repositories (see
986 Bookmarks can be pushed and pulled between repositories (see
986 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
987 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
987 diverged, a new 'divergent bookmark' of the form 'name@path' will
988 diverged, a new 'divergent bookmark' of the form 'name@path' will
988 be created. Using :hg:`merge` will resolve the divergence.
989 be created. Using :hg:`merge` will resolve the divergence.
989
990
990 A bookmark named '@' has the special property that :hg:`clone` will
991 A bookmark named '@' has the special property that :hg:`clone` will
991 check it out by default if it exists.
992 check it out by default if it exists.
992
993
993 .. container:: verbose
994 .. container:: verbose
994
995
995 Examples:
996 Examples:
996
997
997 - create an active bookmark for a new line of development::
998 - create an active bookmark for a new line of development::
998
999
999 hg book new-feature
1000 hg book new-feature
1000
1001
1001 - create an inactive bookmark as a place marker::
1002 - create an inactive bookmark as a place marker::
1002
1003
1003 hg book -i reviewed
1004 hg book -i reviewed
1004
1005
1005 - create an inactive bookmark on another changeset::
1006 - create an inactive bookmark on another changeset::
1006
1007
1007 hg book -r .^ tested
1008 hg book -r .^ tested
1008
1009
1009 - rename bookmark turkey to dinner::
1010 - rename bookmark turkey to dinner::
1010
1011
1011 hg book -m turkey dinner
1012 hg book -m turkey dinner
1012
1013
1013 - move the '@' bookmark from another branch::
1014 - move the '@' bookmark from another branch::
1014
1015
1015 hg book -f @
1016 hg book -f @
1016 '''
1017 '''
1017 force = opts.get('force')
1018 force = opts.get('force')
1018 rev = opts.get('rev')
1019 rev = opts.get('rev')
1019 delete = opts.get('delete')
1020 delete = opts.get('delete')
1020 rename = opts.get('rename')
1021 rename = opts.get('rename')
1021 inactive = opts.get('inactive')
1022 inactive = opts.get('inactive')
1022
1023
1023 def checkformat(mark):
1024 def checkformat(mark):
1024 mark = mark.strip()
1025 mark = mark.strip()
1025 if not mark:
1026 if not mark:
1026 raise error.Abort(_("bookmark names cannot consist entirely of "
1027 raise error.Abort(_("bookmark names cannot consist entirely of "
1027 "whitespace"))
1028 "whitespace"))
1028 scmutil.checknewlabel(repo, mark, 'bookmark')
1029 scmutil.checknewlabel(repo, mark, 'bookmark')
1029 return mark
1030 return mark
1030
1031
1031 def checkconflict(repo, mark, cur, force=False, target=None):
1032 def checkconflict(repo, mark, cur, force=False, target=None):
1032 if mark in marks and not force:
1033 if mark in marks and not force:
1033 if target:
1034 if target:
1034 if marks[mark] == target and target == cur:
1035 if marks[mark] == target and target == cur:
1035 # re-activating a bookmark
1036 # re-activating a bookmark
1036 return
1037 return
1037 anc = repo.changelog.ancestors([repo[target].rev()])
1038 anc = repo.changelog.ancestors([repo[target].rev()])
1038 bmctx = repo[marks[mark]]
1039 bmctx = repo[marks[mark]]
1039 divs = [repo[b].node() for b in marks
1040 divs = [repo[b].node() for b in marks
1040 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
1041 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
1041
1042
1042 # allow resolving a single divergent bookmark even if moving
1043 # allow resolving a single divergent bookmark even if moving
1043 # the bookmark across branches when a revision is specified
1044 # the bookmark across branches when a revision is specified
1044 # that contains a divergent bookmark
1045 # that contains a divergent bookmark
1045 if bmctx.rev() not in anc and target in divs:
1046 if bmctx.rev() not in anc and target in divs:
1046 bookmarks.deletedivergent(repo, [target], mark)
1047 bookmarks.deletedivergent(repo, [target], mark)
1047 return
1048 return
1048
1049
1049 deletefrom = [b for b in divs
1050 deletefrom = [b for b in divs
1050 if repo[b].rev() in anc or b == target]
1051 if repo[b].rev() in anc or b == target]
1051 bookmarks.deletedivergent(repo, deletefrom, mark)
1052 bookmarks.deletedivergent(repo, deletefrom, mark)
1052 if bookmarks.validdest(repo, bmctx, repo[target]):
1053 if bookmarks.validdest(repo, bmctx, repo[target]):
1053 ui.status(_("moving bookmark '%s' forward from %s\n") %
1054 ui.status(_("moving bookmark '%s' forward from %s\n") %
1054 (mark, short(bmctx.node())))
1055 (mark, short(bmctx.node())))
1055 return
1056 return
1056 raise error.Abort(_("bookmark '%s' already exists "
1057 raise error.Abort(_("bookmark '%s' already exists "
1057 "(use -f to force)") % mark)
1058 "(use -f to force)") % mark)
1058 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
1059 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
1059 and not force):
1060 and not force):
1060 raise error.Abort(
1061 raise error.Abort(
1061 _("a bookmark cannot have the name of an existing branch"))
1062 _("a bookmark cannot have the name of an existing branch"))
1062
1063
1063 if delete and rename:
1064 if delete and rename:
1064 raise error.Abort(_("--delete and --rename are incompatible"))
1065 raise error.Abort(_("--delete and --rename are incompatible"))
1065 if delete and rev:
1066 if delete and rev:
1066 raise error.Abort(_("--rev is incompatible with --delete"))
1067 raise error.Abort(_("--rev is incompatible with --delete"))
1067 if rename and rev:
1068 if rename and rev:
1068 raise error.Abort(_("--rev is incompatible with --rename"))
1069 raise error.Abort(_("--rev is incompatible with --rename"))
1069 if not names and (delete or rev):
1070 if not names and (delete or rev):
1070 raise error.Abort(_("bookmark name required"))
1071 raise error.Abort(_("bookmark name required"))
1071
1072
1072 if delete or rename or names or inactive:
1073 if delete or rename or names or inactive:
1073 wlock = lock = tr = None
1074 wlock = lock = tr = None
1074 try:
1075 try:
1075 wlock = repo.wlock()
1076 wlock = repo.wlock()
1076 lock = repo.lock()
1077 lock = repo.lock()
1077 cur = repo.changectx('.').node()
1078 cur = repo.changectx('.').node()
1078 marks = repo._bookmarks
1079 marks = repo._bookmarks
1079 if delete:
1080 if delete:
1080 tr = repo.transaction('bookmark')
1081 tr = repo.transaction('bookmark')
1081 for mark in names:
1082 for mark in names:
1082 if mark not in marks:
1083 if mark not in marks:
1083 raise error.Abort(_("bookmark '%s' does not exist") %
1084 raise error.Abort(_("bookmark '%s' does not exist") %
1084 mark)
1085 mark)
1085 if mark == repo._activebookmark:
1086 if mark == repo._activebookmark:
1086 bookmarks.deactivate(repo)
1087 bookmarks.deactivate(repo)
1087 del marks[mark]
1088 del marks[mark]
1088
1089
1089 elif rename:
1090 elif rename:
1090 tr = repo.transaction('bookmark')
1091 tr = repo.transaction('bookmark')
1091 if not names:
1092 if not names:
1092 raise error.Abort(_("new bookmark name required"))
1093 raise error.Abort(_("new bookmark name required"))
1093 elif len(names) > 1:
1094 elif len(names) > 1:
1094 raise error.Abort(_("only one new bookmark name allowed"))
1095 raise error.Abort(_("only one new bookmark name allowed"))
1095 mark = checkformat(names[0])
1096 mark = checkformat(names[0])
1096 if rename not in marks:
1097 if rename not in marks:
1097 raise error.Abort(_("bookmark '%s' does not exist")
1098 raise error.Abort(_("bookmark '%s' does not exist")
1098 % rename)
1099 % rename)
1099 checkconflict(repo, mark, cur, force)
1100 checkconflict(repo, mark, cur, force)
1100 marks[mark] = marks[rename]
1101 marks[mark] = marks[rename]
1101 if repo._activebookmark == rename and not inactive:
1102 if repo._activebookmark == rename and not inactive:
1102 bookmarks.activate(repo, mark)
1103 bookmarks.activate(repo, mark)
1103 del marks[rename]
1104 del marks[rename]
1104 elif names:
1105 elif names:
1105 tr = repo.transaction('bookmark')
1106 tr = repo.transaction('bookmark')
1106 newact = None
1107 newact = None
1107 for mark in names:
1108 for mark in names:
1108 mark = checkformat(mark)
1109 mark = checkformat(mark)
1109 if newact is None:
1110 if newact is None:
1110 newact = mark
1111 newact = mark
1111 if inactive and mark == repo._activebookmark:
1112 if inactive and mark == repo._activebookmark:
1112 bookmarks.deactivate(repo)
1113 bookmarks.deactivate(repo)
1113 return
1114 return
1114 tgt = cur
1115 tgt = cur
1115 if rev:
1116 if rev:
1116 tgt = scmutil.revsingle(repo, rev).node()
1117 tgt = scmutil.revsingle(repo, rev).node()
1117 checkconflict(repo, mark, cur, force, tgt)
1118 checkconflict(repo, mark, cur, force, tgt)
1118 marks[mark] = tgt
1119 marks[mark] = tgt
1119 if not inactive and cur == marks[newact] and not rev:
1120 if not inactive and cur == marks[newact] and not rev:
1120 bookmarks.activate(repo, newact)
1121 bookmarks.activate(repo, newact)
1121 elif cur != tgt and newact == repo._activebookmark:
1122 elif cur != tgt and newact == repo._activebookmark:
1122 bookmarks.deactivate(repo)
1123 bookmarks.deactivate(repo)
1123 elif inactive:
1124 elif inactive:
1124 if len(marks) == 0:
1125 if len(marks) == 0:
1125 ui.status(_("no bookmarks set\n"))
1126 ui.status(_("no bookmarks set\n"))
1126 elif not repo._activebookmark:
1127 elif not repo._activebookmark:
1127 ui.status(_("no active bookmark\n"))
1128 ui.status(_("no active bookmark\n"))
1128 else:
1129 else:
1129 bookmarks.deactivate(repo)
1130 bookmarks.deactivate(repo)
1130 if tr is not None:
1131 if tr is not None:
1131 marks.recordchange(tr)
1132 marks.recordchange(tr)
1132 tr.close()
1133 tr.close()
1133 finally:
1134 finally:
1134 lockmod.release(tr, lock, wlock)
1135 lockmod.release(tr, lock, wlock)
1135 else: # show bookmarks
1136 else: # show bookmarks
1136 fm = ui.formatter('bookmarks', opts)
1137 fm = ui.formatter('bookmarks', opts)
1137 hexfn = fm.hexfunc
1138 hexfn = fm.hexfunc
1138 marks = repo._bookmarks
1139 marks = repo._bookmarks
1139 if len(marks) == 0 and fm.isplain():
1140 if len(marks) == 0 and fm.isplain():
1140 ui.status(_("no bookmarks set\n"))
1141 ui.status(_("no bookmarks set\n"))
1141 for bmark, n in sorted(marks.iteritems()):
1142 for bmark, n in sorted(marks.iteritems()):
1142 active = repo._activebookmark
1143 active = repo._activebookmark
1143 if bmark == active:
1144 if bmark == active:
1144 prefix, label = '*', activebookmarklabel
1145 prefix, label = '*', activebookmarklabel
1145 else:
1146 else:
1146 prefix, label = ' ', ''
1147 prefix, label = ' ', ''
1147
1148
1148 fm.startitem()
1149 fm.startitem()
1149 if not ui.quiet:
1150 if not ui.quiet:
1150 fm.plain(' %s ' % prefix, label=label)
1151 fm.plain(' %s ' % prefix, label=label)
1151 fm.write('bookmark', '%s', bmark, label=label)
1152 fm.write('bookmark', '%s', bmark, label=label)
1152 pad = " " * (25 - encoding.colwidth(bmark))
1153 pad = " " * (25 - encoding.colwidth(bmark))
1153 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1154 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1154 repo.changelog.rev(n), hexfn(n), label=label)
1155 repo.changelog.rev(n), hexfn(n), label=label)
1155 fm.data(active=(bmark == active))
1156 fm.data(active=(bmark == active))
1156 fm.plain('\n')
1157 fm.plain('\n')
1157 fm.end()
1158 fm.end()
1158
1159
1159 @command('branch',
1160 @command('branch',
1160 [('f', 'force', None,
1161 [('f', 'force', None,
1161 _('set branch name even if it shadows an existing branch')),
1162 _('set branch name even if it shadows an existing branch')),
1162 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1163 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1163 _('[-fC] [NAME]'))
1164 _('[-fC] [NAME]'))
1164 def branch(ui, repo, label=None, **opts):
1165 def branch(ui, repo, label=None, **opts):
1165 """set or show the current branch name
1166 """set or show the current branch name
1166
1167
1167 .. note::
1168 .. note::
1168
1169
1169 Branch names are permanent and global. Use :hg:`bookmark` to create a
1170 Branch names are permanent and global. Use :hg:`bookmark` to create a
1170 light-weight bookmark instead. See :hg:`help glossary` for more
1171 light-weight bookmark instead. See :hg:`help glossary` for more
1171 information about named branches and bookmarks.
1172 information about named branches and bookmarks.
1172
1173
1173 With no argument, show the current branch name. With one argument,
1174 With no argument, show the current branch name. With one argument,
1174 set the working directory branch name (the branch will not exist
1175 set the working directory branch name (the branch will not exist
1175 in the repository until the next commit). Standard practice
1176 in the repository until the next commit). Standard practice
1176 recommends that primary development take place on the 'default'
1177 recommends that primary development take place on the 'default'
1177 branch.
1178 branch.
1178
1179
1179 Unless -f/--force is specified, branch will not let you set a
1180 Unless -f/--force is specified, branch will not let you set a
1180 branch name that already exists.
1181 branch name that already exists.
1181
1182
1182 Use -C/--clean to reset the working directory branch to that of
1183 Use -C/--clean to reset the working directory branch to that of
1183 the parent of the working directory, negating a previous branch
1184 the parent of the working directory, negating a previous branch
1184 change.
1185 change.
1185
1186
1186 Use the command :hg:`update` to switch to an existing branch. Use
1187 Use the command :hg:`update` to switch to an existing branch. Use
1187 :hg:`commit --close-branch` to mark this branch head as closed.
1188 :hg:`commit --close-branch` to mark this branch head as closed.
1188 When all heads of a branch are closed, the branch will be
1189 When all heads of a branch are closed, the branch will be
1189 considered closed.
1190 considered closed.
1190
1191
1191 Returns 0 on success.
1192 Returns 0 on success.
1192 """
1193 """
1193 if label:
1194 if label:
1194 label = label.strip()
1195 label = label.strip()
1195
1196
1196 if not opts.get('clean') and not label:
1197 if not opts.get('clean') and not label:
1197 ui.write("%s\n" % repo.dirstate.branch())
1198 ui.write("%s\n" % repo.dirstate.branch())
1198 return
1199 return
1199
1200
1200 with repo.wlock():
1201 with repo.wlock():
1201 if opts.get('clean'):
1202 if opts.get('clean'):
1202 label = repo[None].p1().branch()
1203 label = repo[None].p1().branch()
1203 repo.dirstate.setbranch(label)
1204 repo.dirstate.setbranch(label)
1204 ui.status(_('reset working directory to branch %s\n') % label)
1205 ui.status(_('reset working directory to branch %s\n') % label)
1205 elif label:
1206 elif label:
1206 if not opts.get('force') and label in repo.branchmap():
1207 if not opts.get('force') and label in repo.branchmap():
1207 if label not in [p.branch() for p in repo[None].parents()]:
1208 if label not in [p.branch() for p in repo[None].parents()]:
1208 raise error.Abort(_('a branch of the same name already'
1209 raise error.Abort(_('a branch of the same name already'
1209 ' exists'),
1210 ' exists'),
1210 # i18n: "it" refers to an existing branch
1211 # i18n: "it" refers to an existing branch
1211 hint=_("use 'hg update' to switch to it"))
1212 hint=_("use 'hg update' to switch to it"))
1212 scmutil.checknewlabel(repo, label, 'branch')
1213 scmutil.checknewlabel(repo, label, 'branch')
1213 repo.dirstate.setbranch(label)
1214 repo.dirstate.setbranch(label)
1214 ui.status(_('marked working directory as branch %s\n') % label)
1215 ui.status(_('marked working directory as branch %s\n') % label)
1215
1216
1216 # find any open named branches aside from default
1217 # find any open named branches aside from default
1217 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1218 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1218 if n != "default" and not c]
1219 if n != "default" and not c]
1219 if not others:
1220 if not others:
1220 ui.status(_('(branches are permanent and global, '
1221 ui.status(_('(branches are permanent and global, '
1221 'did you want a bookmark?)\n'))
1222 'did you want a bookmark?)\n'))
1222
1223
1223 @command('branches',
1224 @command('branches',
1224 [('a', 'active', False,
1225 [('a', 'active', False,
1225 _('show only branches that have unmerged heads (DEPRECATED)')),
1226 _('show only branches that have unmerged heads (DEPRECATED)')),
1226 ('c', 'closed', False, _('show normal and closed branches')),
1227 ('c', 'closed', False, _('show normal and closed branches')),
1227 ] + formatteropts,
1228 ] + formatteropts,
1228 _('[-c]'))
1229 _('[-c]'))
1229 def branches(ui, repo, active=False, closed=False, **opts):
1230 def branches(ui, repo, active=False, closed=False, **opts):
1230 """list repository named branches
1231 """list repository named branches
1231
1232
1232 List the repository's named branches, indicating which ones are
1233 List the repository's named branches, indicating which ones are
1233 inactive. If -c/--closed is specified, also list branches which have
1234 inactive. If -c/--closed is specified, also list branches which have
1234 been marked closed (see :hg:`commit --close-branch`).
1235 been marked closed (see :hg:`commit --close-branch`).
1235
1236
1236 Use the command :hg:`update` to switch to an existing branch.
1237 Use the command :hg:`update` to switch to an existing branch.
1237
1238
1238 Returns 0.
1239 Returns 0.
1239 """
1240 """
1240
1241
1241 fm = ui.formatter('branches', opts)
1242 fm = ui.formatter('branches', opts)
1242 hexfunc = fm.hexfunc
1243 hexfunc = fm.hexfunc
1243
1244
1244 allheads = set(repo.heads())
1245 allheads = set(repo.heads())
1245 branches = []
1246 branches = []
1246 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1247 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1247 isactive = not isclosed and bool(set(heads) & allheads)
1248 isactive = not isclosed and bool(set(heads) & allheads)
1248 branches.append((tag, repo[tip], isactive, not isclosed))
1249 branches.append((tag, repo[tip], isactive, not isclosed))
1249 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1250 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1250 reverse=True)
1251 reverse=True)
1251
1252
1252 for tag, ctx, isactive, isopen in branches:
1253 for tag, ctx, isactive, isopen in branches:
1253 if active and not isactive:
1254 if active and not isactive:
1254 continue
1255 continue
1255 if isactive:
1256 if isactive:
1256 label = 'branches.active'
1257 label = 'branches.active'
1257 notice = ''
1258 notice = ''
1258 elif not isopen:
1259 elif not isopen:
1259 if not closed:
1260 if not closed:
1260 continue
1261 continue
1261 label = 'branches.closed'
1262 label = 'branches.closed'
1262 notice = _(' (closed)')
1263 notice = _(' (closed)')
1263 else:
1264 else:
1264 label = 'branches.inactive'
1265 label = 'branches.inactive'
1265 notice = _(' (inactive)')
1266 notice = _(' (inactive)')
1266 current = (tag == repo.dirstate.branch())
1267 current = (tag == repo.dirstate.branch())
1267 if current:
1268 if current:
1268 label = 'branches.current'
1269 label = 'branches.current'
1269
1270
1270 fm.startitem()
1271 fm.startitem()
1271 fm.write('branch', '%s', tag, label=label)
1272 fm.write('branch', '%s', tag, label=label)
1272 rev = ctx.rev()
1273 rev = ctx.rev()
1273 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1274 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1274 fmt = ' ' * padsize + ' %d:%s'
1275 fmt = ' ' * padsize + ' %d:%s'
1275 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1276 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1276 label='log.changeset changeset.%s' % ctx.phasestr())
1277 label='log.changeset changeset.%s' % ctx.phasestr())
1277 fm.data(active=isactive, closed=not isopen, current=current)
1278 fm.data(active=isactive, closed=not isopen, current=current)
1278 if not ui.quiet:
1279 if not ui.quiet:
1279 fm.plain(notice)
1280 fm.plain(notice)
1280 fm.plain('\n')
1281 fm.plain('\n')
1281 fm.end()
1282 fm.end()
1282
1283
1283 @command('bundle',
1284 @command('bundle',
1284 [('f', 'force', None, _('run even when the destination is unrelated')),
1285 [('f', 'force', None, _('run even when the destination is unrelated')),
1285 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1286 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1286 _('REV')),
1287 _('REV')),
1287 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1288 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1288 _('BRANCH')),
1289 _('BRANCH')),
1289 ('', 'base', [],
1290 ('', 'base', [],
1290 _('a base changeset assumed to be available at the destination'),
1291 _('a base changeset assumed to be available at the destination'),
1291 _('REV')),
1292 _('REV')),
1292 ('a', 'all', None, _('bundle all changesets in the repository')),
1293 ('a', 'all', None, _('bundle all changesets in the repository')),
1293 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1294 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1294 ] + remoteopts,
1295 ] + remoteopts,
1295 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1296 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1296 def bundle(ui, repo, fname, dest=None, **opts):
1297 def bundle(ui, repo, fname, dest=None, **opts):
1297 """create a changegroup file
1298 """create a changegroup file
1298
1299
1299 Generate a changegroup file collecting changesets to be added
1300 Generate a changegroup file collecting changesets to be added
1300 to a repository.
1301 to a repository.
1301
1302
1302 To create a bundle containing all changesets, use -a/--all
1303 To create a bundle containing all changesets, use -a/--all
1303 (or --base null). Otherwise, hg assumes the destination will have
1304 (or --base null). Otherwise, hg assumes the destination will have
1304 all the nodes you specify with --base parameters. Otherwise, hg
1305 all the nodes you specify with --base parameters. Otherwise, hg
1305 will assume the repository has all the nodes in destination, or
1306 will assume the repository has all the nodes in destination, or
1306 default-push/default if no destination is specified.
1307 default-push/default if no destination is specified.
1307
1308
1308 You can change bundle format with the -t/--type option. You can
1309 You can change bundle format with the -t/--type option. You can
1309 specify a compression, a bundle version or both using a dash
1310 specify a compression, a bundle version or both using a dash
1310 (comp-version). The available compression methods are: none, bzip2,
1311 (comp-version). The available compression methods are: none, bzip2,
1311 and gzip (by default, bundles are compressed using bzip2). The
1312 and gzip (by default, bundles are compressed using bzip2). The
1312 available formats are: v1, v2 (default to most suitable).
1313 available formats are: v1, v2 (default to most suitable).
1313
1314
1314 The bundle file can then be transferred using conventional means
1315 The bundle file can then be transferred using conventional means
1315 and applied to another repository with the unbundle or pull
1316 and applied to another repository with the unbundle or pull
1316 command. This is useful when direct push and pull are not
1317 command. This is useful when direct push and pull are not
1317 available or when exporting an entire repository is undesirable.
1318 available or when exporting an entire repository is undesirable.
1318
1319
1319 Applying bundles preserves all changeset contents including
1320 Applying bundles preserves all changeset contents including
1320 permissions, copy/rename information, and revision history.
1321 permissions, copy/rename information, and revision history.
1321
1322
1322 Returns 0 on success, 1 if no changes found.
1323 Returns 0 on success, 1 if no changes found.
1323 """
1324 """
1324 revs = None
1325 revs = None
1325 if 'rev' in opts:
1326 if 'rev' in opts:
1326 revstrings = opts['rev']
1327 revstrings = opts['rev']
1327 revs = scmutil.revrange(repo, revstrings)
1328 revs = scmutil.revrange(repo, revstrings)
1328 if revstrings and not revs:
1329 if revstrings and not revs:
1329 raise error.Abort(_('no commits to bundle'))
1330 raise error.Abort(_('no commits to bundle'))
1330
1331
1331 bundletype = opts.get('type', 'bzip2').lower()
1332 bundletype = opts.get('type', 'bzip2').lower()
1332 try:
1333 try:
1333 bcompression, cgversion, params = exchange.parsebundlespec(
1334 bcompression, cgversion, params = exchange.parsebundlespec(
1334 repo, bundletype, strict=False)
1335 repo, bundletype, strict=False)
1335 except error.UnsupportedBundleSpecification as e:
1336 except error.UnsupportedBundleSpecification as e:
1336 raise error.Abort(str(e),
1337 raise error.Abort(str(e),
1337 hint=_("see 'hg help bundle' for supported "
1338 hint=_("see 'hg help bundle' for supported "
1338 "values for --type"))
1339 "values for --type"))
1339
1340
1340 # Packed bundles are a pseudo bundle format for now.
1341 # Packed bundles are a pseudo bundle format for now.
1341 if cgversion == 's1':
1342 if cgversion == 's1':
1342 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1343 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1343 hint=_("use 'hg debugcreatestreamclonebundle'"))
1344 hint=_("use 'hg debugcreatestreamclonebundle'"))
1344
1345
1345 if opts.get('all'):
1346 if opts.get('all'):
1346 if dest:
1347 if dest:
1347 raise error.Abort(_("--all is incompatible with specifying "
1348 raise error.Abort(_("--all is incompatible with specifying "
1348 "a destination"))
1349 "a destination"))
1349 if opts.get('base'):
1350 if opts.get('base'):
1350 ui.warn(_("ignoring --base because --all was specified\n"))
1351 ui.warn(_("ignoring --base because --all was specified\n"))
1351 base = ['null']
1352 base = ['null']
1352 else:
1353 else:
1353 base = scmutil.revrange(repo, opts.get('base'))
1354 base = scmutil.revrange(repo, opts.get('base'))
1354 # TODO: get desired bundlecaps from command line.
1355 # TODO: get desired bundlecaps from command line.
1355 bundlecaps = None
1356 bundlecaps = None
1356 if cgversion not in changegroup.supportedoutgoingversions(repo):
1357 if cgversion not in changegroup.supportedoutgoingversions(repo):
1357 raise error.Abort(_("repository does not support bundle version %s") %
1358 raise error.Abort(_("repository does not support bundle version %s") %
1358 cgversion)
1359 cgversion)
1359
1360
1360 if base:
1361 if base:
1361 if dest:
1362 if dest:
1362 raise error.Abort(_("--base is incompatible with specifying "
1363 raise error.Abort(_("--base is incompatible with specifying "
1363 "a destination"))
1364 "a destination"))
1364 common = [repo.lookup(rev) for rev in base]
1365 common = [repo.lookup(rev) for rev in base]
1365 heads = revs and map(repo.lookup, revs) or None
1366 heads = revs and map(repo.lookup, revs) or None
1366 outgoing = discovery.outgoing(repo, common, heads)
1367 outgoing = discovery.outgoing(repo, common, heads)
1367 cg = changegroup.getchangegroup(repo, 'bundle', outgoing,
1368 cg = changegroup.getchangegroup(repo, 'bundle', outgoing,
1368 bundlecaps=bundlecaps,
1369 bundlecaps=bundlecaps,
1369 version=cgversion)
1370 version=cgversion)
1370 outgoing = None
1371 outgoing = None
1371 else:
1372 else:
1372 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1373 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1373 dest, branches = hg.parseurl(dest, opts.get('branch'))
1374 dest, branches = hg.parseurl(dest, opts.get('branch'))
1374 other = hg.peer(repo, opts, dest)
1375 other = hg.peer(repo, opts, dest)
1375 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1376 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1376 heads = revs and map(repo.lookup, revs) or revs
1377 heads = revs and map(repo.lookup, revs) or revs
1377 outgoing = discovery.findcommonoutgoing(repo, other,
1378 outgoing = discovery.findcommonoutgoing(repo, other,
1378 onlyheads=heads,
1379 onlyheads=heads,
1379 force=opts.get('force'),
1380 force=opts.get('force'),
1380 portable=True)
1381 portable=True)
1381 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1382 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1382 bundlecaps, version=cgversion)
1383 bundlecaps, version=cgversion)
1383 if not cg:
1384 if not cg:
1384 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1385 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1385 return 1
1386 return 1
1386
1387
1387 if cgversion == '01': #bundle1
1388 if cgversion == '01': #bundle1
1388 if bcompression is None:
1389 if bcompression is None:
1389 bcompression = 'UN'
1390 bcompression = 'UN'
1390 bversion = 'HG10' + bcompression
1391 bversion = 'HG10' + bcompression
1391 bcompression = None
1392 bcompression = None
1392 else:
1393 else:
1393 assert cgversion == '02'
1394 assert cgversion == '02'
1394 bversion = 'HG20'
1395 bversion = 'HG20'
1395
1396
1396 bundle2.writebundle(ui, cg, fname, bversion, compression=bcompression)
1397 bundle2.writebundle(ui, cg, fname, bversion, compression=bcompression)
1397
1398
1398 @command('cat',
1399 @command('cat',
1399 [('o', 'output', '',
1400 [('o', 'output', '',
1400 _('print output to file with formatted name'), _('FORMAT')),
1401 _('print output to file with formatted name'), _('FORMAT')),
1401 ('r', 'rev', '', _('print the given revision'), _('REV')),
1402 ('r', 'rev', '', _('print the given revision'), _('REV')),
1402 ('', 'decode', None, _('apply any matching decode filter')),
1403 ('', 'decode', None, _('apply any matching decode filter')),
1403 ] + walkopts,
1404 ] + walkopts,
1404 _('[OPTION]... FILE...'),
1405 _('[OPTION]... FILE...'),
1405 inferrepo=True)
1406 inferrepo=True)
1406 def cat(ui, repo, file1, *pats, **opts):
1407 def cat(ui, repo, file1, *pats, **opts):
1407 """output the current or given revision of files
1408 """output the current or given revision of files
1408
1409
1409 Print the specified files as they were at the given revision. If
1410 Print the specified files as they were at the given revision. If
1410 no revision is given, the parent of the working directory is used.
1411 no revision is given, the parent of the working directory is used.
1411
1412
1412 Output may be to a file, in which case the name of the file is
1413 Output may be to a file, in which case the name of the file is
1413 given using a format string. The formatting rules as follows:
1414 given using a format string. The formatting rules as follows:
1414
1415
1415 :``%%``: literal "%" character
1416 :``%%``: literal "%" character
1416 :``%s``: basename of file being printed
1417 :``%s``: basename of file being printed
1417 :``%d``: dirname of file being printed, or '.' if in repository root
1418 :``%d``: dirname of file being printed, or '.' if in repository root
1418 :``%p``: root-relative path name of file being printed
1419 :``%p``: root-relative path name of file being printed
1419 :``%H``: changeset hash (40 hexadecimal digits)
1420 :``%H``: changeset hash (40 hexadecimal digits)
1420 :``%R``: changeset revision number
1421 :``%R``: changeset revision number
1421 :``%h``: short-form changeset hash (12 hexadecimal digits)
1422 :``%h``: short-form changeset hash (12 hexadecimal digits)
1422 :``%r``: zero-padded changeset revision number
1423 :``%r``: zero-padded changeset revision number
1423 :``%b``: basename of the exporting repository
1424 :``%b``: basename of the exporting repository
1424
1425
1425 Returns 0 on success.
1426 Returns 0 on success.
1426 """
1427 """
1427 ctx = scmutil.revsingle(repo, opts.get('rev'))
1428 ctx = scmutil.revsingle(repo, opts.get('rev'))
1428 m = scmutil.match(ctx, (file1,) + pats, opts)
1429 m = scmutil.match(ctx, (file1,) + pats, opts)
1429
1430
1430 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1431 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1431
1432
1432 @command('^clone',
1433 @command('^clone',
1433 [('U', 'noupdate', None, _('the clone will include an empty working '
1434 [('U', 'noupdate', None, _('the clone will include an empty working '
1434 'directory (only a repository)')),
1435 'directory (only a repository)')),
1435 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1436 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1436 _('REV')),
1437 _('REV')),
1437 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1438 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1438 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1439 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1439 ('', 'pull', None, _('use pull protocol to copy metadata')),
1440 ('', 'pull', None, _('use pull protocol to copy metadata')),
1440 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1441 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1441 ] + remoteopts,
1442 ] + remoteopts,
1442 _('[OPTION]... SOURCE [DEST]'),
1443 _('[OPTION]... SOURCE [DEST]'),
1443 norepo=True)
1444 norepo=True)
1444 def clone(ui, source, dest=None, **opts):
1445 def clone(ui, source, dest=None, **opts):
1445 """make a copy of an existing repository
1446 """make a copy of an existing repository
1446
1447
1447 Create a copy of an existing repository in a new directory.
1448 Create a copy of an existing repository in a new directory.
1448
1449
1449 If no destination directory name is specified, it defaults to the
1450 If no destination directory name is specified, it defaults to the
1450 basename of the source.
1451 basename of the source.
1451
1452
1452 The location of the source is added to the new repository's
1453 The location of the source is added to the new repository's
1453 ``.hg/hgrc`` file, as the default to be used for future pulls.
1454 ``.hg/hgrc`` file, as the default to be used for future pulls.
1454
1455
1455 Only local paths and ``ssh://`` URLs are supported as
1456 Only local paths and ``ssh://`` URLs are supported as
1456 destinations. For ``ssh://`` destinations, no working directory or
1457 destinations. For ``ssh://`` destinations, no working directory or
1457 ``.hg/hgrc`` will be created on the remote side.
1458 ``.hg/hgrc`` will be created on the remote side.
1458
1459
1459 If the source repository has a bookmark called '@' set, that
1460 If the source repository has a bookmark called '@' set, that
1460 revision will be checked out in the new repository by default.
1461 revision will be checked out in the new repository by default.
1461
1462
1462 To check out a particular version, use -u/--update, or
1463 To check out a particular version, use -u/--update, or
1463 -U/--noupdate to create a clone with no working directory.
1464 -U/--noupdate to create a clone with no working directory.
1464
1465
1465 To pull only a subset of changesets, specify one or more revisions
1466 To pull only a subset of changesets, specify one or more revisions
1466 identifiers with -r/--rev or branches with -b/--branch. The
1467 identifiers with -r/--rev or branches with -b/--branch. The
1467 resulting clone will contain only the specified changesets and
1468 resulting clone will contain only the specified changesets and
1468 their ancestors. These options (or 'clone src#rev dest') imply
1469 their ancestors. These options (or 'clone src#rev dest') imply
1469 --pull, even for local source repositories.
1470 --pull, even for local source repositories.
1470
1471
1471 .. note::
1472 .. note::
1472
1473
1473 Specifying a tag will include the tagged changeset but not the
1474 Specifying a tag will include the tagged changeset but not the
1474 changeset containing the tag.
1475 changeset containing the tag.
1475
1476
1476 .. container:: verbose
1477 .. container:: verbose
1477
1478
1478 For efficiency, hardlinks are used for cloning whenever the
1479 For efficiency, hardlinks are used for cloning whenever the
1479 source and destination are on the same filesystem (note this
1480 source and destination are on the same filesystem (note this
1480 applies only to the repository data, not to the working
1481 applies only to the repository data, not to the working
1481 directory). Some filesystems, such as AFS, implement hardlinking
1482 directory). Some filesystems, such as AFS, implement hardlinking
1482 incorrectly, but do not report errors. In these cases, use the
1483 incorrectly, but do not report errors. In these cases, use the
1483 --pull option to avoid hardlinking.
1484 --pull option to avoid hardlinking.
1484
1485
1485 In some cases, you can clone repositories and the working
1486 In some cases, you can clone repositories and the working
1486 directory using full hardlinks with ::
1487 directory using full hardlinks with ::
1487
1488
1488 $ cp -al REPO REPOCLONE
1489 $ cp -al REPO REPOCLONE
1489
1490
1490 This is the fastest way to clone, but it is not always safe. The
1491 This is the fastest way to clone, but it is not always safe. The
1491 operation is not atomic (making sure REPO is not modified during
1492 operation is not atomic (making sure REPO is not modified during
1492 the operation is up to you) and you have to make sure your
1493 the operation is up to you) and you have to make sure your
1493 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1494 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1494 so). Also, this is not compatible with certain extensions that
1495 so). Also, this is not compatible with certain extensions that
1495 place their metadata under the .hg directory, such as mq.
1496 place their metadata under the .hg directory, such as mq.
1496
1497
1497 Mercurial will update the working directory to the first applicable
1498 Mercurial will update the working directory to the first applicable
1498 revision from this list:
1499 revision from this list:
1499
1500
1500 a) null if -U or the source repository has no changesets
1501 a) null if -U or the source repository has no changesets
1501 b) if -u . and the source repository is local, the first parent of
1502 b) if -u . and the source repository is local, the first parent of
1502 the source repository's working directory
1503 the source repository's working directory
1503 c) the changeset specified with -u (if a branch name, this means the
1504 c) the changeset specified with -u (if a branch name, this means the
1504 latest head of that branch)
1505 latest head of that branch)
1505 d) the changeset specified with -r
1506 d) the changeset specified with -r
1506 e) the tipmost head specified with -b
1507 e) the tipmost head specified with -b
1507 f) the tipmost head specified with the url#branch source syntax
1508 f) the tipmost head specified with the url#branch source syntax
1508 g) the revision marked with the '@' bookmark, if present
1509 g) the revision marked with the '@' bookmark, if present
1509 h) the tipmost head of the default branch
1510 h) the tipmost head of the default branch
1510 i) tip
1511 i) tip
1511
1512
1512 When cloning from servers that support it, Mercurial may fetch
1513 When cloning from servers that support it, Mercurial may fetch
1513 pre-generated data from a server-advertised URL. When this is done,
1514 pre-generated data from a server-advertised URL. When this is done,
1514 hooks operating on incoming changesets and changegroups may fire twice,
1515 hooks operating on incoming changesets and changegroups may fire twice,
1515 once for the bundle fetched from the URL and another for any additional
1516 once for the bundle fetched from the URL and another for any additional
1516 data not fetched from this URL. In addition, if an error occurs, the
1517 data not fetched from this URL. In addition, if an error occurs, the
1517 repository may be rolled back to a partial clone. This behavior may
1518 repository may be rolled back to a partial clone. This behavior may
1518 change in future releases. See :hg:`help -e clonebundles` for more.
1519 change in future releases. See :hg:`help -e clonebundles` for more.
1519
1520
1520 Examples:
1521 Examples:
1521
1522
1522 - clone a remote repository to a new directory named hg/::
1523 - clone a remote repository to a new directory named hg/::
1523
1524
1524 hg clone https://www.mercurial-scm.org/repo/hg/
1525 hg clone https://www.mercurial-scm.org/repo/hg/
1525
1526
1526 - create a lightweight local clone::
1527 - create a lightweight local clone::
1527
1528
1528 hg clone project/ project-feature/
1529 hg clone project/ project-feature/
1529
1530
1530 - clone from an absolute path on an ssh server (note double-slash)::
1531 - clone from an absolute path on an ssh server (note double-slash)::
1531
1532
1532 hg clone ssh://user@server//home/projects/alpha/
1533 hg clone ssh://user@server//home/projects/alpha/
1533
1534
1534 - do a high-speed clone over a LAN while checking out a
1535 - do a high-speed clone over a LAN while checking out a
1535 specified version::
1536 specified version::
1536
1537
1537 hg clone --uncompressed http://server/repo -u 1.5
1538 hg clone --uncompressed http://server/repo -u 1.5
1538
1539
1539 - create a repository without changesets after a particular revision::
1540 - create a repository without changesets after a particular revision::
1540
1541
1541 hg clone -r 04e544 experimental/ good/
1542 hg clone -r 04e544 experimental/ good/
1542
1543
1543 - clone (and track) a particular named branch::
1544 - clone (and track) a particular named branch::
1544
1545
1545 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1546 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1546
1547
1547 See :hg:`help urls` for details on specifying URLs.
1548 See :hg:`help urls` for details on specifying URLs.
1548
1549
1549 Returns 0 on success.
1550 Returns 0 on success.
1550 """
1551 """
1551 if opts.get('noupdate') and opts.get('updaterev'):
1552 if opts.get('noupdate') and opts.get('updaterev'):
1552 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1553 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1553
1554
1554 r = hg.clone(ui, opts, source, dest,
1555 r = hg.clone(ui, opts, source, dest,
1555 pull=opts.get('pull'),
1556 pull=opts.get('pull'),
1556 stream=opts.get('uncompressed'),
1557 stream=opts.get('uncompressed'),
1557 rev=opts.get('rev'),
1558 rev=opts.get('rev'),
1558 update=opts.get('updaterev') or not opts.get('noupdate'),
1559 update=opts.get('updaterev') or not opts.get('noupdate'),
1559 branch=opts.get('branch'),
1560 branch=opts.get('branch'),
1560 shareopts=opts.get('shareopts'))
1561 shareopts=opts.get('shareopts'))
1561
1562
1562 return r is None
1563 return r is None
1563
1564
1564 @command('^commit|ci',
1565 @command('^commit|ci',
1565 [('A', 'addremove', None,
1566 [('A', 'addremove', None,
1566 _('mark new/missing files as added/removed before committing')),
1567 _('mark new/missing files as added/removed before committing')),
1567 ('', 'close-branch', None,
1568 ('', 'close-branch', None,
1568 _('mark a branch head as closed')),
1569 _('mark a branch head as closed')),
1569 ('', 'amend', None, _('amend the parent of the working directory')),
1570 ('', 'amend', None, _('amend the parent of the working directory')),
1570 ('s', 'secret', None, _('use the secret phase for committing')),
1571 ('s', 'secret', None, _('use the secret phase for committing')),
1571 ('e', 'edit', None, _('invoke editor on commit messages')),
1572 ('e', 'edit', None, _('invoke editor on commit messages')),
1572 ('i', 'interactive', None, _('use interactive mode')),
1573 ('i', 'interactive', None, _('use interactive mode')),
1573 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1574 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1574 _('[OPTION]... [FILE]...'),
1575 _('[OPTION]... [FILE]...'),
1575 inferrepo=True)
1576 inferrepo=True)
1576 def commit(ui, repo, *pats, **opts):
1577 def commit(ui, repo, *pats, **opts):
1577 """commit the specified files or all outstanding changes
1578 """commit the specified files or all outstanding changes
1578
1579
1579 Commit changes to the given files into the repository. Unlike a
1580 Commit changes to the given files into the repository. Unlike a
1580 centralized SCM, this operation is a local operation. See
1581 centralized SCM, this operation is a local operation. See
1581 :hg:`push` for a way to actively distribute your changes.
1582 :hg:`push` for a way to actively distribute your changes.
1582
1583
1583 If a list of files is omitted, all changes reported by :hg:`status`
1584 If a list of files is omitted, all changes reported by :hg:`status`
1584 will be committed.
1585 will be committed.
1585
1586
1586 If you are committing the result of a merge, do not provide any
1587 If you are committing the result of a merge, do not provide any
1587 filenames or -I/-X filters.
1588 filenames or -I/-X filters.
1588
1589
1589 If no commit message is specified, Mercurial starts your
1590 If no commit message is specified, Mercurial starts your
1590 configured editor where you can enter a message. In case your
1591 configured editor where you can enter a message. In case your
1591 commit fails, you will find a backup of your message in
1592 commit fails, you will find a backup of your message in
1592 ``.hg/last-message.txt``.
1593 ``.hg/last-message.txt``.
1593
1594
1594 The --close-branch flag can be used to mark the current branch
1595 The --close-branch flag can be used to mark the current branch
1595 head closed. When all heads of a branch are closed, the branch
1596 head closed. When all heads of a branch are closed, the branch
1596 will be considered closed and no longer listed.
1597 will be considered closed and no longer listed.
1597
1598
1598 The --amend flag can be used to amend the parent of the
1599 The --amend flag can be used to amend the parent of the
1599 working directory with a new commit that contains the changes
1600 working directory with a new commit that contains the changes
1600 in the parent in addition to those currently reported by :hg:`status`,
1601 in the parent in addition to those currently reported by :hg:`status`,
1601 if there are any. The old commit is stored in a backup bundle in
1602 if there are any. The old commit is stored in a backup bundle in
1602 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1603 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1603 on how to restore it).
1604 on how to restore it).
1604
1605
1605 Message, user and date are taken from the amended commit unless
1606 Message, user and date are taken from the amended commit unless
1606 specified. When a message isn't specified on the command line,
1607 specified. When a message isn't specified on the command line,
1607 the editor will open with the message of the amended commit.
1608 the editor will open with the message of the amended commit.
1608
1609
1609 It is not possible to amend public changesets (see :hg:`help phases`)
1610 It is not possible to amend public changesets (see :hg:`help phases`)
1610 or changesets that have children.
1611 or changesets that have children.
1611
1612
1612 See :hg:`help dates` for a list of formats valid for -d/--date.
1613 See :hg:`help dates` for a list of formats valid for -d/--date.
1613
1614
1614 Returns 0 on success, 1 if nothing changed.
1615 Returns 0 on success, 1 if nothing changed.
1615
1616
1616 .. container:: verbose
1617 .. container:: verbose
1617
1618
1618 Examples:
1619 Examples:
1619
1620
1620 - commit all files ending in .py::
1621 - commit all files ending in .py::
1621
1622
1622 hg commit --include "set:**.py"
1623 hg commit --include "set:**.py"
1623
1624
1624 - commit all non-binary files::
1625 - commit all non-binary files::
1625
1626
1626 hg commit --exclude "set:binary()"
1627 hg commit --exclude "set:binary()"
1627
1628
1628 - amend the current commit and set the date to now::
1629 - amend the current commit and set the date to now::
1629
1630
1630 hg commit --amend --date now
1631 hg commit --amend --date now
1631 """
1632 """
1632 wlock = lock = None
1633 wlock = lock = None
1633 try:
1634 try:
1634 wlock = repo.wlock()
1635 wlock = repo.wlock()
1635 lock = repo.lock()
1636 lock = repo.lock()
1636 return _docommit(ui, repo, *pats, **opts)
1637 return _docommit(ui, repo, *pats, **opts)
1637 finally:
1638 finally:
1638 release(lock, wlock)
1639 release(lock, wlock)
1639
1640
1640 def _docommit(ui, repo, *pats, **opts):
1641 def _docommit(ui, repo, *pats, **opts):
1641 if opts.get('interactive'):
1642 if opts.get('interactive'):
1642 opts.pop('interactive')
1643 opts.pop('interactive')
1643 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1644 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1644 cmdutil.recordfilter, *pats, **opts)
1645 cmdutil.recordfilter, *pats, **opts)
1645 # ret can be 0 (no changes to record) or the value returned by
1646 # ret can be 0 (no changes to record) or the value returned by
1646 # commit(), 1 if nothing changed or None on success.
1647 # commit(), 1 if nothing changed or None on success.
1647 return 1 if ret == 0 else ret
1648 return 1 if ret == 0 else ret
1648
1649
1649 if opts.get('subrepos'):
1650 if opts.get('subrepos'):
1650 if opts.get('amend'):
1651 if opts.get('amend'):
1651 raise error.Abort(_('cannot amend with --subrepos'))
1652 raise error.Abort(_('cannot amend with --subrepos'))
1652 # Let --subrepos on the command line override config setting.
1653 # Let --subrepos on the command line override config setting.
1653 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1654 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1654
1655
1655 cmdutil.checkunfinished(repo, commit=True)
1656 cmdutil.checkunfinished(repo, commit=True)
1656
1657
1657 branch = repo[None].branch()
1658 branch = repo[None].branch()
1658 bheads = repo.branchheads(branch)
1659 bheads = repo.branchheads(branch)
1659
1660
1660 extra = {}
1661 extra = {}
1661 if opts.get('close_branch'):
1662 if opts.get('close_branch'):
1662 extra['close'] = 1
1663 extra['close'] = 1
1663
1664
1664 if not bheads:
1665 if not bheads:
1665 raise error.Abort(_('can only close branch heads'))
1666 raise error.Abort(_('can only close branch heads'))
1666 elif opts.get('amend'):
1667 elif opts.get('amend'):
1667 if repo[None].parents()[0].p1().branch() != branch and \
1668 if repo[None].parents()[0].p1().branch() != branch and \
1668 repo[None].parents()[0].p2().branch() != branch:
1669 repo[None].parents()[0].p2().branch() != branch:
1669 raise error.Abort(_('can only close branch heads'))
1670 raise error.Abort(_('can only close branch heads'))
1670
1671
1671 if opts.get('amend'):
1672 if opts.get('amend'):
1672 if ui.configbool('ui', 'commitsubrepos'):
1673 if ui.configbool('ui', 'commitsubrepos'):
1673 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1674 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1674
1675
1675 old = repo['.']
1676 old = repo['.']
1676 if not old.mutable():
1677 if not old.mutable():
1677 raise error.Abort(_('cannot amend public changesets'))
1678 raise error.Abort(_('cannot amend public changesets'))
1678 if len(repo[None].parents()) > 1:
1679 if len(repo[None].parents()) > 1:
1679 raise error.Abort(_('cannot amend while merging'))
1680 raise error.Abort(_('cannot amend while merging'))
1680 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1681 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1681 if not allowunstable and old.children():
1682 if not allowunstable and old.children():
1682 raise error.Abort(_('cannot amend changeset with children'))
1683 raise error.Abort(_('cannot amend changeset with children'))
1683
1684
1684 # Currently histedit gets confused if an amend happens while histedit
1685 # Currently histedit gets confused if an amend happens while histedit
1685 # is in progress. Since we have a checkunfinished command, we are
1686 # is in progress. Since we have a checkunfinished command, we are
1686 # temporarily honoring it.
1687 # temporarily honoring it.
1687 #
1688 #
1688 # Note: eventually this guard will be removed. Please do not expect
1689 # Note: eventually this guard will be removed. Please do not expect
1689 # this behavior to remain.
1690 # this behavior to remain.
1690 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1691 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1691 cmdutil.checkunfinished(repo)
1692 cmdutil.checkunfinished(repo)
1692
1693
1693 # commitfunc is used only for temporary amend commit by cmdutil.amend
1694 # commitfunc is used only for temporary amend commit by cmdutil.amend
1694 def commitfunc(ui, repo, message, match, opts):
1695 def commitfunc(ui, repo, message, match, opts):
1695 return repo.commit(message,
1696 return repo.commit(message,
1696 opts.get('user') or old.user(),
1697 opts.get('user') or old.user(),
1697 opts.get('date') or old.date(),
1698 opts.get('date') or old.date(),
1698 match,
1699 match,
1699 extra=extra)
1700 extra=extra)
1700
1701
1701 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1702 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1702 if node == old.node():
1703 if node == old.node():
1703 ui.status(_("nothing changed\n"))
1704 ui.status(_("nothing changed\n"))
1704 return 1
1705 return 1
1705 else:
1706 else:
1706 def commitfunc(ui, repo, message, match, opts):
1707 def commitfunc(ui, repo, message, match, opts):
1707 backup = ui.backupconfig('phases', 'new-commit')
1708 backup = ui.backupconfig('phases', 'new-commit')
1708 baseui = repo.baseui
1709 baseui = repo.baseui
1709 basebackup = baseui.backupconfig('phases', 'new-commit')
1710 basebackup = baseui.backupconfig('phases', 'new-commit')
1710 try:
1711 try:
1711 if opts.get('secret'):
1712 if opts.get('secret'):
1712 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1713 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1713 # Propagate to subrepos
1714 # Propagate to subrepos
1714 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1715 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1715
1716
1716 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1717 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1717 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1718 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1718 return repo.commit(message, opts.get('user'), opts.get('date'),
1719 return repo.commit(message, opts.get('user'), opts.get('date'),
1719 match,
1720 match,
1720 editor=editor,
1721 editor=editor,
1721 extra=extra)
1722 extra=extra)
1722 finally:
1723 finally:
1723 ui.restoreconfig(backup)
1724 ui.restoreconfig(backup)
1724 repo.baseui.restoreconfig(basebackup)
1725 repo.baseui.restoreconfig(basebackup)
1725
1726
1726
1727
1727 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1728 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1728
1729
1729 if not node:
1730 if not node:
1730 stat = cmdutil.postcommitstatus(repo, pats, opts)
1731 stat = cmdutil.postcommitstatus(repo, pats, opts)
1731 if stat[3]:
1732 if stat[3]:
1732 ui.status(_("nothing changed (%d missing files, see "
1733 ui.status(_("nothing changed (%d missing files, see "
1733 "'hg status')\n") % len(stat[3]))
1734 "'hg status')\n") % len(stat[3]))
1734 else:
1735 else:
1735 ui.status(_("nothing changed\n"))
1736 ui.status(_("nothing changed\n"))
1736 return 1
1737 return 1
1737
1738
1738 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1739 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1739
1740
1740 @command('config|showconfig|debugconfig',
1741 @command('config|showconfig|debugconfig',
1741 [('u', 'untrusted', None, _('show untrusted configuration options')),
1742 [('u', 'untrusted', None, _('show untrusted configuration options')),
1742 ('e', 'edit', None, _('edit user config')),
1743 ('e', 'edit', None, _('edit user config')),
1743 ('l', 'local', None, _('edit repository config')),
1744 ('l', 'local', None, _('edit repository config')),
1744 ('g', 'global', None, _('edit global config'))] + formatteropts,
1745 ('g', 'global', None, _('edit global config'))] + formatteropts,
1745 _('[-u] [NAME]...'),
1746 _('[-u] [NAME]...'),
1746 optionalrepo=True)
1747 optionalrepo=True)
1747 def config(ui, repo, *values, **opts):
1748 def config(ui, repo, *values, **opts):
1748 """show combined config settings from all hgrc files
1749 """show combined config settings from all hgrc files
1749
1750
1750 With no arguments, print names and values of all config items.
1751 With no arguments, print names and values of all config items.
1751
1752
1752 With one argument of the form section.name, print just the value
1753 With one argument of the form section.name, print just the value
1753 of that config item.
1754 of that config item.
1754
1755
1755 With multiple arguments, print names and values of all config
1756 With multiple arguments, print names and values of all config
1756 items with matching section names.
1757 items with matching section names.
1757
1758
1758 With --edit, start an editor on the user-level config file. With
1759 With --edit, start an editor on the user-level config file. With
1759 --global, edit the system-wide config file. With --local, edit the
1760 --global, edit the system-wide config file. With --local, edit the
1760 repository-level config file.
1761 repository-level config file.
1761
1762
1762 With --debug, the source (filename and line number) is printed
1763 With --debug, the source (filename and line number) is printed
1763 for each config item.
1764 for each config item.
1764
1765
1765 See :hg:`help config` for more information about config files.
1766 See :hg:`help config` for more information about config files.
1766
1767
1767 Returns 0 on success, 1 if NAME does not exist.
1768 Returns 0 on success, 1 if NAME does not exist.
1768
1769
1769 """
1770 """
1770
1771
1771 if opts.get('edit') or opts.get('local') or opts.get('global'):
1772 if opts.get('edit') or opts.get('local') or opts.get('global'):
1772 if opts.get('local') and opts.get('global'):
1773 if opts.get('local') and opts.get('global'):
1773 raise error.Abort(_("can't use --local and --global together"))
1774 raise error.Abort(_("can't use --local and --global together"))
1774
1775
1775 if opts.get('local'):
1776 if opts.get('local'):
1776 if not repo:
1777 if not repo:
1777 raise error.Abort(_("can't use --local outside a repository"))
1778 raise error.Abort(_("can't use --local outside a repository"))
1778 paths = [repo.join('hgrc')]
1779 paths = [repo.join('hgrc')]
1779 elif opts.get('global'):
1780 elif opts.get('global'):
1780 paths = scmutil.systemrcpath()
1781 paths = scmutil.systemrcpath()
1781 else:
1782 else:
1782 paths = scmutil.userrcpath()
1783 paths = scmutil.userrcpath()
1783
1784
1784 for f in paths:
1785 for f in paths:
1785 if os.path.exists(f):
1786 if os.path.exists(f):
1786 break
1787 break
1787 else:
1788 else:
1788 if opts.get('global'):
1789 if opts.get('global'):
1789 samplehgrc = uimod.samplehgrcs['global']
1790 samplehgrc = uimod.samplehgrcs['global']
1790 elif opts.get('local'):
1791 elif opts.get('local'):
1791 samplehgrc = uimod.samplehgrcs['local']
1792 samplehgrc = uimod.samplehgrcs['local']
1792 else:
1793 else:
1793 samplehgrc = uimod.samplehgrcs['user']
1794 samplehgrc = uimod.samplehgrcs['user']
1794
1795
1795 f = paths[0]
1796 f = paths[0]
1796 fp = open(f, "w")
1797 fp = open(f, "w")
1797 fp.write(samplehgrc)
1798 fp.write(samplehgrc)
1798 fp.close()
1799 fp.close()
1799
1800
1800 editor = ui.geteditor()
1801 editor = ui.geteditor()
1801 ui.system("%s \"%s\"" % (editor, f),
1802 ui.system("%s \"%s\"" % (editor, f),
1802 onerr=error.Abort, errprefix=_("edit failed"))
1803 onerr=error.Abort, errprefix=_("edit failed"))
1803 return
1804 return
1804
1805
1805 fm = ui.formatter('config', opts)
1806 fm = ui.formatter('config', opts)
1806 for f in scmutil.rcpath():
1807 for f in scmutil.rcpath():
1807 ui.debug('read config from: %s\n' % f)
1808 ui.debug('read config from: %s\n' % f)
1808 untrusted = bool(opts.get('untrusted'))
1809 untrusted = bool(opts.get('untrusted'))
1809 if values:
1810 if values:
1810 sections = [v for v in values if '.' not in v]
1811 sections = [v for v in values if '.' not in v]
1811 items = [v for v in values if '.' in v]
1812 items = [v for v in values if '.' in v]
1812 if len(items) > 1 or items and sections:
1813 if len(items) > 1 or items and sections:
1813 raise error.Abort(_('only one config item permitted'))
1814 raise error.Abort(_('only one config item permitted'))
1814 matched = False
1815 matched = False
1815 for section, name, value in ui.walkconfig(untrusted=untrusted):
1816 for section, name, value in ui.walkconfig(untrusted=untrusted):
1816 value = str(value)
1817 value = str(value)
1817 if fm.isplain():
1818 if fm.isplain():
1818 value = value.replace('\n', '\\n')
1819 value = value.replace('\n', '\\n')
1819 entryname = section + '.' + name
1820 entryname = section + '.' + name
1820 if values:
1821 if values:
1821 for v in values:
1822 for v in values:
1822 if v == section:
1823 if v == section:
1823 fm.startitem()
1824 fm.startitem()
1824 fm.condwrite(ui.debugflag, 'source', '%s: ',
1825 fm.condwrite(ui.debugflag, 'source', '%s: ',
1825 ui.configsource(section, name, untrusted))
1826 ui.configsource(section, name, untrusted))
1826 fm.write('name value', '%s=%s\n', entryname, value)
1827 fm.write('name value', '%s=%s\n', entryname, value)
1827 matched = True
1828 matched = True
1828 elif v == entryname:
1829 elif v == entryname:
1829 fm.startitem()
1830 fm.startitem()
1830 fm.condwrite(ui.debugflag, 'source', '%s: ',
1831 fm.condwrite(ui.debugflag, 'source', '%s: ',
1831 ui.configsource(section, name, untrusted))
1832 ui.configsource(section, name, untrusted))
1832 fm.write('value', '%s\n', value)
1833 fm.write('value', '%s\n', value)
1833 fm.data(name=entryname)
1834 fm.data(name=entryname)
1834 matched = True
1835 matched = True
1835 else:
1836 else:
1836 fm.startitem()
1837 fm.startitem()
1837 fm.condwrite(ui.debugflag, 'source', '%s: ',
1838 fm.condwrite(ui.debugflag, 'source', '%s: ',
1838 ui.configsource(section, name, untrusted))
1839 ui.configsource(section, name, untrusted))
1839 fm.write('name value', '%s=%s\n', entryname, value)
1840 fm.write('name value', '%s=%s\n', entryname, value)
1840 matched = True
1841 matched = True
1841 fm.end()
1842 fm.end()
1842 if matched:
1843 if matched:
1843 return 0
1844 return 0
1844 return 1
1845 return 1
1845
1846
1846 @command('copy|cp',
1847 @command('copy|cp',
1847 [('A', 'after', None, _('record a copy that has already occurred')),
1848 [('A', 'after', None, _('record a copy that has already occurred')),
1848 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1849 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1849 ] + walkopts + dryrunopts,
1850 ] + walkopts + dryrunopts,
1850 _('[OPTION]... [SOURCE]... DEST'))
1851 _('[OPTION]... [SOURCE]... DEST'))
1851 def copy(ui, repo, *pats, **opts):
1852 def copy(ui, repo, *pats, **opts):
1852 """mark files as copied for the next commit
1853 """mark files as copied for the next commit
1853
1854
1854 Mark dest as having copies of source files. If dest is a
1855 Mark dest as having copies of source files. If dest is a
1855 directory, copies are put in that directory. If dest is a file,
1856 directory, copies are put in that directory. If dest is a file,
1856 the source must be a single file.
1857 the source must be a single file.
1857
1858
1858 By default, this command copies the contents of files as they
1859 By default, this command copies the contents of files as they
1859 exist in the working directory. If invoked with -A/--after, the
1860 exist in the working directory. If invoked with -A/--after, the
1860 operation is recorded, but no copying is performed.
1861 operation is recorded, but no copying is performed.
1861
1862
1862 This command takes effect with the next commit. To undo a copy
1863 This command takes effect with the next commit. To undo a copy
1863 before that, see :hg:`revert`.
1864 before that, see :hg:`revert`.
1864
1865
1865 Returns 0 on success, 1 if errors are encountered.
1866 Returns 0 on success, 1 if errors are encountered.
1866 """
1867 """
1867 with repo.wlock(False):
1868 with repo.wlock(False):
1868 return cmdutil.copy(ui, repo, pats, opts)
1869 return cmdutil.copy(ui, repo, pats, opts)
1869
1870
1870 @command('debugdag',
1871 @command('debugdag',
1871 [('t', 'tags', None, _('use tags as labels')),
1872 [('t', 'tags', None, _('use tags as labels')),
1872 ('b', 'branches', None, _('annotate with branch names')),
1873 ('b', 'branches', None, _('annotate with branch names')),
1873 ('', 'dots', None, _('use dots for runs')),
1874 ('', 'dots', None, _('use dots for runs')),
1874 ('s', 'spaces', None, _('separate elements by spaces'))],
1875 ('s', 'spaces', None, _('separate elements by spaces'))],
1875 _('[OPTION]... [FILE [REV]...]'),
1876 _('[OPTION]... [FILE [REV]...]'),
1876 optionalrepo=True)
1877 optionalrepo=True)
1877 def debugdag(ui, repo, file_=None, *revs, **opts):
1878 def debugdag(ui, repo, file_=None, *revs, **opts):
1878 """format the changelog or an index DAG as a concise textual description
1879 """format the changelog or an index DAG as a concise textual description
1879
1880
1880 If you pass a revlog index, the revlog's DAG is emitted. If you list
1881 If you pass a revlog index, the revlog's DAG is emitted. If you list
1881 revision numbers, they get labeled in the output as rN.
1882 revision numbers, they get labeled in the output as rN.
1882
1883
1883 Otherwise, the changelog DAG of the current repo is emitted.
1884 Otherwise, the changelog DAG of the current repo is emitted.
1884 """
1885 """
1885 spaces = opts.get('spaces')
1886 spaces = opts.get('spaces')
1886 dots = opts.get('dots')
1887 dots = opts.get('dots')
1887 if file_:
1888 if file_:
1888 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1889 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1889 revs = set((int(r) for r in revs))
1890 revs = set((int(r) for r in revs))
1890 def events():
1891 def events():
1891 for r in rlog:
1892 for r in rlog:
1892 yield 'n', (r, list(p for p in rlog.parentrevs(r)
1893 yield 'n', (r, list(p for p in rlog.parentrevs(r)
1893 if p != -1))
1894 if p != -1))
1894 if r in revs:
1895 if r in revs:
1895 yield 'l', (r, "r%i" % r)
1896 yield 'l', (r, "r%i" % r)
1896 elif repo:
1897 elif repo:
1897 cl = repo.changelog
1898 cl = repo.changelog
1898 tags = opts.get('tags')
1899 tags = opts.get('tags')
1899 branches = opts.get('branches')
1900 branches = opts.get('branches')
1900 if tags:
1901 if tags:
1901 labels = {}
1902 labels = {}
1902 for l, n in repo.tags().items():
1903 for l, n in repo.tags().items():
1903 labels.setdefault(cl.rev(n), []).append(l)
1904 labels.setdefault(cl.rev(n), []).append(l)
1904 def events():
1905 def events():
1905 b = "default"
1906 b = "default"
1906 for r in cl:
1907 for r in cl:
1907 if branches:
1908 if branches:
1908 newb = cl.read(cl.node(r))[5]['branch']
1909 newb = cl.read(cl.node(r))[5]['branch']
1909 if newb != b:
1910 if newb != b:
1910 yield 'a', newb
1911 yield 'a', newb
1911 b = newb
1912 b = newb
1912 yield 'n', (r, list(p for p in cl.parentrevs(r)
1913 yield 'n', (r, list(p for p in cl.parentrevs(r)
1913 if p != -1))
1914 if p != -1))
1914 if tags:
1915 if tags:
1915 ls = labels.get(r)
1916 ls = labels.get(r)
1916 if ls:
1917 if ls:
1917 for l in ls:
1918 for l in ls:
1918 yield 'l', (r, l)
1919 yield 'l', (r, l)
1919 else:
1920 else:
1920 raise error.Abort(_('need repo for changelog dag'))
1921 raise error.Abort(_('need repo for changelog dag'))
1921
1922
1922 for line in dagparser.dagtextlines(events(),
1923 for line in dagparser.dagtextlines(events(),
1923 addspaces=spaces,
1924 addspaces=spaces,
1924 wraplabels=True,
1925 wraplabels=True,
1925 wrapannotations=True,
1926 wrapannotations=True,
1926 wrapnonlinear=dots,
1927 wrapnonlinear=dots,
1927 usedots=dots,
1928 usedots=dots,
1928 maxlinewidth=70):
1929 maxlinewidth=70):
1929 ui.write(line)
1930 ui.write(line)
1930 ui.write("\n")
1931 ui.write("\n")
1931
1932
1932 @command('debugdata', debugrevlogopts, _('-c|-m|FILE REV'))
1933 @command('debugdata', debugrevlogopts, _('-c|-m|FILE REV'))
1933 def debugdata(ui, repo, file_, rev=None, **opts):
1934 def debugdata(ui, repo, file_, rev=None, **opts):
1934 """dump the contents of a data file revision"""
1935 """dump the contents of a data file revision"""
1935 if opts.get('changelog') or opts.get('manifest') or opts.get('dir'):
1936 if opts.get('changelog') or opts.get('manifest') or opts.get('dir'):
1936 if rev is not None:
1937 if rev is not None:
1937 raise error.CommandError('debugdata', _('invalid arguments'))
1938 raise error.CommandError('debugdata', _('invalid arguments'))
1938 file_, rev = None, file_
1939 file_, rev = None, file_
1939 elif rev is None:
1940 elif rev is None:
1940 raise error.CommandError('debugdata', _('invalid arguments'))
1941 raise error.CommandError('debugdata', _('invalid arguments'))
1941 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1942 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1942 try:
1943 try:
1943 ui.write(r.revision(r.lookup(rev)))
1944 ui.write(r.revision(r.lookup(rev)))
1944 except KeyError:
1945 except KeyError:
1945 raise error.Abort(_('invalid revision identifier %s') % rev)
1946 raise error.Abort(_('invalid revision identifier %s') % rev)
1946
1947
1947 @command('debugdate',
1948 @command('debugdate',
1948 [('e', 'extended', None, _('try extended date formats'))],
1949 [('e', 'extended', None, _('try extended date formats'))],
1949 _('[-e] DATE [RANGE]'),
1950 _('[-e] DATE [RANGE]'),
1950 norepo=True, optionalrepo=True)
1951 norepo=True, optionalrepo=True)
1951 def debugdate(ui, date, range=None, **opts):
1952 def debugdate(ui, date, range=None, **opts):
1952 """parse and display a date"""
1953 """parse and display a date"""
1953 if opts["extended"]:
1954 if opts["extended"]:
1954 d = util.parsedate(date, util.extendeddateformats)
1955 d = util.parsedate(date, util.extendeddateformats)
1955 else:
1956 else:
1956 d = util.parsedate(date)
1957 d = util.parsedate(date)
1957 ui.write(("internal: %s %s\n") % d)
1958 ui.write(("internal: %s %s\n") % d)
1958 ui.write(("standard: %s\n") % util.datestr(d))
1959 ui.write(("standard: %s\n") % util.datestr(d))
1959 if range:
1960 if range:
1960 m = util.matchdate(range)
1961 m = util.matchdate(range)
1961 ui.write(("match: %s\n") % m(d[0]))
1962 ui.write(("match: %s\n") % m(d[0]))
1962
1963
1963 @command('debugdiscovery',
1964 @command('debugdiscovery',
1964 [('', 'old', None, _('use old-style discovery')),
1965 [('', 'old', None, _('use old-style discovery')),
1965 ('', 'nonheads', None,
1966 ('', 'nonheads', None,
1966 _('use old-style discovery with non-heads included')),
1967 _('use old-style discovery with non-heads included')),
1967 ] + remoteopts,
1968 ] + remoteopts,
1968 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1969 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1969 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1970 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1970 """runs the changeset discovery protocol in isolation"""
1971 """runs the changeset discovery protocol in isolation"""
1971 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
1972 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
1972 opts.get('branch'))
1973 opts.get('branch'))
1973 remote = hg.peer(repo, opts, remoteurl)
1974 remote = hg.peer(repo, opts, remoteurl)
1974 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1975 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1975
1976
1976 # make sure tests are repeatable
1977 # make sure tests are repeatable
1977 random.seed(12323)
1978 random.seed(12323)
1978
1979
1979 def doit(localheads, remoteheads, remote=remote):
1980 def doit(localheads, remoteheads, remote=remote):
1980 if opts.get('old'):
1981 if opts.get('old'):
1981 if localheads:
1982 if localheads:
1982 raise error.Abort('cannot use localheads with old style '
1983 raise error.Abort('cannot use localheads with old style '
1983 'discovery')
1984 'discovery')
1984 if not util.safehasattr(remote, 'branches'):
1985 if not util.safehasattr(remote, 'branches'):
1985 # enable in-client legacy support
1986 # enable in-client legacy support
1986 remote = localrepo.locallegacypeer(remote.local())
1987 remote = localrepo.locallegacypeer(remote.local())
1987 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1988 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1988 force=True)
1989 force=True)
1989 common = set(common)
1990 common = set(common)
1990 if not opts.get('nonheads'):
1991 if not opts.get('nonheads'):
1991 ui.write(("unpruned common: %s\n") %
1992 ui.write(("unpruned common: %s\n") %
1992 " ".join(sorted(short(n) for n in common)))
1993 " ".join(sorted(short(n) for n in common)))
1993 dag = dagutil.revlogdag(repo.changelog)
1994 dag = dagutil.revlogdag(repo.changelog)
1994 all = dag.ancestorset(dag.internalizeall(common))
1995 all = dag.ancestorset(dag.internalizeall(common))
1995 common = dag.externalizeall(dag.headsetofconnecteds(all))
1996 common = dag.externalizeall(dag.headsetofconnecteds(all))
1996 else:
1997 else:
1997 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1998 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1998 common = set(common)
1999 common = set(common)
1999 rheads = set(hds)
2000 rheads = set(hds)
2000 lheads = set(repo.heads())
2001 lheads = set(repo.heads())
2001 ui.write(("common heads: %s\n") %
2002 ui.write(("common heads: %s\n") %
2002 " ".join(sorted(short(n) for n in common)))
2003 " ".join(sorted(short(n) for n in common)))
2003 if lheads <= common:
2004 if lheads <= common:
2004 ui.write(("local is subset\n"))
2005 ui.write(("local is subset\n"))
2005 elif rheads <= common:
2006 elif rheads <= common:
2006 ui.write(("remote is subset\n"))
2007 ui.write(("remote is subset\n"))
2007
2008
2008 serverlogs = opts.get('serverlog')
2009 serverlogs = opts.get('serverlog')
2009 if serverlogs:
2010 if serverlogs:
2010 for filename in serverlogs:
2011 for filename in serverlogs:
2011 with open(filename, 'r') as logfile:
2012 with open(filename, 'r') as logfile:
2012 line = logfile.readline()
2013 line = logfile.readline()
2013 while line:
2014 while line:
2014 parts = line.strip().split(';')
2015 parts = line.strip().split(';')
2015 op = parts[1]
2016 op = parts[1]
2016 if op == 'cg':
2017 if op == 'cg':
2017 pass
2018 pass
2018 elif op == 'cgss':
2019 elif op == 'cgss':
2019 doit(parts[2].split(' '), parts[3].split(' '))
2020 doit(parts[2].split(' '), parts[3].split(' '))
2020 elif op == 'unb':
2021 elif op == 'unb':
2021 doit(parts[3].split(' '), parts[2].split(' '))
2022 doit(parts[3].split(' '), parts[2].split(' '))
2022 line = logfile.readline()
2023 line = logfile.readline()
2023 else:
2024 else:
2024 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2025 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2025 opts.get('remote_head'))
2026 opts.get('remote_head'))
2026 localrevs = opts.get('local_head')
2027 localrevs = opts.get('local_head')
2027 doit(localrevs, remoterevs)
2028 doit(localrevs, remoterevs)
2028
2029
2029 @command('debugextensions', formatteropts, [], norepo=True)
2030 @command('debugextensions', formatteropts, [], norepo=True)
2030 def debugextensions(ui, **opts):
2031 def debugextensions(ui, **opts):
2031 '''show information about active extensions'''
2032 '''show information about active extensions'''
2032 exts = extensions.extensions(ui)
2033 exts = extensions.extensions(ui)
2033 hgver = util.version()
2034 hgver = util.version()
2034 fm = ui.formatter('debugextensions', opts)
2035 fm = ui.formatter('debugextensions', opts)
2035 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
2036 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
2036 isinternal = extensions.ismoduleinternal(extmod)
2037 isinternal = extensions.ismoduleinternal(extmod)
2037 extsource = extmod.__file__
2038 extsource = extmod.__file__
2038 if isinternal:
2039 if isinternal:
2039 exttestedwith = [] # never expose magic string to users
2040 exttestedwith = [] # never expose magic string to users
2040 else:
2041 else:
2041 exttestedwith = getattr(extmod, 'testedwith', '').split()
2042 exttestedwith = getattr(extmod, 'testedwith', '').split()
2042 extbuglink = getattr(extmod, 'buglink', None)
2043 extbuglink = getattr(extmod, 'buglink', None)
2043
2044
2044 fm.startitem()
2045 fm.startitem()
2045
2046
2046 if ui.quiet or ui.verbose:
2047 if ui.quiet or ui.verbose:
2047 fm.write('name', '%s\n', extname)
2048 fm.write('name', '%s\n', extname)
2048 else:
2049 else:
2049 fm.write('name', '%s', extname)
2050 fm.write('name', '%s', extname)
2050 if isinternal or hgver in exttestedwith:
2051 if isinternal or hgver in exttestedwith:
2051 fm.plain('\n')
2052 fm.plain('\n')
2052 elif not exttestedwith:
2053 elif not exttestedwith:
2053 fm.plain(_(' (untested!)\n'))
2054 fm.plain(_(' (untested!)\n'))
2054 else:
2055 else:
2055 lasttestedversion = exttestedwith[-1]
2056 lasttestedversion = exttestedwith[-1]
2056 fm.plain(' (%s!)\n' % lasttestedversion)
2057 fm.plain(' (%s!)\n' % lasttestedversion)
2057
2058
2058 fm.condwrite(ui.verbose and extsource, 'source',
2059 fm.condwrite(ui.verbose and extsource, 'source',
2059 _(' location: %s\n'), extsource or "")
2060 _(' location: %s\n'), extsource or "")
2060
2061
2061 if ui.verbose:
2062 if ui.verbose:
2062 fm.plain(_(' bundled: %s\n') % ['no', 'yes'][isinternal])
2063 fm.plain(_(' bundled: %s\n') % ['no', 'yes'][isinternal])
2063 fm.data(bundled=isinternal)
2064 fm.data(bundled=isinternal)
2064
2065
2065 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
2066 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
2066 _(' tested with: %s\n'),
2067 _(' tested with: %s\n'),
2067 fm.formatlist(exttestedwith, name='ver'))
2068 fm.formatlist(exttestedwith, name='ver'))
2068
2069
2069 fm.condwrite(ui.verbose and extbuglink, 'buglink',
2070 fm.condwrite(ui.verbose and extbuglink, 'buglink',
2070 _(' bug reporting: %s\n'), extbuglink or "")
2071 _(' bug reporting: %s\n'), extbuglink or "")
2071
2072
2072 fm.end()
2073 fm.end()
2073
2074
2074 @command('debugfileset',
2075 @command('debugfileset',
2075 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2076 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2076 _('[-r REV] FILESPEC'))
2077 _('[-r REV] FILESPEC'))
2077 def debugfileset(ui, repo, expr, **opts):
2078 def debugfileset(ui, repo, expr, **opts):
2078 '''parse and apply a fileset specification'''
2079 '''parse and apply a fileset specification'''
2079 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2080 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2080 if ui.verbose:
2081 if ui.verbose:
2081 tree = fileset.parse(expr)
2082 tree = fileset.parse(expr)
2082 ui.note(fileset.prettyformat(tree), "\n")
2083 ui.note(fileset.prettyformat(tree), "\n")
2083
2084
2084 for f in ctx.getfileset(expr):
2085 for f in ctx.getfileset(expr):
2085 ui.write("%s\n" % f)
2086 ui.write("%s\n" % f)
2086
2087
2087 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2088 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2088 def debugfsinfo(ui, path="."):
2089 def debugfsinfo(ui, path="."):
2089 """show information detected about current filesystem"""
2090 """show information detected about current filesystem"""
2090 util.writefile('.debugfsinfo', '')
2091 util.writefile('.debugfsinfo', '')
2091 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2092 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2092 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2093 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2093 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2094 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2094 ui.write(('case-sensitive: %s\n') % (util.fscasesensitive('.debugfsinfo')
2095 ui.write(('case-sensitive: %s\n') % (util.fscasesensitive('.debugfsinfo')
2095 and 'yes' or 'no'))
2096 and 'yes' or 'no'))
2096 os.unlink('.debugfsinfo')
2097 os.unlink('.debugfsinfo')
2097
2098
2098 @command('debuggetbundle',
2099 @command('debuggetbundle',
2099 [('H', 'head', [], _('id of head node'), _('ID')),
2100 [('H', 'head', [], _('id of head node'), _('ID')),
2100 ('C', 'common', [], _('id of common node'), _('ID')),
2101 ('C', 'common', [], _('id of common node'), _('ID')),
2101 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2102 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2102 _('REPO FILE [-H|-C ID]...'),
2103 _('REPO FILE [-H|-C ID]...'),
2103 norepo=True)
2104 norepo=True)
2104 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2105 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2105 """retrieves a bundle from a repo
2106 """retrieves a bundle from a repo
2106
2107
2107 Every ID must be a full-length hex node id string. Saves the bundle to the
2108 Every ID must be a full-length hex node id string. Saves the bundle to the
2108 given file.
2109 given file.
2109 """
2110 """
2110 repo = hg.peer(ui, opts, repopath)
2111 repo = hg.peer(ui, opts, repopath)
2111 if not repo.capable('getbundle'):
2112 if not repo.capable('getbundle'):
2112 raise error.Abort("getbundle() not supported by target repository")
2113 raise error.Abort("getbundle() not supported by target repository")
2113 args = {}
2114 args = {}
2114 if common:
2115 if common:
2115 args['common'] = [bin(s) for s in common]
2116 args['common'] = [bin(s) for s in common]
2116 if head:
2117 if head:
2117 args['heads'] = [bin(s) for s in head]
2118 args['heads'] = [bin(s) for s in head]
2118 # TODO: get desired bundlecaps from command line.
2119 # TODO: get desired bundlecaps from command line.
2119 args['bundlecaps'] = None
2120 args['bundlecaps'] = None
2120 bundle = repo.getbundle('debug', **args)
2121 bundle = repo.getbundle('debug', **args)
2121
2122
2122 bundletype = opts.get('type', 'bzip2').lower()
2123 bundletype = opts.get('type', 'bzip2').lower()
2123 btypes = {'none': 'HG10UN',
2124 btypes = {'none': 'HG10UN',
2124 'bzip2': 'HG10BZ',
2125 'bzip2': 'HG10BZ',
2125 'gzip': 'HG10GZ',
2126 'gzip': 'HG10GZ',
2126 'bundle2': 'HG20'}
2127 'bundle2': 'HG20'}
2127 bundletype = btypes.get(bundletype)
2128 bundletype = btypes.get(bundletype)
2128 if bundletype not in bundle2.bundletypes:
2129 if bundletype not in bundle2.bundletypes:
2129 raise error.Abort(_('unknown bundle type specified with --type'))
2130 raise error.Abort(_('unknown bundle type specified with --type'))
2130 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
2131 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
2131
2132
2132 @command('debugignore', [], '[FILE]')
2133 @command('debugignore', [], '[FILE]')
2133 def debugignore(ui, repo, *files, **opts):
2134 def debugignore(ui, repo, *files, **opts):
2134 """display the combined ignore pattern and information about ignored files
2135 """display the combined ignore pattern and information about ignored files
2135
2136
2136 With no argument display the combined ignore pattern.
2137 With no argument display the combined ignore pattern.
2137
2138
2138 Given space separated file names, shows if the given file is ignored and
2139 Given space separated file names, shows if the given file is ignored and
2139 if so, show the ignore rule (file and line number) that matched it.
2140 if so, show the ignore rule (file and line number) that matched it.
2140 """
2141 """
2141 ignore = repo.dirstate._ignore
2142 ignore = repo.dirstate._ignore
2142 if not files:
2143 if not files:
2143 # Show all the patterns
2144 # Show all the patterns
2144 includepat = getattr(ignore, 'includepat', None)
2145 includepat = getattr(ignore, 'includepat', None)
2145 if includepat is not None:
2146 if includepat is not None:
2146 ui.write("%s\n" % includepat)
2147 ui.write("%s\n" % includepat)
2147 else:
2148 else:
2148 raise error.Abort(_("no ignore patterns found"))
2149 raise error.Abort(_("no ignore patterns found"))
2149 else:
2150 else:
2150 for f in files:
2151 for f in files:
2151 nf = util.normpath(f)
2152 nf = util.normpath(f)
2152 ignored = None
2153 ignored = None
2153 ignoredata = None
2154 ignoredata = None
2154 if nf != '.':
2155 if nf != '.':
2155 if ignore(nf):
2156 if ignore(nf):
2156 ignored = nf
2157 ignored = nf
2157 ignoredata = repo.dirstate._ignorefileandline(nf)
2158 ignoredata = repo.dirstate._ignorefileandline(nf)
2158 else:
2159 else:
2159 for p in util.finddirs(nf):
2160 for p in util.finddirs(nf):
2160 if ignore(p):
2161 if ignore(p):
2161 ignored = p
2162 ignored = p
2162 ignoredata = repo.dirstate._ignorefileandline(p)
2163 ignoredata = repo.dirstate._ignorefileandline(p)
2163 break
2164 break
2164 if ignored:
2165 if ignored:
2165 if ignored == nf:
2166 if ignored == nf:
2166 ui.write(_("%s is ignored\n") % f)
2167 ui.write(_("%s is ignored\n") % f)
2167 else:
2168 else:
2168 ui.write(_("%s is ignored because of "
2169 ui.write(_("%s is ignored because of "
2169 "containing folder %s\n")
2170 "containing folder %s\n")
2170 % (f, ignored))
2171 % (f, ignored))
2171 ignorefile, lineno, line = ignoredata
2172 ignorefile, lineno, line = ignoredata
2172 ui.write(_("(ignore rule in %s, line %d: '%s')\n")
2173 ui.write(_("(ignore rule in %s, line %d: '%s')\n")
2173 % (ignorefile, lineno, line))
2174 % (ignorefile, lineno, line))
2174 else:
2175 else:
2175 ui.write(_("%s is not ignored\n") % f)
2176 ui.write(_("%s is not ignored\n") % f)
2176
2177
2177 @command('debugindex', debugrevlogopts +
2178 @command('debugindex', debugrevlogopts +
2178 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2179 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2179 _('[-f FORMAT] -c|-m|FILE'),
2180 _('[-f FORMAT] -c|-m|FILE'),
2180 optionalrepo=True)
2181 optionalrepo=True)
2181 def debugindex(ui, repo, file_=None, **opts):
2182 def debugindex(ui, repo, file_=None, **opts):
2182 """dump the contents of an index file"""
2183 """dump the contents of an index file"""
2183 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2184 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2184 format = opts.get('format', 0)
2185 format = opts.get('format', 0)
2185 if format not in (0, 1):
2186 if format not in (0, 1):
2186 raise error.Abort(_("unknown format %d") % format)
2187 raise error.Abort(_("unknown format %d") % format)
2187
2188
2188 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2189 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2189 if generaldelta:
2190 if generaldelta:
2190 basehdr = ' delta'
2191 basehdr = ' delta'
2191 else:
2192 else:
2192 basehdr = ' base'
2193 basehdr = ' base'
2193
2194
2194 if ui.debugflag:
2195 if ui.debugflag:
2195 shortfn = hex
2196 shortfn = hex
2196 else:
2197 else:
2197 shortfn = short
2198 shortfn = short
2198
2199
2199 # There might not be anything in r, so have a sane default
2200 # There might not be anything in r, so have a sane default
2200 idlen = 12
2201 idlen = 12
2201 for i in r:
2202 for i in r:
2202 idlen = len(shortfn(r.node(i)))
2203 idlen = len(shortfn(r.node(i)))
2203 break
2204 break
2204
2205
2205 if format == 0:
2206 if format == 0:
2206 ui.write((" rev offset length " + basehdr + " linkrev"
2207 ui.write((" rev offset length " + basehdr + " linkrev"
2207 " %s %s p2\n") % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2208 " %s %s p2\n") % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2208 elif format == 1:
2209 elif format == 1:
2209 ui.write((" rev flag offset length"
2210 ui.write((" rev flag offset length"
2210 " size " + basehdr + " link p1 p2"
2211 " size " + basehdr + " link p1 p2"
2211 " %s\n") % "nodeid".rjust(idlen))
2212 " %s\n") % "nodeid".rjust(idlen))
2212
2213
2213 for i in r:
2214 for i in r:
2214 node = r.node(i)
2215 node = r.node(i)
2215 if generaldelta:
2216 if generaldelta:
2216 base = r.deltaparent(i)
2217 base = r.deltaparent(i)
2217 else:
2218 else:
2218 base = r.chainbase(i)
2219 base = r.chainbase(i)
2219 if format == 0:
2220 if format == 0:
2220 try:
2221 try:
2221 pp = r.parents(node)
2222 pp = r.parents(node)
2222 except Exception:
2223 except Exception:
2223 pp = [nullid, nullid]
2224 pp = [nullid, nullid]
2224 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2225 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2225 i, r.start(i), r.length(i), base, r.linkrev(i),
2226 i, r.start(i), r.length(i), base, r.linkrev(i),
2226 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2227 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2227 elif format == 1:
2228 elif format == 1:
2228 pr = r.parentrevs(i)
2229 pr = r.parentrevs(i)
2229 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2230 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2230 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2231 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2231 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2232 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2232
2233
2233 @command('debugindexdot', debugrevlogopts,
2234 @command('debugindexdot', debugrevlogopts,
2234 _('-c|-m|FILE'), optionalrepo=True)
2235 _('-c|-m|FILE'), optionalrepo=True)
2235 def debugindexdot(ui, repo, file_=None, **opts):
2236 def debugindexdot(ui, repo, file_=None, **opts):
2236 """dump an index DAG as a graphviz dot file"""
2237 """dump an index DAG as a graphviz dot file"""
2237 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
2238 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
2238 ui.write(("digraph G {\n"))
2239 ui.write(("digraph G {\n"))
2239 for i in r:
2240 for i in r:
2240 node = r.node(i)
2241 node = r.node(i)
2241 pp = r.parents(node)
2242 pp = r.parents(node)
2242 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2243 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2243 if pp[1] != nullid:
2244 if pp[1] != nullid:
2244 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2245 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2245 ui.write("}\n")
2246 ui.write("}\n")
2246
2247
2247 @command('debugdeltachain',
2248 @command('debugdeltachain',
2248 debugrevlogopts + formatteropts,
2249 debugrevlogopts + formatteropts,
2249 _('-c|-m|FILE'),
2250 _('-c|-m|FILE'),
2250 optionalrepo=True)
2251 optionalrepo=True)
2251 def debugdeltachain(ui, repo, file_=None, **opts):
2252 def debugdeltachain(ui, repo, file_=None, **opts):
2252 """dump information about delta chains in a revlog
2253 """dump information about delta chains in a revlog
2253
2254
2254 Output can be templatized. Available template keywords are:
2255 Output can be templatized. Available template keywords are:
2255
2256
2256 :``rev``: revision number
2257 :``rev``: revision number
2257 :``chainid``: delta chain identifier (numbered by unique base)
2258 :``chainid``: delta chain identifier (numbered by unique base)
2258 :``chainlen``: delta chain length to this revision
2259 :``chainlen``: delta chain length to this revision
2259 :``prevrev``: previous revision in delta chain
2260 :``prevrev``: previous revision in delta chain
2260 :``deltatype``: role of delta / how it was computed
2261 :``deltatype``: role of delta / how it was computed
2261 :``compsize``: compressed size of revision
2262 :``compsize``: compressed size of revision
2262 :``uncompsize``: uncompressed size of revision
2263 :``uncompsize``: uncompressed size of revision
2263 :``chainsize``: total size of compressed revisions in chain
2264 :``chainsize``: total size of compressed revisions in chain
2264 :``chainratio``: total chain size divided by uncompressed revision size
2265 :``chainratio``: total chain size divided by uncompressed revision size
2265 (new delta chains typically start at ratio 2.00)
2266 (new delta chains typically start at ratio 2.00)
2266 :``lindist``: linear distance from base revision in delta chain to end
2267 :``lindist``: linear distance from base revision in delta chain to end
2267 of this revision
2268 of this revision
2268 :``extradist``: total size of revisions not part of this delta chain from
2269 :``extradist``: total size of revisions not part of this delta chain from
2269 base of delta chain to end of this revision; a measurement
2270 base of delta chain to end of this revision; a measurement
2270 of how much extra data we need to read/seek across to read
2271 of how much extra data we need to read/seek across to read
2271 the delta chain for this revision
2272 the delta chain for this revision
2272 :``extraratio``: extradist divided by chainsize; another representation of
2273 :``extraratio``: extradist divided by chainsize; another representation of
2273 how much unrelated data is needed to load this delta chain
2274 how much unrelated data is needed to load this delta chain
2274 """
2275 """
2275 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
2276 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
2276 index = r.index
2277 index = r.index
2277 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2278 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2278
2279
2279 def revinfo(rev):
2280 def revinfo(rev):
2280 e = index[rev]
2281 e = index[rev]
2281 compsize = e[1]
2282 compsize = e[1]
2282 uncompsize = e[2]
2283 uncompsize = e[2]
2283 chainsize = 0
2284 chainsize = 0
2284
2285
2285 if generaldelta:
2286 if generaldelta:
2286 if e[3] == e[5]:
2287 if e[3] == e[5]:
2287 deltatype = 'p1'
2288 deltatype = 'p1'
2288 elif e[3] == e[6]:
2289 elif e[3] == e[6]:
2289 deltatype = 'p2'
2290 deltatype = 'p2'
2290 elif e[3] == rev - 1:
2291 elif e[3] == rev - 1:
2291 deltatype = 'prev'
2292 deltatype = 'prev'
2292 elif e[3] == rev:
2293 elif e[3] == rev:
2293 deltatype = 'base'
2294 deltatype = 'base'
2294 else:
2295 else:
2295 deltatype = 'other'
2296 deltatype = 'other'
2296 else:
2297 else:
2297 if e[3] == rev:
2298 if e[3] == rev:
2298 deltatype = 'base'
2299 deltatype = 'base'
2299 else:
2300 else:
2300 deltatype = 'prev'
2301 deltatype = 'prev'
2301
2302
2302 chain = r._deltachain(rev)[0]
2303 chain = r._deltachain(rev)[0]
2303 for iterrev in chain:
2304 for iterrev in chain:
2304 e = index[iterrev]
2305 e = index[iterrev]
2305 chainsize += e[1]
2306 chainsize += e[1]
2306
2307
2307 return compsize, uncompsize, deltatype, chain, chainsize
2308 return compsize, uncompsize, deltatype, chain, chainsize
2308
2309
2309 fm = ui.formatter('debugdeltachain', opts)
2310 fm = ui.formatter('debugdeltachain', opts)
2310
2311
2311 fm.plain(' rev chain# chainlen prev delta '
2312 fm.plain(' rev chain# chainlen prev delta '
2312 'size rawsize chainsize ratio lindist extradist '
2313 'size rawsize chainsize ratio lindist extradist '
2313 'extraratio\n')
2314 'extraratio\n')
2314
2315
2315 chainbases = {}
2316 chainbases = {}
2316 for rev in r:
2317 for rev in r:
2317 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
2318 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
2318 chainbase = chain[0]
2319 chainbase = chain[0]
2319 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
2320 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
2320 basestart = r.start(chainbase)
2321 basestart = r.start(chainbase)
2321 revstart = r.start(rev)
2322 revstart = r.start(rev)
2322 lineardist = revstart + comp - basestart
2323 lineardist = revstart + comp - basestart
2323 extradist = lineardist - chainsize
2324 extradist = lineardist - chainsize
2324 try:
2325 try:
2325 prevrev = chain[-2]
2326 prevrev = chain[-2]
2326 except IndexError:
2327 except IndexError:
2327 prevrev = -1
2328 prevrev = -1
2328
2329
2329 chainratio = float(chainsize) / float(uncomp)
2330 chainratio = float(chainsize) / float(uncomp)
2330 extraratio = float(extradist) / float(chainsize)
2331 extraratio = float(extradist) / float(chainsize)
2331
2332
2332 fm.startitem()
2333 fm.startitem()
2333 fm.write('rev chainid chainlen prevrev deltatype compsize '
2334 fm.write('rev chainid chainlen prevrev deltatype compsize '
2334 'uncompsize chainsize chainratio lindist extradist '
2335 'uncompsize chainsize chainratio lindist extradist '
2335 'extraratio',
2336 'extraratio',
2336 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f\n',
2337 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f\n',
2337 rev, chainid, len(chain), prevrev, deltatype, comp,
2338 rev, chainid, len(chain), prevrev, deltatype, comp,
2338 uncomp, chainsize, chainratio, lineardist, extradist,
2339 uncomp, chainsize, chainratio, lineardist, extradist,
2339 extraratio,
2340 extraratio,
2340 rev=rev, chainid=chainid, chainlen=len(chain),
2341 rev=rev, chainid=chainid, chainlen=len(chain),
2341 prevrev=prevrev, deltatype=deltatype, compsize=comp,
2342 prevrev=prevrev, deltatype=deltatype, compsize=comp,
2342 uncompsize=uncomp, chainsize=chainsize,
2343 uncompsize=uncomp, chainsize=chainsize,
2343 chainratio=chainratio, lindist=lineardist,
2344 chainratio=chainratio, lindist=lineardist,
2344 extradist=extradist, extraratio=extraratio)
2345 extradist=extradist, extraratio=extraratio)
2345
2346
2346 fm.end()
2347 fm.end()
2347
2348
2348 @command('debuginstall', [] + formatteropts, '', norepo=True)
2349 @command('debuginstall', [] + formatteropts, '', norepo=True)
2349 def debuginstall(ui, **opts):
2350 def debuginstall(ui, **opts):
2350 '''test Mercurial installation
2351 '''test Mercurial installation
2351
2352
2352 Returns 0 on success.
2353 Returns 0 on success.
2353 '''
2354 '''
2354
2355
2355 def writetemp(contents):
2356 def writetemp(contents):
2356 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2357 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2357 f = os.fdopen(fd, "wb")
2358 f = os.fdopen(fd, "wb")
2358 f.write(contents)
2359 f.write(contents)
2359 f.close()
2360 f.close()
2360 return name
2361 return name
2361
2362
2362 problems = 0
2363 problems = 0
2363
2364
2364 fm = ui.formatter('debuginstall', opts)
2365 fm = ui.formatter('debuginstall', opts)
2365 fm.startitem()
2366 fm.startitem()
2366
2367
2367 # encoding
2368 # encoding
2368 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
2369 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
2369 err = None
2370 err = None
2370 try:
2371 try:
2371 encoding.fromlocal("test")
2372 encoding.fromlocal("test")
2372 except error.Abort as inst:
2373 except error.Abort as inst:
2373 err = inst
2374 err = inst
2374 problems += 1
2375 problems += 1
2375 fm.condwrite(err, 'encodingerror', _(" %s\n"
2376 fm.condwrite(err, 'encodingerror', _(" %s\n"
2376 " (check that your locale is properly set)\n"), err)
2377 " (check that your locale is properly set)\n"), err)
2377
2378
2378 # Python
2379 # Python
2379 fm.write('pythonexe', _("checking Python executable (%s)\n"),
2380 fm.write('pythonexe', _("checking Python executable (%s)\n"),
2380 sys.executable)
2381 sys.executable)
2381 fm.write('pythonver', _("checking Python version (%s)\n"),
2382 fm.write('pythonver', _("checking Python version (%s)\n"),
2382 ("%s.%s.%s" % sys.version_info[:3]))
2383 ("%s.%s.%s" % sys.version_info[:3]))
2383 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
2384 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
2384 os.path.dirname(os.__file__))
2385 os.path.dirname(os.__file__))
2385
2386
2386 security = set(sslutil.supportedprotocols)
2387 security = set(sslutil.supportedprotocols)
2387 if sslutil.hassni:
2388 if sslutil.hassni:
2388 security.add('sni')
2389 security.add('sni')
2389
2390
2390 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
2391 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
2391 fm.formatlist(sorted(security), name='protocol',
2392 fm.formatlist(sorted(security), name='protocol',
2392 fmt='%s', sep=','))
2393 fmt='%s', sep=','))
2393
2394
2394 # These are warnings, not errors. So don't increment problem count. This
2395 # These are warnings, not errors. So don't increment problem count. This
2395 # may change in the future.
2396 # may change in the future.
2396 if 'tls1.2' not in security:
2397 if 'tls1.2' not in security:
2397 fm.plain(_(' TLS 1.2 not supported by Python install; '
2398 fm.plain(_(' TLS 1.2 not supported by Python install; '
2398 'network connections lack modern security\n'))
2399 'network connections lack modern security\n'))
2399 if 'sni' not in security:
2400 if 'sni' not in security:
2400 fm.plain(_(' SNI not supported by Python install; may have '
2401 fm.plain(_(' SNI not supported by Python install; may have '
2401 'connectivity issues with some servers\n'))
2402 'connectivity issues with some servers\n'))
2402
2403
2403 # TODO print CA cert info
2404 # TODO print CA cert info
2404
2405
2405 # hg version
2406 # hg version
2406 hgver = util.version()
2407 hgver = util.version()
2407 fm.write('hgver', _("checking Mercurial version (%s)\n"),
2408 fm.write('hgver', _("checking Mercurial version (%s)\n"),
2408 hgver.split('+')[0])
2409 hgver.split('+')[0])
2409 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
2410 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
2410 '+'.join(hgver.split('+')[1:]))
2411 '+'.join(hgver.split('+')[1:]))
2411
2412
2412 # compiled modules
2413 # compiled modules
2413 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
2414 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
2414 policy.policy)
2415 policy.policy)
2415 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
2416 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
2416 os.path.dirname(__file__))
2417 os.path.dirname(__file__))
2417
2418
2418 err = None
2419 err = None
2419 try:
2420 try:
2420 from . import (
2421 from . import (
2421 base85,
2422 base85,
2422 bdiff,
2423 bdiff,
2423 mpatch,
2424 mpatch,
2424 osutil,
2425 osutil,
2425 )
2426 )
2426 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2427 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2427 except Exception as inst:
2428 except Exception as inst:
2428 err = inst
2429 err = inst
2429 problems += 1
2430 problems += 1
2430 fm.condwrite(err, 'extensionserror', " %s\n", err)
2431 fm.condwrite(err, 'extensionserror', " %s\n", err)
2431
2432
2432 compengines = util.compengines._engines.values()
2433 compengines = util.compengines._engines.values()
2433 fm.write('compengines', _('checking registered compression engines (%s)\n'),
2434 fm.write('compengines', _('checking registered compression engines (%s)\n'),
2434 fm.formatlist(sorted(e.name() for e in compengines),
2435 fm.formatlist(sorted(e.name() for e in compengines),
2435 name='compengine', fmt='%s', sep=', '))
2436 name='compengine', fmt='%s', sep=', '))
2436 fm.write('compenginesavail', _('checking available compression engines '
2437 fm.write('compenginesavail', _('checking available compression engines '
2437 '(%s)\n'),
2438 '(%s)\n'),
2438 fm.formatlist(sorted(e.name() for e in compengines
2439 fm.formatlist(sorted(e.name() for e in compengines
2439 if e.available()),
2440 if e.available()),
2440 name='compengine', fmt='%s', sep=', '))
2441 name='compengine', fmt='%s', sep=', '))
2441
2442
2442 # templates
2443 # templates
2443 p = templater.templatepaths()
2444 p = templater.templatepaths()
2444 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
2445 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
2445 fm.condwrite(not p, '', _(" no template directories found\n"))
2446 fm.condwrite(not p, '', _(" no template directories found\n"))
2446 if p:
2447 if p:
2447 m = templater.templatepath("map-cmdline.default")
2448 m = templater.templatepath("map-cmdline.default")
2448 if m:
2449 if m:
2449 # template found, check if it is working
2450 # template found, check if it is working
2450 err = None
2451 err = None
2451 try:
2452 try:
2452 templater.templater.frommapfile(m)
2453 templater.templater.frommapfile(m)
2453 except Exception as inst:
2454 except Exception as inst:
2454 err = inst
2455 err = inst
2455 p = None
2456 p = None
2456 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
2457 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
2457 else:
2458 else:
2458 p = None
2459 p = None
2459 fm.condwrite(p, 'defaulttemplate',
2460 fm.condwrite(p, 'defaulttemplate',
2460 _("checking default template (%s)\n"), m)
2461 _("checking default template (%s)\n"), m)
2461 fm.condwrite(not m, 'defaulttemplatenotfound',
2462 fm.condwrite(not m, 'defaulttemplatenotfound',
2462 _(" template '%s' not found\n"), "default")
2463 _(" template '%s' not found\n"), "default")
2463 if not p:
2464 if not p:
2464 problems += 1
2465 problems += 1
2465 fm.condwrite(not p, '',
2466 fm.condwrite(not p, '',
2466 _(" (templates seem to have been installed incorrectly)\n"))
2467 _(" (templates seem to have been installed incorrectly)\n"))
2467
2468
2468 # editor
2469 # editor
2469 editor = ui.geteditor()
2470 editor = ui.geteditor()
2470 editor = util.expandpath(editor)
2471 editor = util.expandpath(editor)
2471 fm.write('editor', _("checking commit editor... (%s)\n"), editor)
2472 fm.write('editor', _("checking commit editor... (%s)\n"), editor)
2472 cmdpath = util.findexe(shlex.split(editor)[0])
2473 cmdpath = util.findexe(shlex.split(editor)[0])
2473 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
2474 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
2474 _(" No commit editor set and can't find %s in PATH\n"
2475 _(" No commit editor set and can't find %s in PATH\n"
2475 " (specify a commit editor in your configuration"
2476 " (specify a commit editor in your configuration"
2476 " file)\n"), not cmdpath and editor == 'vi' and editor)
2477 " file)\n"), not cmdpath and editor == 'vi' and editor)
2477 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
2478 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
2478 _(" Can't find editor '%s' in PATH\n"
2479 _(" Can't find editor '%s' in PATH\n"
2479 " (specify a commit editor in your configuration"
2480 " (specify a commit editor in your configuration"
2480 " file)\n"), not cmdpath and editor)
2481 " file)\n"), not cmdpath and editor)
2481 if not cmdpath and editor != 'vi':
2482 if not cmdpath and editor != 'vi':
2482 problems += 1
2483 problems += 1
2483
2484
2484 # check username
2485 # check username
2485 username = None
2486 username = None
2486 err = None
2487 err = None
2487 try:
2488 try:
2488 username = ui.username()
2489 username = ui.username()
2489 except error.Abort as e:
2490 except error.Abort as e:
2490 err = e
2491 err = e
2491 problems += 1
2492 problems += 1
2492
2493
2493 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
2494 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
2494 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
2495 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
2495 " (specify a username in your configuration file)\n"), err)
2496 " (specify a username in your configuration file)\n"), err)
2496
2497
2497 fm.condwrite(not problems, '',
2498 fm.condwrite(not problems, '',
2498 _("no problems detected\n"))
2499 _("no problems detected\n"))
2499 if not problems:
2500 if not problems:
2500 fm.data(problems=problems)
2501 fm.data(problems=problems)
2501 fm.condwrite(problems, 'problems',
2502 fm.condwrite(problems, 'problems',
2502 _("%d problems detected,"
2503 _("%d problems detected,"
2503 " please check your install!\n"), problems)
2504 " please check your install!\n"), problems)
2504 fm.end()
2505 fm.end()
2505
2506
2506 return problems
2507 return problems
2507
2508
2508 @command('debugknown', [], _('REPO ID...'), norepo=True)
2509 @command('debugknown', [], _('REPO ID...'), norepo=True)
2509 def debugknown(ui, repopath, *ids, **opts):
2510 def debugknown(ui, repopath, *ids, **opts):
2510 """test whether node ids are known to a repo
2511 """test whether node ids are known to a repo
2511
2512
2512 Every ID must be a full-length hex node id string. Returns a list of 0s
2513 Every ID must be a full-length hex node id string. Returns a list of 0s
2513 and 1s indicating unknown/known.
2514 and 1s indicating unknown/known.
2514 """
2515 """
2515 repo = hg.peer(ui, opts, repopath)
2516 repo = hg.peer(ui, opts, repopath)
2516 if not repo.capable('known'):
2517 if not repo.capable('known'):
2517 raise error.Abort("known() not supported by target repository")
2518 raise error.Abort("known() not supported by target repository")
2518 flags = repo.known([bin(s) for s in ids])
2519 flags = repo.known([bin(s) for s in ids])
2519 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2520 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2520
2521
2521 @command('debuglabelcomplete', [], _('LABEL...'))
2522 @command('debuglabelcomplete', [], _('LABEL...'))
2522 def debuglabelcomplete(ui, repo, *args):
2523 def debuglabelcomplete(ui, repo, *args):
2523 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2524 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2524 debugnamecomplete(ui, repo, *args)
2525 debugnamecomplete(ui, repo, *args)
2525
2526
2526 @command('debugmergestate', [], '')
2527 @command('debugmergestate', [], '')
2527 def debugmergestate(ui, repo, *args):
2528 def debugmergestate(ui, repo, *args):
2528 """print merge state
2529 """print merge state
2529
2530
2530 Use --verbose to print out information about whether v1 or v2 merge state
2531 Use --verbose to print out information about whether v1 or v2 merge state
2531 was chosen."""
2532 was chosen."""
2532 def _hashornull(h):
2533 def _hashornull(h):
2533 if h == nullhex:
2534 if h == nullhex:
2534 return 'null'
2535 return 'null'
2535 else:
2536 else:
2536 return h
2537 return h
2537
2538
2538 def printrecords(version):
2539 def printrecords(version):
2539 ui.write(('* version %s records\n') % version)
2540 ui.write(('* version %s records\n') % version)
2540 if version == 1:
2541 if version == 1:
2541 records = v1records
2542 records = v1records
2542 else:
2543 else:
2543 records = v2records
2544 records = v2records
2544
2545
2545 for rtype, record in records:
2546 for rtype, record in records:
2546 # pretty print some record types
2547 # pretty print some record types
2547 if rtype == 'L':
2548 if rtype == 'L':
2548 ui.write(('local: %s\n') % record)
2549 ui.write(('local: %s\n') % record)
2549 elif rtype == 'O':
2550 elif rtype == 'O':
2550 ui.write(('other: %s\n') % record)
2551 ui.write(('other: %s\n') % record)
2551 elif rtype == 'm':
2552 elif rtype == 'm':
2552 driver, mdstate = record.split('\0', 1)
2553 driver, mdstate = record.split('\0', 1)
2553 ui.write(('merge driver: %s (state "%s")\n')
2554 ui.write(('merge driver: %s (state "%s")\n')
2554 % (driver, mdstate))
2555 % (driver, mdstate))
2555 elif rtype in 'FDC':
2556 elif rtype in 'FDC':
2556 r = record.split('\0')
2557 r = record.split('\0')
2557 f, state, hash, lfile, afile, anode, ofile = r[0:7]
2558 f, state, hash, lfile, afile, anode, ofile = r[0:7]
2558 if version == 1:
2559 if version == 1:
2559 onode = 'not stored in v1 format'
2560 onode = 'not stored in v1 format'
2560 flags = r[7]
2561 flags = r[7]
2561 else:
2562 else:
2562 onode, flags = r[7:9]
2563 onode, flags = r[7:9]
2563 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
2564 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
2564 % (f, rtype, state, _hashornull(hash)))
2565 % (f, rtype, state, _hashornull(hash)))
2565 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
2566 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
2566 ui.write((' ancestor path: %s (node %s)\n')
2567 ui.write((' ancestor path: %s (node %s)\n')
2567 % (afile, _hashornull(anode)))
2568 % (afile, _hashornull(anode)))
2568 ui.write((' other path: %s (node %s)\n')
2569 ui.write((' other path: %s (node %s)\n')
2569 % (ofile, _hashornull(onode)))
2570 % (ofile, _hashornull(onode)))
2570 elif rtype == 'f':
2571 elif rtype == 'f':
2571 filename, rawextras = record.split('\0', 1)
2572 filename, rawextras = record.split('\0', 1)
2572 extras = rawextras.split('\0')
2573 extras = rawextras.split('\0')
2573 i = 0
2574 i = 0
2574 extrastrings = []
2575 extrastrings = []
2575 while i < len(extras):
2576 while i < len(extras):
2576 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
2577 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
2577 i += 2
2578 i += 2
2578
2579
2579 ui.write(('file extras: %s (%s)\n')
2580 ui.write(('file extras: %s (%s)\n')
2580 % (filename, ', '.join(extrastrings)))
2581 % (filename, ', '.join(extrastrings)))
2581 elif rtype == 'l':
2582 elif rtype == 'l':
2582 labels = record.split('\0', 2)
2583 labels = record.split('\0', 2)
2583 labels = [l for l in labels if len(l) > 0]
2584 labels = [l for l in labels if len(l) > 0]
2584 ui.write(('labels:\n'))
2585 ui.write(('labels:\n'))
2585 ui.write((' local: %s\n' % labels[0]))
2586 ui.write((' local: %s\n' % labels[0]))
2586 ui.write((' other: %s\n' % labels[1]))
2587 ui.write((' other: %s\n' % labels[1]))
2587 if len(labels) > 2:
2588 if len(labels) > 2:
2588 ui.write((' base: %s\n' % labels[2]))
2589 ui.write((' base: %s\n' % labels[2]))
2589 else:
2590 else:
2590 ui.write(('unrecognized entry: %s\t%s\n')
2591 ui.write(('unrecognized entry: %s\t%s\n')
2591 % (rtype, record.replace('\0', '\t')))
2592 % (rtype, record.replace('\0', '\t')))
2592
2593
2593 # Avoid mergestate.read() since it may raise an exception for unsupported
2594 # Avoid mergestate.read() since it may raise an exception for unsupported
2594 # merge state records. We shouldn't be doing this, but this is OK since this
2595 # merge state records. We shouldn't be doing this, but this is OK since this
2595 # command is pretty low-level.
2596 # command is pretty low-level.
2596 ms = mergemod.mergestate(repo)
2597 ms = mergemod.mergestate(repo)
2597
2598
2598 # sort so that reasonable information is on top
2599 # sort so that reasonable information is on top
2599 v1records = ms._readrecordsv1()
2600 v1records = ms._readrecordsv1()
2600 v2records = ms._readrecordsv2()
2601 v2records = ms._readrecordsv2()
2601 order = 'LOml'
2602 order = 'LOml'
2602 def key(r):
2603 def key(r):
2603 idx = order.find(r[0])
2604 idx = order.find(r[0])
2604 if idx == -1:
2605 if idx == -1:
2605 return (1, r[1])
2606 return (1, r[1])
2606 else:
2607 else:
2607 return (0, idx)
2608 return (0, idx)
2608 v1records.sort(key=key)
2609 v1records.sort(key=key)
2609 v2records.sort(key=key)
2610 v2records.sort(key=key)
2610
2611
2611 if not v1records and not v2records:
2612 if not v1records and not v2records:
2612 ui.write(('no merge state found\n'))
2613 ui.write(('no merge state found\n'))
2613 elif not v2records:
2614 elif not v2records:
2614 ui.note(('no version 2 merge state\n'))
2615 ui.note(('no version 2 merge state\n'))
2615 printrecords(1)
2616 printrecords(1)
2616 elif ms._v1v2match(v1records, v2records):
2617 elif ms._v1v2match(v1records, v2records):
2617 ui.note(('v1 and v2 states match: using v2\n'))
2618 ui.note(('v1 and v2 states match: using v2\n'))
2618 printrecords(2)
2619 printrecords(2)
2619 else:
2620 else:
2620 ui.note(('v1 and v2 states mismatch: using v1\n'))
2621 ui.note(('v1 and v2 states mismatch: using v1\n'))
2621 printrecords(1)
2622 printrecords(1)
2622 if ui.verbose:
2623 if ui.verbose:
2623 printrecords(2)
2624 printrecords(2)
2624
2625
2625 @command('debugnamecomplete', [], _('NAME...'))
2626 @command('debugnamecomplete', [], _('NAME...'))
2626 def debugnamecomplete(ui, repo, *args):
2627 def debugnamecomplete(ui, repo, *args):
2627 '''complete "names" - tags, open branch names, bookmark names'''
2628 '''complete "names" - tags, open branch names, bookmark names'''
2628
2629
2629 names = set()
2630 names = set()
2630 # since we previously only listed open branches, we will handle that
2631 # since we previously only listed open branches, we will handle that
2631 # specially (after this for loop)
2632 # specially (after this for loop)
2632 for name, ns in repo.names.iteritems():
2633 for name, ns in repo.names.iteritems():
2633 if name != 'branches':
2634 if name != 'branches':
2634 names.update(ns.listnames(repo))
2635 names.update(ns.listnames(repo))
2635 names.update(tag for (tag, heads, tip, closed)
2636 names.update(tag for (tag, heads, tip, closed)
2636 in repo.branchmap().iterbranches() if not closed)
2637 in repo.branchmap().iterbranches() if not closed)
2637 completions = set()
2638 completions = set()
2638 if not args:
2639 if not args:
2639 args = ['']
2640 args = ['']
2640 for a in args:
2641 for a in args:
2641 completions.update(n for n in names if n.startswith(a))
2642 completions.update(n for n in names if n.startswith(a))
2642 ui.write('\n'.join(sorted(completions)))
2643 ui.write('\n'.join(sorted(completions)))
2643 ui.write('\n')
2644 ui.write('\n')
2644
2645
2645 @command('debuglocks',
2646 @command('debuglocks',
2646 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2647 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2647 ('W', 'force-wlock', None,
2648 ('W', 'force-wlock', None,
2648 _('free the working state lock (DANGEROUS)'))],
2649 _('free the working state lock (DANGEROUS)'))],
2649 _('[OPTION]...'))
2650 _('[OPTION]...'))
2650 def debuglocks(ui, repo, **opts):
2651 def debuglocks(ui, repo, **opts):
2651 """show or modify state of locks
2652 """show or modify state of locks
2652
2653
2653 By default, this command will show which locks are held. This
2654 By default, this command will show which locks are held. This
2654 includes the user and process holding the lock, the amount of time
2655 includes the user and process holding the lock, the amount of time
2655 the lock has been held, and the machine name where the process is
2656 the lock has been held, and the machine name where the process is
2656 running if it's not local.
2657 running if it's not local.
2657
2658
2658 Locks protect the integrity of Mercurial's data, so should be
2659 Locks protect the integrity of Mercurial's data, so should be
2659 treated with care. System crashes or other interruptions may cause
2660 treated with care. System crashes or other interruptions may cause
2660 locks to not be properly released, though Mercurial will usually
2661 locks to not be properly released, though Mercurial will usually
2661 detect and remove such stale locks automatically.
2662 detect and remove such stale locks automatically.
2662
2663
2663 However, detecting stale locks may not always be possible (for
2664 However, detecting stale locks may not always be possible (for
2664 instance, on a shared filesystem). Removing locks may also be
2665 instance, on a shared filesystem). Removing locks may also be
2665 blocked by filesystem permissions.
2666 blocked by filesystem permissions.
2666
2667
2667 Returns 0 if no locks are held.
2668 Returns 0 if no locks are held.
2668
2669
2669 """
2670 """
2670
2671
2671 if opts.get('force_lock'):
2672 if opts.get('force_lock'):
2672 repo.svfs.unlink('lock')
2673 repo.svfs.unlink('lock')
2673 if opts.get('force_wlock'):
2674 if opts.get('force_wlock'):
2674 repo.vfs.unlink('wlock')
2675 repo.vfs.unlink('wlock')
2675 if opts.get('force_lock') or opts.get('force_lock'):
2676 if opts.get('force_lock') or opts.get('force_lock'):
2676 return 0
2677 return 0
2677
2678
2678 now = time.time()
2679 now = time.time()
2679 held = 0
2680 held = 0
2680
2681
2681 def report(vfs, name, method):
2682 def report(vfs, name, method):
2682 # this causes stale locks to get reaped for more accurate reporting
2683 # this causes stale locks to get reaped for more accurate reporting
2683 try:
2684 try:
2684 l = method(False)
2685 l = method(False)
2685 except error.LockHeld:
2686 except error.LockHeld:
2686 l = None
2687 l = None
2687
2688
2688 if l:
2689 if l:
2689 l.release()
2690 l.release()
2690 else:
2691 else:
2691 try:
2692 try:
2692 stat = vfs.lstat(name)
2693 stat = vfs.lstat(name)
2693 age = now - stat.st_mtime
2694 age = now - stat.st_mtime
2694 user = util.username(stat.st_uid)
2695 user = util.username(stat.st_uid)
2695 locker = vfs.readlock(name)
2696 locker = vfs.readlock(name)
2696 if ":" in locker:
2697 if ":" in locker:
2697 host, pid = locker.split(':')
2698 host, pid = locker.split(':')
2698 if host == socket.gethostname():
2699 if host == socket.gethostname():
2699 locker = 'user %s, process %s' % (user, pid)
2700 locker = 'user %s, process %s' % (user, pid)
2700 else:
2701 else:
2701 locker = 'user %s, process %s, host %s' \
2702 locker = 'user %s, process %s, host %s' \
2702 % (user, pid, host)
2703 % (user, pid, host)
2703 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
2704 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
2704 return 1
2705 return 1
2705 except OSError as e:
2706 except OSError as e:
2706 if e.errno != errno.ENOENT:
2707 if e.errno != errno.ENOENT:
2707 raise
2708 raise
2708
2709
2709 ui.write(("%-6s free\n") % (name + ":"))
2710 ui.write(("%-6s free\n") % (name + ":"))
2710 return 0
2711 return 0
2711
2712
2712 held += report(repo.svfs, "lock", repo.lock)
2713 held += report(repo.svfs, "lock", repo.lock)
2713 held += report(repo.vfs, "wlock", repo.wlock)
2714 held += report(repo.vfs, "wlock", repo.wlock)
2714
2715
2715 return held
2716 return held
2716
2717
2717 @command('debugobsolete',
2718 @command('debugobsolete',
2718 [('', 'flags', 0, _('markers flag')),
2719 [('', 'flags', 0, _('markers flag')),
2719 ('', 'record-parents', False,
2720 ('', 'record-parents', False,
2720 _('record parent information for the precursor')),
2721 _('record parent information for the precursor')),
2721 ('r', 'rev', [], _('display markers relevant to REV')),
2722 ('r', 'rev', [], _('display markers relevant to REV')),
2722 ('', 'index', False, _('display index of the marker')),
2723 ('', 'index', False, _('display index of the marker')),
2723 ('', 'delete', [], _('delete markers specified by indices')),
2724 ('', 'delete', [], _('delete markers specified by indices')),
2724 ] + commitopts2 + formatteropts,
2725 ] + commitopts2 + formatteropts,
2725 _('[OBSOLETED [REPLACEMENT ...]]'))
2726 _('[OBSOLETED [REPLACEMENT ...]]'))
2726 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2727 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2727 """create arbitrary obsolete marker
2728 """create arbitrary obsolete marker
2728
2729
2729 With no arguments, displays the list of obsolescence markers."""
2730 With no arguments, displays the list of obsolescence markers."""
2730
2731
2731 def parsenodeid(s):
2732 def parsenodeid(s):
2732 try:
2733 try:
2733 # We do not use revsingle/revrange functions here to accept
2734 # We do not use revsingle/revrange functions here to accept
2734 # arbitrary node identifiers, possibly not present in the
2735 # arbitrary node identifiers, possibly not present in the
2735 # local repository.
2736 # local repository.
2736 n = bin(s)
2737 n = bin(s)
2737 if len(n) != len(nullid):
2738 if len(n) != len(nullid):
2738 raise TypeError()
2739 raise TypeError()
2739 return n
2740 return n
2740 except TypeError:
2741 except TypeError:
2741 raise error.Abort('changeset references must be full hexadecimal '
2742 raise error.Abort('changeset references must be full hexadecimal '
2742 'node identifiers')
2743 'node identifiers')
2743
2744
2744 if opts.get('delete'):
2745 if opts.get('delete'):
2745 indices = []
2746 indices = []
2746 for v in opts.get('delete'):
2747 for v in opts.get('delete'):
2747 try:
2748 try:
2748 indices.append(int(v))
2749 indices.append(int(v))
2749 except ValueError:
2750 except ValueError:
2750 raise error.Abort(_('invalid index value: %r') % v,
2751 raise error.Abort(_('invalid index value: %r') % v,
2751 hint=_('use integers for indices'))
2752 hint=_('use integers for indices'))
2752
2753
2753 if repo.currenttransaction():
2754 if repo.currenttransaction():
2754 raise error.Abort(_('cannot delete obsmarkers in the middle '
2755 raise error.Abort(_('cannot delete obsmarkers in the middle '
2755 'of transaction.'))
2756 'of transaction.'))
2756
2757
2757 with repo.lock():
2758 with repo.lock():
2758 n = repair.deleteobsmarkers(repo.obsstore, indices)
2759 n = repair.deleteobsmarkers(repo.obsstore, indices)
2759 ui.write(_('deleted %i obsolescence markers\n') % n)
2760 ui.write(_('deleted %i obsolescence markers\n') % n)
2760
2761
2761 return
2762 return
2762
2763
2763 if precursor is not None:
2764 if precursor is not None:
2764 if opts['rev']:
2765 if opts['rev']:
2765 raise error.Abort('cannot select revision when creating marker')
2766 raise error.Abort('cannot select revision when creating marker')
2766 metadata = {}
2767 metadata = {}
2767 metadata['user'] = opts['user'] or ui.username()
2768 metadata['user'] = opts['user'] or ui.username()
2768 succs = tuple(parsenodeid(succ) for succ in successors)
2769 succs = tuple(parsenodeid(succ) for succ in successors)
2769 l = repo.lock()
2770 l = repo.lock()
2770 try:
2771 try:
2771 tr = repo.transaction('debugobsolete')
2772 tr = repo.transaction('debugobsolete')
2772 try:
2773 try:
2773 date = opts.get('date')
2774 date = opts.get('date')
2774 if date:
2775 if date:
2775 date = util.parsedate(date)
2776 date = util.parsedate(date)
2776 else:
2777 else:
2777 date = None
2778 date = None
2778 prec = parsenodeid(precursor)
2779 prec = parsenodeid(precursor)
2779 parents = None
2780 parents = None
2780 if opts['record_parents']:
2781 if opts['record_parents']:
2781 if prec not in repo.unfiltered():
2782 if prec not in repo.unfiltered():
2782 raise error.Abort('cannot used --record-parents on '
2783 raise error.Abort('cannot used --record-parents on '
2783 'unknown changesets')
2784 'unknown changesets')
2784 parents = repo.unfiltered()[prec].parents()
2785 parents = repo.unfiltered()[prec].parents()
2785 parents = tuple(p.node() for p in parents)
2786 parents = tuple(p.node() for p in parents)
2786 repo.obsstore.create(tr, prec, succs, opts['flags'],
2787 repo.obsstore.create(tr, prec, succs, opts['flags'],
2787 parents=parents, date=date,
2788 parents=parents, date=date,
2788 metadata=metadata)
2789 metadata=metadata)
2789 tr.close()
2790 tr.close()
2790 except ValueError as exc:
2791 except ValueError as exc:
2791 raise error.Abort(_('bad obsmarker input: %s') % exc)
2792 raise error.Abort(_('bad obsmarker input: %s') % exc)
2792 finally:
2793 finally:
2793 tr.release()
2794 tr.release()
2794 finally:
2795 finally:
2795 l.release()
2796 l.release()
2796 else:
2797 else:
2797 if opts['rev']:
2798 if opts['rev']:
2798 revs = scmutil.revrange(repo, opts['rev'])
2799 revs = scmutil.revrange(repo, opts['rev'])
2799 nodes = [repo[r].node() for r in revs]
2800 nodes = [repo[r].node() for r in revs]
2800 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2801 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2801 markers.sort(key=lambda x: x._data)
2802 markers.sort(key=lambda x: x._data)
2802 else:
2803 else:
2803 markers = obsolete.getmarkers(repo)
2804 markers = obsolete.getmarkers(repo)
2804
2805
2805 markerstoiter = markers
2806 markerstoiter = markers
2806 isrelevant = lambda m: True
2807 isrelevant = lambda m: True
2807 if opts.get('rev') and opts.get('index'):
2808 if opts.get('rev') and opts.get('index'):
2808 markerstoiter = obsolete.getmarkers(repo)
2809 markerstoiter = obsolete.getmarkers(repo)
2809 markerset = set(markers)
2810 markerset = set(markers)
2810 isrelevant = lambda m: m in markerset
2811 isrelevant = lambda m: m in markerset
2811
2812
2812 fm = ui.formatter('debugobsolete', opts)
2813 fm = ui.formatter('debugobsolete', opts)
2813 for i, m in enumerate(markerstoiter):
2814 for i, m in enumerate(markerstoiter):
2814 if not isrelevant(m):
2815 if not isrelevant(m):
2815 # marker can be irrelevant when we're iterating over a set
2816 # marker can be irrelevant when we're iterating over a set
2816 # of markers (markerstoiter) which is bigger than the set
2817 # of markers (markerstoiter) which is bigger than the set
2817 # of markers we want to display (markers)
2818 # of markers we want to display (markers)
2818 # this can happen if both --index and --rev options are
2819 # this can happen if both --index and --rev options are
2819 # provided and thus we need to iterate over all of the markers
2820 # provided and thus we need to iterate over all of the markers
2820 # to get the correct indices, but only display the ones that
2821 # to get the correct indices, but only display the ones that
2821 # are relevant to --rev value
2822 # are relevant to --rev value
2822 continue
2823 continue
2823 fm.startitem()
2824 fm.startitem()
2824 ind = i if opts.get('index') else None
2825 ind = i if opts.get('index') else None
2825 cmdutil.showmarker(fm, m, index=ind)
2826 cmdutil.showmarker(fm, m, index=ind)
2826 fm.end()
2827 fm.end()
2827
2828
2828 @command('debugpathcomplete',
2829 @command('debugpathcomplete',
2829 [('f', 'full', None, _('complete an entire path')),
2830 [('f', 'full', None, _('complete an entire path')),
2830 ('n', 'normal', None, _('show only normal files')),
2831 ('n', 'normal', None, _('show only normal files')),
2831 ('a', 'added', None, _('show only added files')),
2832 ('a', 'added', None, _('show only added files')),
2832 ('r', 'removed', None, _('show only removed files'))],
2833 ('r', 'removed', None, _('show only removed files'))],
2833 _('FILESPEC...'))
2834 _('FILESPEC...'))
2834 def debugpathcomplete(ui, repo, *specs, **opts):
2835 def debugpathcomplete(ui, repo, *specs, **opts):
2835 '''complete part or all of a tracked path
2836 '''complete part or all of a tracked path
2836
2837
2837 This command supports shells that offer path name completion. It
2838 This command supports shells that offer path name completion. It
2838 currently completes only files already known to the dirstate.
2839 currently completes only files already known to the dirstate.
2839
2840
2840 Completion extends only to the next path segment unless
2841 Completion extends only to the next path segment unless
2841 --full is specified, in which case entire paths are used.'''
2842 --full is specified, in which case entire paths are used.'''
2842
2843
2843 def complete(path, acceptable):
2844 def complete(path, acceptable):
2844 dirstate = repo.dirstate
2845 dirstate = repo.dirstate
2845 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2846 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2846 rootdir = repo.root + os.sep
2847 rootdir = repo.root + os.sep
2847 if spec != repo.root and not spec.startswith(rootdir):
2848 if spec != repo.root and not spec.startswith(rootdir):
2848 return [], []
2849 return [], []
2849 if os.path.isdir(spec):
2850 if os.path.isdir(spec):
2850 spec += '/'
2851 spec += '/'
2851 spec = spec[len(rootdir):]
2852 spec = spec[len(rootdir):]
2852 fixpaths = pycompat.ossep != '/'
2853 fixpaths = pycompat.ossep != '/'
2853 if fixpaths:
2854 if fixpaths:
2854 spec = spec.replace(os.sep, '/')
2855 spec = spec.replace(os.sep, '/')
2855 speclen = len(spec)
2856 speclen = len(spec)
2856 fullpaths = opts['full']
2857 fullpaths = opts['full']
2857 files, dirs = set(), set()
2858 files, dirs = set(), set()
2858 adddir, addfile = dirs.add, files.add
2859 adddir, addfile = dirs.add, files.add
2859 for f, st in dirstate.iteritems():
2860 for f, st in dirstate.iteritems():
2860 if f.startswith(spec) and st[0] in acceptable:
2861 if f.startswith(spec) and st[0] in acceptable:
2861 if fixpaths:
2862 if fixpaths:
2862 f = f.replace('/', os.sep)
2863 f = f.replace('/', os.sep)
2863 if fullpaths:
2864 if fullpaths:
2864 addfile(f)
2865 addfile(f)
2865 continue
2866 continue
2866 s = f.find(os.sep, speclen)
2867 s = f.find(os.sep, speclen)
2867 if s >= 0:
2868 if s >= 0:
2868 adddir(f[:s])
2869 adddir(f[:s])
2869 else:
2870 else:
2870 addfile(f)
2871 addfile(f)
2871 return files, dirs
2872 return files, dirs
2872
2873
2873 acceptable = ''
2874 acceptable = ''
2874 if opts['normal']:
2875 if opts['normal']:
2875 acceptable += 'nm'
2876 acceptable += 'nm'
2876 if opts['added']:
2877 if opts['added']:
2877 acceptable += 'a'
2878 acceptable += 'a'
2878 if opts['removed']:
2879 if opts['removed']:
2879 acceptable += 'r'
2880 acceptable += 'r'
2880 cwd = repo.getcwd()
2881 cwd = repo.getcwd()
2881 if not specs:
2882 if not specs:
2882 specs = ['.']
2883 specs = ['.']
2883
2884
2884 files, dirs = set(), set()
2885 files, dirs = set(), set()
2885 for spec in specs:
2886 for spec in specs:
2886 f, d = complete(spec, acceptable or 'nmar')
2887 f, d = complete(spec, acceptable or 'nmar')
2887 files.update(f)
2888 files.update(f)
2888 dirs.update(d)
2889 dirs.update(d)
2889 files.update(dirs)
2890 files.update(dirs)
2890 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2891 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2891 ui.write('\n')
2892 ui.write('\n')
2892
2893
2893 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2894 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2894 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2895 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2895 '''access the pushkey key/value protocol
2896 '''access the pushkey key/value protocol
2896
2897
2897 With two args, list the keys in the given namespace.
2898 With two args, list the keys in the given namespace.
2898
2899
2899 With five args, set a key to new if it currently is set to old.
2900 With five args, set a key to new if it currently is set to old.
2900 Reports success or failure.
2901 Reports success or failure.
2901 '''
2902 '''
2902
2903
2903 target = hg.peer(ui, {}, repopath)
2904 target = hg.peer(ui, {}, repopath)
2904 if keyinfo:
2905 if keyinfo:
2905 key, old, new = keyinfo
2906 key, old, new = keyinfo
2906 r = target.pushkey(namespace, key, old, new)
2907 r = target.pushkey(namespace, key, old, new)
2907 ui.status(str(r) + '\n')
2908 ui.status(str(r) + '\n')
2908 return not r
2909 return not r
2909 else:
2910 else:
2910 for k, v in sorted(target.listkeys(namespace).iteritems()):
2911 for k, v in sorted(target.listkeys(namespace).iteritems()):
2911 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2912 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2912 v.encode('string-escape')))
2913 v.encode('string-escape')))
2913
2914
2914 @command('debugpvec', [], _('A B'))
2915 @command('debugpvec', [], _('A B'))
2915 def debugpvec(ui, repo, a, b=None):
2916 def debugpvec(ui, repo, a, b=None):
2916 ca = scmutil.revsingle(repo, a)
2917 ca = scmutil.revsingle(repo, a)
2917 cb = scmutil.revsingle(repo, b)
2918 cb = scmutil.revsingle(repo, b)
2918 pa = pvec.ctxpvec(ca)
2919 pa = pvec.ctxpvec(ca)
2919 pb = pvec.ctxpvec(cb)
2920 pb = pvec.ctxpvec(cb)
2920 if pa == pb:
2921 if pa == pb:
2921 rel = "="
2922 rel = "="
2922 elif pa > pb:
2923 elif pa > pb:
2923 rel = ">"
2924 rel = ">"
2924 elif pa < pb:
2925 elif pa < pb:
2925 rel = "<"
2926 rel = "<"
2926 elif pa | pb:
2927 elif pa | pb:
2927 rel = "|"
2928 rel = "|"
2928 ui.write(_("a: %s\n") % pa)
2929 ui.write(_("a: %s\n") % pa)
2929 ui.write(_("b: %s\n") % pb)
2930 ui.write(_("b: %s\n") % pb)
2930 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2931 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2931 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2932 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2932 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2933 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2933 pa.distance(pb), rel))
2934 pa.distance(pb), rel))
2934
2935
2935 @command('debugrebuilddirstate|debugrebuildstate',
2936 @command('debugrebuilddirstate|debugrebuildstate',
2936 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
2937 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
2937 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
2938 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
2938 'the working copy parent')),
2939 'the working copy parent')),
2939 ],
2940 ],
2940 _('[-r REV]'))
2941 _('[-r REV]'))
2941 def debugrebuilddirstate(ui, repo, rev, **opts):
2942 def debugrebuilddirstate(ui, repo, rev, **opts):
2942 """rebuild the dirstate as it would look like for the given revision
2943 """rebuild the dirstate as it would look like for the given revision
2943
2944
2944 If no revision is specified the first current parent will be used.
2945 If no revision is specified the first current parent will be used.
2945
2946
2946 The dirstate will be set to the files of the given revision.
2947 The dirstate will be set to the files of the given revision.
2947 The actual working directory content or existing dirstate
2948 The actual working directory content or existing dirstate
2948 information such as adds or removes is not considered.
2949 information such as adds or removes is not considered.
2949
2950
2950 ``minimal`` will only rebuild the dirstate status for files that claim to be
2951 ``minimal`` will only rebuild the dirstate status for files that claim to be
2951 tracked but are not in the parent manifest, or that exist in the parent
2952 tracked but are not in the parent manifest, or that exist in the parent
2952 manifest but are not in the dirstate. It will not change adds, removes, or
2953 manifest but are not in the dirstate. It will not change adds, removes, or
2953 modified files that are in the working copy parent.
2954 modified files that are in the working copy parent.
2954
2955
2955 One use of this command is to make the next :hg:`status` invocation
2956 One use of this command is to make the next :hg:`status` invocation
2956 check the actual file content.
2957 check the actual file content.
2957 """
2958 """
2958 ctx = scmutil.revsingle(repo, rev)
2959 ctx = scmutil.revsingle(repo, rev)
2959 with repo.wlock():
2960 with repo.wlock():
2960 dirstate = repo.dirstate
2961 dirstate = repo.dirstate
2961 changedfiles = None
2962 changedfiles = None
2962 # See command doc for what minimal does.
2963 # See command doc for what minimal does.
2963 if opts.get('minimal'):
2964 if opts.get('minimal'):
2964 manifestfiles = set(ctx.manifest().keys())
2965 manifestfiles = set(ctx.manifest().keys())
2965 dirstatefiles = set(dirstate)
2966 dirstatefiles = set(dirstate)
2966 manifestonly = manifestfiles - dirstatefiles
2967 manifestonly = manifestfiles - dirstatefiles
2967 dsonly = dirstatefiles - manifestfiles
2968 dsonly = dirstatefiles - manifestfiles
2968 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
2969 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
2969 changedfiles = manifestonly | dsnotadded
2970 changedfiles = manifestonly | dsnotadded
2970
2971
2971 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
2972 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
2972
2973
2973 @command('debugrebuildfncache', [], '')
2974 @command('debugrebuildfncache', [], '')
2974 def debugrebuildfncache(ui, repo):
2975 def debugrebuildfncache(ui, repo):
2975 """rebuild the fncache file"""
2976 """rebuild the fncache file"""
2976 repair.rebuildfncache(ui, repo)
2977 repair.rebuildfncache(ui, repo)
2977
2978
2978 @command('debugrename',
2979 @command('debugrename',
2979 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2980 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2980 _('[-r REV] FILE'))
2981 _('[-r REV] FILE'))
2981 def debugrename(ui, repo, file1, *pats, **opts):
2982 def debugrename(ui, repo, file1, *pats, **opts):
2982 """dump rename information"""
2983 """dump rename information"""
2983
2984
2984 ctx = scmutil.revsingle(repo, opts.get('rev'))
2985 ctx = scmutil.revsingle(repo, opts.get('rev'))
2985 m = scmutil.match(ctx, (file1,) + pats, opts)
2986 m = scmutil.match(ctx, (file1,) + pats, opts)
2986 for abs in ctx.walk(m):
2987 for abs in ctx.walk(m):
2987 fctx = ctx[abs]
2988 fctx = ctx[abs]
2988 o = fctx.filelog().renamed(fctx.filenode())
2989 o = fctx.filelog().renamed(fctx.filenode())
2989 rel = m.rel(abs)
2990 rel = m.rel(abs)
2990 if o:
2991 if o:
2991 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2992 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2992 else:
2993 else:
2993 ui.write(_("%s not renamed\n") % rel)
2994 ui.write(_("%s not renamed\n") % rel)
2994
2995
2995 @command('debugrevlog', debugrevlogopts +
2996 @command('debugrevlog', debugrevlogopts +
2996 [('d', 'dump', False, _('dump index data'))],
2997 [('d', 'dump', False, _('dump index data'))],
2997 _('-c|-m|FILE'),
2998 _('-c|-m|FILE'),
2998 optionalrepo=True)
2999 optionalrepo=True)
2999 def debugrevlog(ui, repo, file_=None, **opts):
3000 def debugrevlog(ui, repo, file_=None, **opts):
3000 """show data and statistics about a revlog"""
3001 """show data and statistics about a revlog"""
3001 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
3002 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
3002
3003
3003 if opts.get("dump"):
3004 if opts.get("dump"):
3004 numrevs = len(r)
3005 numrevs = len(r)
3005 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
3006 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
3006 " rawsize totalsize compression heads chainlen\n"))
3007 " rawsize totalsize compression heads chainlen\n"))
3007 ts = 0
3008 ts = 0
3008 heads = set()
3009 heads = set()
3009
3010
3010 for rev in xrange(numrevs):
3011 for rev in xrange(numrevs):
3011 dbase = r.deltaparent(rev)
3012 dbase = r.deltaparent(rev)
3012 if dbase == -1:
3013 if dbase == -1:
3013 dbase = rev
3014 dbase = rev
3014 cbase = r.chainbase(rev)
3015 cbase = r.chainbase(rev)
3015 clen = r.chainlen(rev)
3016 clen = r.chainlen(rev)
3016 p1, p2 = r.parentrevs(rev)
3017 p1, p2 = r.parentrevs(rev)
3017 rs = r.rawsize(rev)
3018 rs = r.rawsize(rev)
3018 ts = ts + rs
3019 ts = ts + rs
3019 heads -= set(r.parentrevs(rev))
3020 heads -= set(r.parentrevs(rev))
3020 heads.add(rev)
3021 heads.add(rev)
3021 try:
3022 try:
3022 compression = ts / r.end(rev)
3023 compression = ts / r.end(rev)
3023 except ZeroDivisionError:
3024 except ZeroDivisionError:
3024 compression = 0
3025 compression = 0
3025 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
3026 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
3026 "%11d %5d %8d\n" %
3027 "%11d %5d %8d\n" %
3027 (rev, p1, p2, r.start(rev), r.end(rev),
3028 (rev, p1, p2, r.start(rev), r.end(rev),
3028 r.start(dbase), r.start(cbase),
3029 r.start(dbase), r.start(cbase),
3029 r.start(p1), r.start(p2),
3030 r.start(p1), r.start(p2),
3030 rs, ts, compression, len(heads), clen))
3031 rs, ts, compression, len(heads), clen))
3031 return 0
3032 return 0
3032
3033
3033 v = r.version
3034 v = r.version
3034 format = v & 0xFFFF
3035 format = v & 0xFFFF
3035 flags = []
3036 flags = []
3036 gdelta = False
3037 gdelta = False
3037 if v & revlog.REVLOGNGINLINEDATA:
3038 if v & revlog.REVLOGNGINLINEDATA:
3038 flags.append('inline')
3039 flags.append('inline')
3039 if v & revlog.REVLOGGENERALDELTA:
3040 if v & revlog.REVLOGGENERALDELTA:
3040 gdelta = True
3041 gdelta = True
3041 flags.append('generaldelta')
3042 flags.append('generaldelta')
3042 if not flags:
3043 if not flags:
3043 flags = ['(none)']
3044 flags = ['(none)']
3044
3045
3045 nummerges = 0
3046 nummerges = 0
3046 numfull = 0
3047 numfull = 0
3047 numprev = 0
3048 numprev = 0
3048 nump1 = 0
3049 nump1 = 0
3049 nump2 = 0
3050 nump2 = 0
3050 numother = 0
3051 numother = 0
3051 nump1prev = 0
3052 nump1prev = 0
3052 nump2prev = 0
3053 nump2prev = 0
3053 chainlengths = []
3054 chainlengths = []
3054
3055
3055 datasize = [None, 0, 0]
3056 datasize = [None, 0, 0]
3056 fullsize = [None, 0, 0]
3057 fullsize = [None, 0, 0]
3057 deltasize = [None, 0, 0]
3058 deltasize = [None, 0, 0]
3058 chunktypecounts = {}
3059 chunktypecounts = {}
3059 chunktypesizes = {}
3060 chunktypesizes = {}
3060
3061
3061 def addsize(size, l):
3062 def addsize(size, l):
3062 if l[0] is None or size < l[0]:
3063 if l[0] is None or size < l[0]:
3063 l[0] = size
3064 l[0] = size
3064 if size > l[1]:
3065 if size > l[1]:
3065 l[1] = size
3066 l[1] = size
3066 l[2] += size
3067 l[2] += size
3067
3068
3068 numrevs = len(r)
3069 numrevs = len(r)
3069 for rev in xrange(numrevs):
3070 for rev in xrange(numrevs):
3070 p1, p2 = r.parentrevs(rev)
3071 p1, p2 = r.parentrevs(rev)
3071 delta = r.deltaparent(rev)
3072 delta = r.deltaparent(rev)
3072 if format > 0:
3073 if format > 0:
3073 addsize(r.rawsize(rev), datasize)
3074 addsize(r.rawsize(rev), datasize)
3074 if p2 != nullrev:
3075 if p2 != nullrev:
3075 nummerges += 1
3076 nummerges += 1
3076 size = r.length(rev)
3077 size = r.length(rev)
3077 if delta == nullrev:
3078 if delta == nullrev:
3078 chainlengths.append(0)
3079 chainlengths.append(0)
3079 numfull += 1
3080 numfull += 1
3080 addsize(size, fullsize)
3081 addsize(size, fullsize)
3081 else:
3082 else:
3082 chainlengths.append(chainlengths[delta] + 1)
3083 chainlengths.append(chainlengths[delta] + 1)
3083 addsize(size, deltasize)
3084 addsize(size, deltasize)
3084 if delta == rev - 1:
3085 if delta == rev - 1:
3085 numprev += 1
3086 numprev += 1
3086 if delta == p1:
3087 if delta == p1:
3087 nump1prev += 1
3088 nump1prev += 1
3088 elif delta == p2:
3089 elif delta == p2:
3089 nump2prev += 1
3090 nump2prev += 1
3090 elif delta == p1:
3091 elif delta == p1:
3091 nump1 += 1
3092 nump1 += 1
3092 elif delta == p2:
3093 elif delta == p2:
3093 nump2 += 1
3094 nump2 += 1
3094 elif delta != nullrev:
3095 elif delta != nullrev:
3095 numother += 1
3096 numother += 1
3096
3097
3097 # Obtain data on the raw chunks in the revlog.
3098 # Obtain data on the raw chunks in the revlog.
3098 chunk = r._chunkraw(rev, rev)[1]
3099 chunk = r._chunkraw(rev, rev)[1]
3099 if chunk:
3100 if chunk:
3100 chunktype = chunk[0]
3101 chunktype = chunk[0]
3101 else:
3102 else:
3102 chunktype = 'empty'
3103 chunktype = 'empty'
3103
3104
3104 if chunktype not in chunktypecounts:
3105 if chunktype not in chunktypecounts:
3105 chunktypecounts[chunktype] = 0
3106 chunktypecounts[chunktype] = 0
3106 chunktypesizes[chunktype] = 0
3107 chunktypesizes[chunktype] = 0
3107
3108
3108 chunktypecounts[chunktype] += 1
3109 chunktypecounts[chunktype] += 1
3109 chunktypesizes[chunktype] += size
3110 chunktypesizes[chunktype] += size
3110
3111
3111 # Adjust size min value for empty cases
3112 # Adjust size min value for empty cases
3112 for size in (datasize, fullsize, deltasize):
3113 for size in (datasize, fullsize, deltasize):
3113 if size[0] is None:
3114 if size[0] is None:
3114 size[0] = 0
3115 size[0] = 0
3115
3116
3116 numdeltas = numrevs - numfull
3117 numdeltas = numrevs - numfull
3117 numoprev = numprev - nump1prev - nump2prev
3118 numoprev = numprev - nump1prev - nump2prev
3118 totalrawsize = datasize[2]
3119 totalrawsize = datasize[2]
3119 datasize[2] /= numrevs
3120 datasize[2] /= numrevs
3120 fulltotal = fullsize[2]
3121 fulltotal = fullsize[2]
3121 fullsize[2] /= numfull
3122 fullsize[2] /= numfull
3122 deltatotal = deltasize[2]
3123 deltatotal = deltasize[2]
3123 if numrevs - numfull > 0:
3124 if numrevs - numfull > 0:
3124 deltasize[2] /= numrevs - numfull
3125 deltasize[2] /= numrevs - numfull
3125 totalsize = fulltotal + deltatotal
3126 totalsize = fulltotal + deltatotal
3126 avgchainlen = sum(chainlengths) / numrevs
3127 avgchainlen = sum(chainlengths) / numrevs
3127 maxchainlen = max(chainlengths)
3128 maxchainlen = max(chainlengths)
3128 compratio = 1
3129 compratio = 1
3129 if totalsize:
3130 if totalsize:
3130 compratio = totalrawsize / totalsize
3131 compratio = totalrawsize / totalsize
3131
3132
3132 basedfmtstr = '%%%dd\n'
3133 basedfmtstr = '%%%dd\n'
3133 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
3134 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
3134
3135
3135 def dfmtstr(max):
3136 def dfmtstr(max):
3136 return basedfmtstr % len(str(max))
3137 return basedfmtstr % len(str(max))
3137 def pcfmtstr(max, padding=0):
3138 def pcfmtstr(max, padding=0):
3138 return basepcfmtstr % (len(str(max)), ' ' * padding)
3139 return basepcfmtstr % (len(str(max)), ' ' * padding)
3139
3140
3140 def pcfmt(value, total):
3141 def pcfmt(value, total):
3141 if total:
3142 if total:
3142 return (value, 100 * float(value) / total)
3143 return (value, 100 * float(value) / total)
3143 else:
3144 else:
3144 return value, 100.0
3145 return value, 100.0
3145
3146
3146 ui.write(('format : %d\n') % format)
3147 ui.write(('format : %d\n') % format)
3147 ui.write(('flags : %s\n') % ', '.join(flags))
3148 ui.write(('flags : %s\n') % ', '.join(flags))
3148
3149
3149 ui.write('\n')
3150 ui.write('\n')
3150 fmt = pcfmtstr(totalsize)
3151 fmt = pcfmtstr(totalsize)
3151 fmt2 = dfmtstr(totalsize)
3152 fmt2 = dfmtstr(totalsize)
3152 ui.write(('revisions : ') + fmt2 % numrevs)
3153 ui.write(('revisions : ') + fmt2 % numrevs)
3153 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
3154 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
3154 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
3155 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
3155 ui.write(('revisions : ') + fmt2 % numrevs)
3156 ui.write(('revisions : ') + fmt2 % numrevs)
3156 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
3157 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
3157 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
3158 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
3158 ui.write(('revision size : ') + fmt2 % totalsize)
3159 ui.write(('revision size : ') + fmt2 % totalsize)
3159 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
3160 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
3160 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
3161 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
3161
3162
3162 def fmtchunktype(chunktype):
3163 def fmtchunktype(chunktype):
3163 if chunktype == 'empty':
3164 if chunktype == 'empty':
3164 return ' %s : ' % chunktype
3165 return ' %s : ' % chunktype
3165 elif chunktype in string.ascii_letters:
3166 elif chunktype in string.ascii_letters:
3166 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
3167 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
3167 else:
3168 else:
3168 return ' 0x%s : ' % hex(chunktype)
3169 return ' 0x%s : ' % hex(chunktype)
3169
3170
3170 ui.write('\n')
3171 ui.write('\n')
3171 ui.write(('chunks : ') + fmt2 % numrevs)
3172 ui.write(('chunks : ') + fmt2 % numrevs)
3172 for chunktype in sorted(chunktypecounts):
3173 for chunktype in sorted(chunktypecounts):
3173 ui.write(fmtchunktype(chunktype))
3174 ui.write(fmtchunktype(chunktype))
3174 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
3175 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
3175 ui.write(('chunks size : ') + fmt2 % totalsize)
3176 ui.write(('chunks size : ') + fmt2 % totalsize)
3176 for chunktype in sorted(chunktypecounts):
3177 for chunktype in sorted(chunktypecounts):
3177 ui.write(fmtchunktype(chunktype))
3178 ui.write(fmtchunktype(chunktype))
3178 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
3179 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
3179
3180
3180 ui.write('\n')
3181 ui.write('\n')
3181 fmt = dfmtstr(max(avgchainlen, compratio))
3182 fmt = dfmtstr(max(avgchainlen, compratio))
3182 ui.write(('avg chain length : ') + fmt % avgchainlen)
3183 ui.write(('avg chain length : ') + fmt % avgchainlen)
3183 ui.write(('max chain length : ') + fmt % maxchainlen)
3184 ui.write(('max chain length : ') + fmt % maxchainlen)
3184 ui.write(('compression ratio : ') + fmt % compratio)
3185 ui.write(('compression ratio : ') + fmt % compratio)
3185
3186
3186 if format > 0:
3187 if format > 0:
3187 ui.write('\n')
3188 ui.write('\n')
3188 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
3189 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
3189 % tuple(datasize))
3190 % tuple(datasize))
3190 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
3191 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
3191 % tuple(fullsize))
3192 % tuple(fullsize))
3192 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
3193 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
3193 % tuple(deltasize))
3194 % tuple(deltasize))
3194
3195
3195 if numdeltas > 0:
3196 if numdeltas > 0:
3196 ui.write('\n')
3197 ui.write('\n')
3197 fmt = pcfmtstr(numdeltas)
3198 fmt = pcfmtstr(numdeltas)
3198 fmt2 = pcfmtstr(numdeltas, 4)
3199 fmt2 = pcfmtstr(numdeltas, 4)
3199 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
3200 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
3200 if numprev > 0:
3201 if numprev > 0:
3201 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
3202 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
3202 numprev))
3203 numprev))
3203 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
3204 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
3204 numprev))
3205 numprev))
3205 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
3206 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
3206 numprev))
3207 numprev))
3207 if gdelta:
3208 if gdelta:
3208 ui.write(('deltas against p1 : ')
3209 ui.write(('deltas against p1 : ')
3209 + fmt % pcfmt(nump1, numdeltas))
3210 + fmt % pcfmt(nump1, numdeltas))
3210 ui.write(('deltas against p2 : ')
3211 ui.write(('deltas against p2 : ')
3211 + fmt % pcfmt(nump2, numdeltas))
3212 + fmt % pcfmt(nump2, numdeltas))
3212 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
3213 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
3213 numdeltas))
3214 numdeltas))
3214
3215
3215 @command('debugrevspec',
3216 @command('debugrevspec',
3216 [('', 'optimize', None,
3217 [('', 'optimize', None,
3217 _('print parsed tree after optimizing (DEPRECATED)')),
3218 _('print parsed tree after optimizing (DEPRECATED)')),
3218 ('p', 'show-stage', [],
3219 ('p', 'show-stage', [],
3219 _('print parsed tree at the given stage'), _('NAME')),
3220 _('print parsed tree at the given stage'), _('NAME')),
3220 ('', 'no-optimized', False, _('evaluate tree without optimization')),
3221 ('', 'no-optimized', False, _('evaluate tree without optimization')),
3221 ('', 'verify-optimized', False, _('verify optimized result')),
3222 ('', 'verify-optimized', False, _('verify optimized result')),
3222 ],
3223 ],
3223 ('REVSPEC'))
3224 ('REVSPEC'))
3224 def debugrevspec(ui, repo, expr, **opts):
3225 def debugrevspec(ui, repo, expr, **opts):
3225 """parse and apply a revision specification
3226 """parse and apply a revision specification
3226
3227
3227 Use -p/--show-stage option to print the parsed tree at the given stages.
3228 Use -p/--show-stage option to print the parsed tree at the given stages.
3228 Use -p all to print tree at every stage.
3229 Use -p all to print tree at every stage.
3229
3230
3230 Use --verify-optimized to compare the optimized result with the unoptimized
3231 Use --verify-optimized to compare the optimized result with the unoptimized
3231 one. Returns 1 if the optimized result differs.
3232 one. Returns 1 if the optimized result differs.
3232 """
3233 """
3233 stages = [
3234 stages = [
3234 ('parsed', lambda tree: tree),
3235 ('parsed', lambda tree: tree),
3235 ('expanded', lambda tree: revset.expandaliases(ui, tree)),
3236 ('expanded', lambda tree: revset.expandaliases(ui, tree)),
3236 ('concatenated', revset.foldconcat),
3237 ('concatenated', revset.foldconcat),
3237 ('analyzed', revset.analyze),
3238 ('analyzed', revset.analyze),
3238 ('optimized', revset.optimize),
3239 ('optimized', revset.optimize),
3239 ]
3240 ]
3240 if opts['no_optimized']:
3241 if opts['no_optimized']:
3241 stages = stages[:-1]
3242 stages = stages[:-1]
3242 if opts['verify_optimized'] and opts['no_optimized']:
3243 if opts['verify_optimized'] and opts['no_optimized']:
3243 raise error.Abort(_('cannot use --verify-optimized with '
3244 raise error.Abort(_('cannot use --verify-optimized with '
3244 '--no-optimized'))
3245 '--no-optimized'))
3245 stagenames = set(n for n, f in stages)
3246 stagenames = set(n for n, f in stages)
3246
3247
3247 showalways = set()
3248 showalways = set()
3248 showchanged = set()
3249 showchanged = set()
3249 if ui.verbose and not opts['show_stage']:
3250 if ui.verbose and not opts['show_stage']:
3250 # show parsed tree by --verbose (deprecated)
3251 # show parsed tree by --verbose (deprecated)
3251 showalways.add('parsed')
3252 showalways.add('parsed')
3252 showchanged.update(['expanded', 'concatenated'])
3253 showchanged.update(['expanded', 'concatenated'])
3253 if opts['optimize']:
3254 if opts['optimize']:
3254 showalways.add('optimized')
3255 showalways.add('optimized')
3255 if opts['show_stage'] and opts['optimize']:
3256 if opts['show_stage'] and opts['optimize']:
3256 raise error.Abort(_('cannot use --optimize with --show-stage'))
3257 raise error.Abort(_('cannot use --optimize with --show-stage'))
3257 if opts['show_stage'] == ['all']:
3258 if opts['show_stage'] == ['all']:
3258 showalways.update(stagenames)
3259 showalways.update(stagenames)
3259 else:
3260 else:
3260 for n in opts['show_stage']:
3261 for n in opts['show_stage']:
3261 if n not in stagenames:
3262 if n not in stagenames:
3262 raise error.Abort(_('invalid stage name: %s') % n)
3263 raise error.Abort(_('invalid stage name: %s') % n)
3263 showalways.update(opts['show_stage'])
3264 showalways.update(opts['show_stage'])
3264
3265
3265 treebystage = {}
3266 treebystage = {}
3266 printedtree = None
3267 printedtree = None
3267 tree = revset.parse(expr, lookup=repo.__contains__)
3268 tree = revset.parse(expr, lookup=repo.__contains__)
3268 for n, f in stages:
3269 for n, f in stages:
3269 treebystage[n] = tree = f(tree)
3270 treebystage[n] = tree = f(tree)
3270 if n in showalways or (n in showchanged and tree != printedtree):
3271 if n in showalways or (n in showchanged and tree != printedtree):
3271 if opts['show_stage'] or n != 'parsed':
3272 if opts['show_stage'] or n != 'parsed':
3272 ui.write(("* %s:\n") % n)
3273 ui.write(("* %s:\n") % n)
3273 ui.write(revset.prettyformat(tree), "\n")
3274 ui.write(revset.prettyformat(tree), "\n")
3274 printedtree = tree
3275 printedtree = tree
3275
3276
3276 if opts['verify_optimized']:
3277 if opts['verify_optimized']:
3277 arevs = revset.makematcher(treebystage['analyzed'])(repo)
3278 arevs = revset.makematcher(treebystage['analyzed'])(repo)
3278 brevs = revset.makematcher(treebystage['optimized'])(repo)
3279 brevs = revset.makematcher(treebystage['optimized'])(repo)
3279 if ui.verbose:
3280 if ui.verbose:
3280 ui.note(("* analyzed set:\n"), revset.prettyformatset(arevs), "\n")
3281 ui.note(("* analyzed set:\n"), revset.prettyformatset(arevs), "\n")
3281 ui.note(("* optimized set:\n"), revset.prettyformatset(brevs), "\n")
3282 ui.note(("* optimized set:\n"), revset.prettyformatset(brevs), "\n")
3282 arevs = list(arevs)
3283 arevs = list(arevs)
3283 brevs = list(brevs)
3284 brevs = list(brevs)
3284 if arevs == brevs:
3285 if arevs == brevs:
3285 return 0
3286 return 0
3286 ui.write(('--- analyzed\n'), label='diff.file_a')
3287 ui.write(('--- analyzed\n'), label='diff.file_a')
3287 ui.write(('+++ optimized\n'), label='diff.file_b')
3288 ui.write(('+++ optimized\n'), label='diff.file_b')
3288 sm = difflib.SequenceMatcher(None, arevs, brevs)
3289 sm = difflib.SequenceMatcher(None, arevs, brevs)
3289 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3290 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3290 if tag in ('delete', 'replace'):
3291 if tag in ('delete', 'replace'):
3291 for c in arevs[alo:ahi]:
3292 for c in arevs[alo:ahi]:
3292 ui.write('-%s\n' % c, label='diff.deleted')
3293 ui.write('-%s\n' % c, label='diff.deleted')
3293 if tag in ('insert', 'replace'):
3294 if tag in ('insert', 'replace'):
3294 for c in brevs[blo:bhi]:
3295 for c in brevs[blo:bhi]:
3295 ui.write('+%s\n' % c, label='diff.inserted')
3296 ui.write('+%s\n' % c, label='diff.inserted')
3296 if tag == 'equal':
3297 if tag == 'equal':
3297 for c in arevs[alo:ahi]:
3298 for c in arevs[alo:ahi]:
3298 ui.write(' %s\n' % c)
3299 ui.write(' %s\n' % c)
3299 return 1
3300 return 1
3300
3301
3301 func = revset.makematcher(tree)
3302 func = revset.makematcher(tree)
3302 revs = func(repo)
3303 revs = func(repo)
3303 if ui.verbose:
3304 if ui.verbose:
3304 ui.note(("* set:\n"), revset.prettyformatset(revs), "\n")
3305 ui.note(("* set:\n"), revset.prettyformatset(revs), "\n")
3305 for c in revs:
3306 for c in revs:
3306 ui.write("%s\n" % c)
3307 ui.write("%s\n" % c)
3307
3308
3308 @command('debugsetparents', [], _('REV1 [REV2]'))
3309 @command('debugsetparents', [], _('REV1 [REV2]'))
3309 def debugsetparents(ui, repo, rev1, rev2=None):
3310 def debugsetparents(ui, repo, rev1, rev2=None):
3310 """manually set the parents of the current working directory
3311 """manually set the parents of the current working directory
3311
3312
3312 This is useful for writing repository conversion tools, but should
3313 This is useful for writing repository conversion tools, but should
3313 be used with care. For example, neither the working directory nor the
3314 be used with care. For example, neither the working directory nor the
3314 dirstate is updated, so file status may be incorrect after running this
3315 dirstate is updated, so file status may be incorrect after running this
3315 command.
3316 command.
3316
3317
3317 Returns 0 on success.
3318 Returns 0 on success.
3318 """
3319 """
3319
3320
3320 r1 = scmutil.revsingle(repo, rev1).node()
3321 r1 = scmutil.revsingle(repo, rev1).node()
3321 r2 = scmutil.revsingle(repo, rev2, 'null').node()
3322 r2 = scmutil.revsingle(repo, rev2, 'null').node()
3322
3323
3323 with repo.wlock():
3324 with repo.wlock():
3324 repo.setparents(r1, r2)
3325 repo.setparents(r1, r2)
3325
3326
3326 @command('debugdirstate|debugstate',
3327 @command('debugdirstate|debugstate',
3327 [('', 'nodates', None, _('do not display the saved mtime')),
3328 [('', 'nodates', None, _('do not display the saved mtime')),
3328 ('', 'datesort', None, _('sort by saved mtime'))],
3329 ('', 'datesort', None, _('sort by saved mtime'))],
3329 _('[OPTION]...'))
3330 _('[OPTION]...'))
3330 def debugstate(ui, repo, **opts):
3331 def debugstate(ui, repo, **opts):
3331 """show the contents of the current dirstate"""
3332 """show the contents of the current dirstate"""
3332
3333
3333 nodates = opts.get('nodates')
3334 nodates = opts.get('nodates')
3334 datesort = opts.get('datesort')
3335 datesort = opts.get('datesort')
3335
3336
3336 timestr = ""
3337 timestr = ""
3337 if datesort:
3338 if datesort:
3338 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
3339 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
3339 else:
3340 else:
3340 keyfunc = None # sort by filename
3341 keyfunc = None # sort by filename
3341 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
3342 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
3342 if ent[3] == -1:
3343 if ent[3] == -1:
3343 timestr = 'unset '
3344 timestr = 'unset '
3344 elif nodates:
3345 elif nodates:
3345 timestr = 'set '
3346 timestr = 'set '
3346 else:
3347 else:
3347 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
3348 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
3348 time.localtime(ent[3]))
3349 time.localtime(ent[3]))
3349 if ent[1] & 0o20000:
3350 if ent[1] & 0o20000:
3350 mode = 'lnk'
3351 mode = 'lnk'
3351 else:
3352 else:
3352 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
3353 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
3353 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
3354 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
3354 for f in repo.dirstate.copies():
3355 for f in repo.dirstate.copies():
3355 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
3356 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
3356
3357
3357 @command('debugsub',
3358 @command('debugsub',
3358 [('r', 'rev', '',
3359 [('r', 'rev', '',
3359 _('revision to check'), _('REV'))],
3360 _('revision to check'), _('REV'))],
3360 _('[-r REV] [REV]'))
3361 _('[-r REV] [REV]'))
3361 def debugsub(ui, repo, rev=None):
3362 def debugsub(ui, repo, rev=None):
3362 ctx = scmutil.revsingle(repo, rev, None)
3363 ctx = scmutil.revsingle(repo, rev, None)
3363 for k, v in sorted(ctx.substate.items()):
3364 for k, v in sorted(ctx.substate.items()):
3364 ui.write(('path %s\n') % k)
3365 ui.write(('path %s\n') % k)
3365 ui.write((' source %s\n') % v[0])
3366 ui.write((' source %s\n') % v[0])
3366 ui.write((' revision %s\n') % v[1])
3367 ui.write((' revision %s\n') % v[1])
3367
3368
3368 @command('debugsuccessorssets',
3369 @command('debugsuccessorssets',
3369 [],
3370 [],
3370 _('[REV]'))
3371 _('[REV]'))
3371 def debugsuccessorssets(ui, repo, *revs):
3372 def debugsuccessorssets(ui, repo, *revs):
3372 """show set of successors for revision
3373 """show set of successors for revision
3373
3374
3374 A successors set of changeset A is a consistent group of revisions that
3375 A successors set of changeset A is a consistent group of revisions that
3375 succeed A. It contains non-obsolete changesets only.
3376 succeed A. It contains non-obsolete changesets only.
3376
3377
3377 In most cases a changeset A has a single successors set containing a single
3378 In most cases a changeset A has a single successors set containing a single
3378 successor (changeset A replaced by A').
3379 successor (changeset A replaced by A').
3379
3380
3380 A changeset that is made obsolete with no successors are called "pruned".
3381 A changeset that is made obsolete with no successors are called "pruned".
3381 Such changesets have no successors sets at all.
3382 Such changesets have no successors sets at all.
3382
3383
3383 A changeset that has been "split" will have a successors set containing
3384 A changeset that has been "split" will have a successors set containing
3384 more than one successor.
3385 more than one successor.
3385
3386
3386 A changeset that has been rewritten in multiple different ways is called
3387 A changeset that has been rewritten in multiple different ways is called
3387 "divergent". Such changesets have multiple successor sets (each of which
3388 "divergent". Such changesets have multiple successor sets (each of which
3388 may also be split, i.e. have multiple successors).
3389 may also be split, i.e. have multiple successors).
3389
3390
3390 Results are displayed as follows::
3391 Results are displayed as follows::
3391
3392
3392 <rev1>
3393 <rev1>
3393 <successors-1A>
3394 <successors-1A>
3394 <rev2>
3395 <rev2>
3395 <successors-2A>
3396 <successors-2A>
3396 <successors-2B1> <successors-2B2> <successors-2B3>
3397 <successors-2B1> <successors-2B2> <successors-2B3>
3397
3398
3398 Here rev2 has two possible (i.e. divergent) successors sets. The first
3399 Here rev2 has two possible (i.e. divergent) successors sets. The first
3399 holds one element, whereas the second holds three (i.e. the changeset has
3400 holds one element, whereas the second holds three (i.e. the changeset has
3400 been split).
3401 been split).
3401 """
3402 """
3402 # passed to successorssets caching computation from one call to another
3403 # passed to successorssets caching computation from one call to another
3403 cache = {}
3404 cache = {}
3404 ctx2str = str
3405 ctx2str = str
3405 node2str = short
3406 node2str = short
3406 if ui.debug():
3407 if ui.debug():
3407 def ctx2str(ctx):
3408 def ctx2str(ctx):
3408 return ctx.hex()
3409 return ctx.hex()
3409 node2str = hex
3410 node2str = hex
3410 for rev in scmutil.revrange(repo, revs):
3411 for rev in scmutil.revrange(repo, revs):
3411 ctx = repo[rev]
3412 ctx = repo[rev]
3412 ui.write('%s\n'% ctx2str(ctx))
3413 ui.write('%s\n'% ctx2str(ctx))
3413 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
3414 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
3414 if succsset:
3415 if succsset:
3415 ui.write(' ')
3416 ui.write(' ')
3416 ui.write(node2str(succsset[0]))
3417 ui.write(node2str(succsset[0]))
3417 for node in succsset[1:]:
3418 for node in succsset[1:]:
3418 ui.write(' ')
3419 ui.write(' ')
3419 ui.write(node2str(node))
3420 ui.write(node2str(node))
3420 ui.write('\n')
3421 ui.write('\n')
3421
3422
3422 @command('debugtemplate',
3423 @command('debugtemplate',
3423 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
3424 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
3424 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
3425 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
3425 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
3426 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
3426 optionalrepo=True)
3427 optionalrepo=True)
3427 def debugtemplate(ui, repo, tmpl, **opts):
3428 def debugtemplate(ui, repo, tmpl, **opts):
3428 """parse and apply a template
3429 """parse and apply a template
3429
3430
3430 If -r/--rev is given, the template is processed as a log template and
3431 If -r/--rev is given, the template is processed as a log template and
3431 applied to the given changesets. Otherwise, it is processed as a generic
3432 applied to the given changesets. Otherwise, it is processed as a generic
3432 template.
3433 template.
3433
3434
3434 Use --verbose to print the parsed tree.
3435 Use --verbose to print the parsed tree.
3435 """
3436 """
3436 revs = None
3437 revs = None
3437 if opts['rev']:
3438 if opts['rev']:
3438 if repo is None:
3439 if repo is None:
3439 raise error.RepoError(_('there is no Mercurial repository here '
3440 raise error.RepoError(_('there is no Mercurial repository here '
3440 '(.hg not found)'))
3441 '(.hg not found)'))
3441 revs = scmutil.revrange(repo, opts['rev'])
3442 revs = scmutil.revrange(repo, opts['rev'])
3442
3443
3443 props = {}
3444 props = {}
3444 for d in opts['define']:
3445 for d in opts['define']:
3445 try:
3446 try:
3446 k, v = (e.strip() for e in d.split('=', 1))
3447 k, v = (e.strip() for e in d.split('=', 1))
3447 if not k:
3448 if not k:
3448 raise ValueError
3449 raise ValueError
3449 props[k] = v
3450 props[k] = v
3450 except ValueError:
3451 except ValueError:
3451 raise error.Abort(_('malformed keyword definition: %s') % d)
3452 raise error.Abort(_('malformed keyword definition: %s') % d)
3452
3453
3453 if ui.verbose:
3454 if ui.verbose:
3454 aliases = ui.configitems('templatealias')
3455 aliases = ui.configitems('templatealias')
3455 tree = templater.parse(tmpl)
3456 tree = templater.parse(tmpl)
3456 ui.note(templater.prettyformat(tree), '\n')
3457 ui.note(templater.prettyformat(tree), '\n')
3457 newtree = templater.expandaliases(tree, aliases)
3458 newtree = templater.expandaliases(tree, aliases)
3458 if newtree != tree:
3459 if newtree != tree:
3459 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
3460 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
3460
3461
3461 mapfile = None
3462 mapfile = None
3462 if revs is None:
3463 if revs is None:
3463 k = 'debugtemplate'
3464 k = 'debugtemplate'
3464 t = formatter.maketemplater(ui, k, tmpl)
3465 t = formatter.maketemplater(ui, k, tmpl)
3465 ui.write(templater.stringify(t(k, **props)))
3466 ui.write(templater.stringify(t(k, **props)))
3466 else:
3467 else:
3467 displayer = cmdutil.changeset_templater(ui, repo, None, opts, tmpl,
3468 displayer = cmdutil.changeset_templater(ui, repo, None, opts, tmpl,
3468 mapfile, buffered=False)
3469 mapfile, buffered=False)
3469 for r in revs:
3470 for r in revs:
3470 displayer.show(repo[r], **props)
3471 displayer.show(repo[r], **props)
3471 displayer.close()
3472 displayer.close()
3472
3473
3473 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
3474 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
3474 def debugwalk(ui, repo, *pats, **opts):
3475 def debugwalk(ui, repo, *pats, **opts):
3475 """show how files match on given patterns"""
3476 """show how files match on given patterns"""
3476 m = scmutil.match(repo[None], pats, opts)
3477 m = scmutil.match(repo[None], pats, opts)
3477 items = list(repo.walk(m))
3478 items = list(repo.walk(m))
3478 if not items:
3479 if not items:
3479 return
3480 return
3480 f = lambda fn: fn
3481 f = lambda fn: fn
3481 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
3482 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
3482 f = lambda fn: util.normpath(fn)
3483 f = lambda fn: util.normpath(fn)
3483 fmt = 'f %%-%ds %%-%ds %%s' % (
3484 fmt = 'f %%-%ds %%-%ds %%s' % (
3484 max([len(abs) for abs in items]),
3485 max([len(abs) for abs in items]),
3485 max([len(m.rel(abs)) for abs in items]))
3486 max([len(m.rel(abs)) for abs in items]))
3486 for abs in items:
3487 for abs in items:
3487 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3488 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3488 ui.write("%s\n" % line.rstrip())
3489 ui.write("%s\n" % line.rstrip())
3489
3490
3490 @command('debugwireargs',
3491 @command('debugwireargs',
3491 [('', 'three', '', 'three'),
3492 [('', 'three', '', 'three'),
3492 ('', 'four', '', 'four'),
3493 ('', 'four', '', 'four'),
3493 ('', 'five', '', 'five'),
3494 ('', 'five', '', 'five'),
3494 ] + remoteopts,
3495 ] + remoteopts,
3495 _('REPO [OPTIONS]... [ONE [TWO]]'),
3496 _('REPO [OPTIONS]... [ONE [TWO]]'),
3496 norepo=True)
3497 norepo=True)
3497 def debugwireargs(ui, repopath, *vals, **opts):
3498 def debugwireargs(ui, repopath, *vals, **opts):
3498 repo = hg.peer(ui, opts, repopath)
3499 repo = hg.peer(ui, opts, repopath)
3499 for opt in remoteopts:
3500 for opt in remoteopts:
3500 del opts[opt[1]]
3501 del opts[opt[1]]
3501 args = {}
3502 args = {}
3502 for k, v in opts.iteritems():
3503 for k, v in opts.iteritems():
3503 if v:
3504 if v:
3504 args[k] = v
3505 args[k] = v
3505 # run twice to check that we don't mess up the stream for the next command
3506 # run twice to check that we don't mess up the stream for the next command
3506 res1 = repo.debugwireargs(*vals, **args)
3507 res1 = repo.debugwireargs(*vals, **args)
3507 res2 = repo.debugwireargs(*vals, **args)
3508 res2 = repo.debugwireargs(*vals, **args)
3508 ui.write("%s\n" % res1)
3509 ui.write("%s\n" % res1)
3509 if res1 != res2:
3510 if res1 != res2:
3510 ui.warn("%s\n" % res2)
3511 ui.warn("%s\n" % res2)
3511
3512
3512 @command('^diff',
3513 @command('^diff',
3513 [('r', 'rev', [], _('revision'), _('REV')),
3514 [('r', 'rev', [], _('revision'), _('REV')),
3514 ('c', 'change', '', _('change made by revision'), _('REV'))
3515 ('c', 'change', '', _('change made by revision'), _('REV'))
3515 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3516 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3516 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3517 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3517 inferrepo=True)
3518 inferrepo=True)
3518 def diff(ui, repo, *pats, **opts):
3519 def diff(ui, repo, *pats, **opts):
3519 """diff repository (or selected files)
3520 """diff repository (or selected files)
3520
3521
3521 Show differences between revisions for the specified files.
3522 Show differences between revisions for the specified files.
3522
3523
3523 Differences between files are shown using the unified diff format.
3524 Differences between files are shown using the unified diff format.
3524
3525
3525 .. note::
3526 .. note::
3526
3527
3527 :hg:`diff` may generate unexpected results for merges, as it will
3528 :hg:`diff` may generate unexpected results for merges, as it will
3528 default to comparing against the working directory's first
3529 default to comparing against the working directory's first
3529 parent changeset if no revisions are specified.
3530 parent changeset if no revisions are specified.
3530
3531
3531 When two revision arguments are given, then changes are shown
3532 When two revision arguments are given, then changes are shown
3532 between those revisions. If only one revision is specified then
3533 between those revisions. If only one revision is specified then
3533 that revision is compared to the working directory, and, when no
3534 that revision is compared to the working directory, and, when no
3534 revisions are specified, the working directory files are compared
3535 revisions are specified, the working directory files are compared
3535 to its first parent.
3536 to its first parent.
3536
3537
3537 Alternatively you can specify -c/--change with a revision to see
3538 Alternatively you can specify -c/--change with a revision to see
3538 the changes in that changeset relative to its first parent.
3539 the changes in that changeset relative to its first parent.
3539
3540
3540 Without the -a/--text option, diff will avoid generating diffs of
3541 Without the -a/--text option, diff will avoid generating diffs of
3541 files it detects as binary. With -a, diff will generate a diff
3542 files it detects as binary. With -a, diff will generate a diff
3542 anyway, probably with undesirable results.
3543 anyway, probably with undesirable results.
3543
3544
3544 Use the -g/--git option to generate diffs in the git extended diff
3545 Use the -g/--git option to generate diffs in the git extended diff
3545 format. For more information, read :hg:`help diffs`.
3546 format. For more information, read :hg:`help diffs`.
3546
3547
3547 .. container:: verbose
3548 .. container:: verbose
3548
3549
3549 Examples:
3550 Examples:
3550
3551
3551 - compare a file in the current working directory to its parent::
3552 - compare a file in the current working directory to its parent::
3552
3553
3553 hg diff foo.c
3554 hg diff foo.c
3554
3555
3555 - compare two historical versions of a directory, with rename info::
3556 - compare two historical versions of a directory, with rename info::
3556
3557
3557 hg diff --git -r 1.0:1.2 lib/
3558 hg diff --git -r 1.0:1.2 lib/
3558
3559
3559 - get change stats relative to the last change on some date::
3560 - get change stats relative to the last change on some date::
3560
3561
3561 hg diff --stat -r "date('may 2')"
3562 hg diff --stat -r "date('may 2')"
3562
3563
3563 - diff all newly-added files that contain a keyword::
3564 - diff all newly-added files that contain a keyword::
3564
3565
3565 hg diff "set:added() and grep(GNU)"
3566 hg diff "set:added() and grep(GNU)"
3566
3567
3567 - compare a revision and its parents::
3568 - compare a revision and its parents::
3568
3569
3569 hg diff -c 9353 # compare against first parent
3570 hg diff -c 9353 # compare against first parent
3570 hg diff -r 9353^:9353 # same using revset syntax
3571 hg diff -r 9353^:9353 # same using revset syntax
3571 hg diff -r 9353^2:9353 # compare against the second parent
3572 hg diff -r 9353^2:9353 # compare against the second parent
3572
3573
3573 Returns 0 on success.
3574 Returns 0 on success.
3574 """
3575 """
3575
3576
3576 revs = opts.get('rev')
3577 revs = opts.get('rev')
3577 change = opts.get('change')
3578 change = opts.get('change')
3578 stat = opts.get('stat')
3579 stat = opts.get('stat')
3579 reverse = opts.get('reverse')
3580 reverse = opts.get('reverse')
3580
3581
3581 if revs and change:
3582 if revs and change:
3582 msg = _('cannot specify --rev and --change at the same time')
3583 msg = _('cannot specify --rev and --change at the same time')
3583 raise error.Abort(msg)
3584 raise error.Abort(msg)
3584 elif change:
3585 elif change:
3585 node2 = scmutil.revsingle(repo, change, None).node()
3586 node2 = scmutil.revsingle(repo, change, None).node()
3586 node1 = repo[node2].p1().node()
3587 node1 = repo[node2].p1().node()
3587 else:
3588 else:
3588 node1, node2 = scmutil.revpair(repo, revs)
3589 node1, node2 = scmutil.revpair(repo, revs)
3589
3590
3590 if reverse:
3591 if reverse:
3591 node1, node2 = node2, node1
3592 node1, node2 = node2, node1
3592
3593
3593 diffopts = patch.diffallopts(ui, opts)
3594 diffopts = patch.diffallopts(ui, opts)
3594 m = scmutil.match(repo[node2], pats, opts)
3595 m = scmutil.match(repo[node2], pats, opts)
3595 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3596 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3596 listsubrepos=opts.get('subrepos'),
3597 listsubrepos=opts.get('subrepos'),
3597 root=opts.get('root'))
3598 root=opts.get('root'))
3598
3599
3599 @command('^export',
3600 @command('^export',
3600 [('o', 'output', '',
3601 [('o', 'output', '',
3601 _('print output to file with formatted name'), _('FORMAT')),
3602 _('print output to file with formatted name'), _('FORMAT')),
3602 ('', 'switch-parent', None, _('diff against the second parent')),
3603 ('', 'switch-parent', None, _('diff against the second parent')),
3603 ('r', 'rev', [], _('revisions to export'), _('REV')),
3604 ('r', 'rev', [], _('revisions to export'), _('REV')),
3604 ] + diffopts,
3605 ] + diffopts,
3605 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3606 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3606 def export(ui, repo, *changesets, **opts):
3607 def export(ui, repo, *changesets, **opts):
3607 """dump the header and diffs for one or more changesets
3608 """dump the header and diffs for one or more changesets
3608
3609
3609 Print the changeset header and diffs for one or more revisions.
3610 Print the changeset header and diffs for one or more revisions.
3610 If no revision is given, the parent of the working directory is used.
3611 If no revision is given, the parent of the working directory is used.
3611
3612
3612 The information shown in the changeset header is: author, date,
3613 The information shown in the changeset header is: author, date,
3613 branch name (if non-default), changeset hash, parent(s) and commit
3614 branch name (if non-default), changeset hash, parent(s) and commit
3614 comment.
3615 comment.
3615
3616
3616 .. note::
3617 .. note::
3617
3618
3618 :hg:`export` may generate unexpected diff output for merge
3619 :hg:`export` may generate unexpected diff output for merge
3619 changesets, as it will compare the merge changeset against its
3620 changesets, as it will compare the merge changeset against its
3620 first parent only.
3621 first parent only.
3621
3622
3622 Output may be to a file, in which case the name of the file is
3623 Output may be to a file, in which case the name of the file is
3623 given using a format string. The formatting rules are as follows:
3624 given using a format string. The formatting rules are as follows:
3624
3625
3625 :``%%``: literal "%" character
3626 :``%%``: literal "%" character
3626 :``%H``: changeset hash (40 hexadecimal digits)
3627 :``%H``: changeset hash (40 hexadecimal digits)
3627 :``%N``: number of patches being generated
3628 :``%N``: number of patches being generated
3628 :``%R``: changeset revision number
3629 :``%R``: changeset revision number
3629 :``%b``: basename of the exporting repository
3630 :``%b``: basename of the exporting repository
3630 :``%h``: short-form changeset hash (12 hexadecimal digits)
3631 :``%h``: short-form changeset hash (12 hexadecimal digits)
3631 :``%m``: first line of the commit message (only alphanumeric characters)
3632 :``%m``: first line of the commit message (only alphanumeric characters)
3632 :``%n``: zero-padded sequence number, starting at 1
3633 :``%n``: zero-padded sequence number, starting at 1
3633 :``%r``: zero-padded changeset revision number
3634 :``%r``: zero-padded changeset revision number
3634
3635
3635 Without the -a/--text option, export will avoid generating diffs
3636 Without the -a/--text option, export will avoid generating diffs
3636 of files it detects as binary. With -a, export will generate a
3637 of files it detects as binary. With -a, export will generate a
3637 diff anyway, probably with undesirable results.
3638 diff anyway, probably with undesirable results.
3638
3639
3639 Use the -g/--git option to generate diffs in the git extended diff
3640 Use the -g/--git option to generate diffs in the git extended diff
3640 format. See :hg:`help diffs` for more information.
3641 format. See :hg:`help diffs` for more information.
3641
3642
3642 With the --switch-parent option, the diff will be against the
3643 With the --switch-parent option, the diff will be against the
3643 second parent. It can be useful to review a merge.
3644 second parent. It can be useful to review a merge.
3644
3645
3645 .. container:: verbose
3646 .. container:: verbose
3646
3647
3647 Examples:
3648 Examples:
3648
3649
3649 - use export and import to transplant a bugfix to the current
3650 - use export and import to transplant a bugfix to the current
3650 branch::
3651 branch::
3651
3652
3652 hg export -r 9353 | hg import -
3653 hg export -r 9353 | hg import -
3653
3654
3654 - export all the changesets between two revisions to a file with
3655 - export all the changesets between two revisions to a file with
3655 rename information::
3656 rename information::
3656
3657
3657 hg export --git -r 123:150 > changes.txt
3658 hg export --git -r 123:150 > changes.txt
3658
3659
3659 - split outgoing changes into a series of patches with
3660 - split outgoing changes into a series of patches with
3660 descriptive names::
3661 descriptive names::
3661
3662
3662 hg export -r "outgoing()" -o "%n-%m.patch"
3663 hg export -r "outgoing()" -o "%n-%m.patch"
3663
3664
3664 Returns 0 on success.
3665 Returns 0 on success.
3665 """
3666 """
3666 changesets += tuple(opts.get('rev', []))
3667 changesets += tuple(opts.get('rev', []))
3667 if not changesets:
3668 if not changesets:
3668 changesets = ['.']
3669 changesets = ['.']
3669 revs = scmutil.revrange(repo, changesets)
3670 revs = scmutil.revrange(repo, changesets)
3670 if not revs:
3671 if not revs:
3671 raise error.Abort(_("export requires at least one changeset"))
3672 raise error.Abort(_("export requires at least one changeset"))
3672 if len(revs) > 1:
3673 if len(revs) > 1:
3673 ui.note(_('exporting patches:\n'))
3674 ui.note(_('exporting patches:\n'))
3674 else:
3675 else:
3675 ui.note(_('exporting patch:\n'))
3676 ui.note(_('exporting patch:\n'))
3676 cmdutil.export(repo, revs, template=opts.get('output'),
3677 cmdutil.export(repo, revs, template=opts.get('output'),
3677 switch_parent=opts.get('switch_parent'),
3678 switch_parent=opts.get('switch_parent'),
3678 opts=patch.diffallopts(ui, opts))
3679 opts=patch.diffallopts(ui, opts))
3679
3680
3680 @command('files',
3681 @command('files',
3681 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3682 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3682 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3683 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3683 ] + walkopts + formatteropts + subrepoopts,
3684 ] + walkopts + formatteropts + subrepoopts,
3684 _('[OPTION]... [FILE]...'))
3685 _('[OPTION]... [FILE]...'))
3685 def files(ui, repo, *pats, **opts):
3686 def files(ui, repo, *pats, **opts):
3686 """list tracked files
3687 """list tracked files
3687
3688
3688 Print files under Mercurial control in the working directory or
3689 Print files under Mercurial control in the working directory or
3689 specified revision for given files (excluding removed files).
3690 specified revision for given files (excluding removed files).
3690 Files can be specified as filenames or filesets.
3691 Files can be specified as filenames or filesets.
3691
3692
3692 If no files are given to match, this command prints the names
3693 If no files are given to match, this command prints the names
3693 of all files under Mercurial control.
3694 of all files under Mercurial control.
3694
3695
3695 .. container:: verbose
3696 .. container:: verbose
3696
3697
3697 Examples:
3698 Examples:
3698
3699
3699 - list all files under the current directory::
3700 - list all files under the current directory::
3700
3701
3701 hg files .
3702 hg files .
3702
3703
3703 - shows sizes and flags for current revision::
3704 - shows sizes and flags for current revision::
3704
3705
3705 hg files -vr .
3706 hg files -vr .
3706
3707
3707 - list all files named README::
3708 - list all files named README::
3708
3709
3709 hg files -I "**/README"
3710 hg files -I "**/README"
3710
3711
3711 - list all binary files::
3712 - list all binary files::
3712
3713
3713 hg files "set:binary()"
3714 hg files "set:binary()"
3714
3715
3715 - find files containing a regular expression::
3716 - find files containing a regular expression::
3716
3717
3717 hg files "set:grep('bob')"
3718 hg files "set:grep('bob')"
3718
3719
3719 - search tracked file contents with xargs and grep::
3720 - search tracked file contents with xargs and grep::
3720
3721
3721 hg files -0 | xargs -0 grep foo
3722 hg files -0 | xargs -0 grep foo
3722
3723
3723 See :hg:`help patterns` and :hg:`help filesets` for more information
3724 See :hg:`help patterns` and :hg:`help filesets` for more information
3724 on specifying file patterns.
3725 on specifying file patterns.
3725
3726
3726 Returns 0 if a match is found, 1 otherwise.
3727 Returns 0 if a match is found, 1 otherwise.
3727
3728
3728 """
3729 """
3729 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3730 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3730
3731
3731 end = '\n'
3732 end = '\n'
3732 if opts.get('print0'):
3733 if opts.get('print0'):
3733 end = '\0'
3734 end = '\0'
3734 fmt = '%s' + end
3735 fmt = '%s' + end
3735
3736
3736 m = scmutil.match(ctx, pats, opts)
3737 m = scmutil.match(ctx, pats, opts)
3737 with ui.formatter('files', opts) as fm:
3738 with ui.formatter('files', opts) as fm:
3738 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
3739 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
3739
3740
3740 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3741 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3741 def forget(ui, repo, *pats, **opts):
3742 def forget(ui, repo, *pats, **opts):
3742 """forget the specified files on the next commit
3743 """forget the specified files on the next commit
3743
3744
3744 Mark the specified files so they will no longer be tracked
3745 Mark the specified files so they will no longer be tracked
3745 after the next commit.
3746 after the next commit.
3746
3747
3747 This only removes files from the current branch, not from the
3748 This only removes files from the current branch, not from the
3748 entire project history, and it does not delete them from the
3749 entire project history, and it does not delete them from the
3749 working directory.
3750 working directory.
3750
3751
3751 To delete the file from the working directory, see :hg:`remove`.
3752 To delete the file from the working directory, see :hg:`remove`.
3752
3753
3753 To undo a forget before the next commit, see :hg:`add`.
3754 To undo a forget before the next commit, see :hg:`add`.
3754
3755
3755 .. container:: verbose
3756 .. container:: verbose
3756
3757
3757 Examples:
3758 Examples:
3758
3759
3759 - forget newly-added binary files::
3760 - forget newly-added binary files::
3760
3761
3761 hg forget "set:added() and binary()"
3762 hg forget "set:added() and binary()"
3762
3763
3763 - forget files that would be excluded by .hgignore::
3764 - forget files that would be excluded by .hgignore::
3764
3765
3765 hg forget "set:hgignore()"
3766 hg forget "set:hgignore()"
3766
3767
3767 Returns 0 on success.
3768 Returns 0 on success.
3768 """
3769 """
3769
3770
3770 if not pats:
3771 if not pats:
3771 raise error.Abort(_('no files specified'))
3772 raise error.Abort(_('no files specified'))
3772
3773
3773 m = scmutil.match(repo[None], pats, opts)
3774 m = scmutil.match(repo[None], pats, opts)
3774 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3775 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3775 return rejected and 1 or 0
3776 return rejected and 1 or 0
3776
3777
3777 @command(
3778 @command(
3778 'graft',
3779 'graft',
3779 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3780 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3780 ('c', 'continue', False, _('resume interrupted graft')),
3781 ('c', 'continue', False, _('resume interrupted graft')),
3781 ('e', 'edit', False, _('invoke editor on commit messages')),
3782 ('e', 'edit', False, _('invoke editor on commit messages')),
3782 ('', 'log', None, _('append graft info to log message')),
3783 ('', 'log', None, _('append graft info to log message')),
3783 ('f', 'force', False, _('force graft')),
3784 ('f', 'force', False, _('force graft')),
3784 ('D', 'currentdate', False,
3785 ('D', 'currentdate', False,
3785 _('record the current date as commit date')),
3786 _('record the current date as commit date')),
3786 ('U', 'currentuser', False,
3787 ('U', 'currentuser', False,
3787 _('record the current user as committer'), _('DATE'))]
3788 _('record the current user as committer'), _('DATE'))]
3788 + commitopts2 + mergetoolopts + dryrunopts,
3789 + commitopts2 + mergetoolopts + dryrunopts,
3789 _('[OPTION]... [-r REV]... REV...'))
3790 _('[OPTION]... [-r REV]... REV...'))
3790 def graft(ui, repo, *revs, **opts):
3791 def graft(ui, repo, *revs, **opts):
3791 '''copy changes from other branches onto the current branch
3792 '''copy changes from other branches onto the current branch
3792
3793
3793 This command uses Mercurial's merge logic to copy individual
3794 This command uses Mercurial's merge logic to copy individual
3794 changes from other branches without merging branches in the
3795 changes from other branches without merging branches in the
3795 history graph. This is sometimes known as 'backporting' or
3796 history graph. This is sometimes known as 'backporting' or
3796 'cherry-picking'. By default, graft will copy user, date, and
3797 'cherry-picking'. By default, graft will copy user, date, and
3797 description from the source changesets.
3798 description from the source changesets.
3798
3799
3799 Changesets that are ancestors of the current revision, that have
3800 Changesets that are ancestors of the current revision, that have
3800 already been grafted, or that are merges will be skipped.
3801 already been grafted, or that are merges will be skipped.
3801
3802
3802 If --log is specified, log messages will have a comment appended
3803 If --log is specified, log messages will have a comment appended
3803 of the form::
3804 of the form::
3804
3805
3805 (grafted from CHANGESETHASH)
3806 (grafted from CHANGESETHASH)
3806
3807
3807 If --force is specified, revisions will be grafted even if they
3808 If --force is specified, revisions will be grafted even if they
3808 are already ancestors of or have been grafted to the destination.
3809 are already ancestors of or have been grafted to the destination.
3809 This is useful when the revisions have since been backed out.
3810 This is useful when the revisions have since been backed out.
3810
3811
3811 If a graft merge results in conflicts, the graft process is
3812 If a graft merge results in conflicts, the graft process is
3812 interrupted so that the current merge can be manually resolved.
3813 interrupted so that the current merge can be manually resolved.
3813 Once all conflicts are addressed, the graft process can be
3814 Once all conflicts are addressed, the graft process can be
3814 continued with the -c/--continue option.
3815 continued with the -c/--continue option.
3815
3816
3816 .. note::
3817 .. note::
3817
3818
3818 The -c/--continue option does not reapply earlier options, except
3819 The -c/--continue option does not reapply earlier options, except
3819 for --force.
3820 for --force.
3820
3821
3821 .. container:: verbose
3822 .. container:: verbose
3822
3823
3823 Examples:
3824 Examples:
3824
3825
3825 - copy a single change to the stable branch and edit its description::
3826 - copy a single change to the stable branch and edit its description::
3826
3827
3827 hg update stable
3828 hg update stable
3828 hg graft --edit 9393
3829 hg graft --edit 9393
3829
3830
3830 - graft a range of changesets with one exception, updating dates::
3831 - graft a range of changesets with one exception, updating dates::
3831
3832
3832 hg graft -D "2085::2093 and not 2091"
3833 hg graft -D "2085::2093 and not 2091"
3833
3834
3834 - continue a graft after resolving conflicts::
3835 - continue a graft after resolving conflicts::
3835
3836
3836 hg graft -c
3837 hg graft -c
3837
3838
3838 - show the source of a grafted changeset::
3839 - show the source of a grafted changeset::
3839
3840
3840 hg log --debug -r .
3841 hg log --debug -r .
3841
3842
3842 - show revisions sorted by date::
3843 - show revisions sorted by date::
3843
3844
3844 hg log -r "sort(all(), date)"
3845 hg log -r "sort(all(), date)"
3845
3846
3846 See :hg:`help revisions` and :hg:`help revsets` for more about
3847 See :hg:`help revisions` and :hg:`help revsets` for more about
3847 specifying revisions.
3848 specifying revisions.
3848
3849
3849 Returns 0 on successful completion.
3850 Returns 0 on successful completion.
3850 '''
3851 '''
3851 with repo.wlock():
3852 with repo.wlock():
3852 return _dograft(ui, repo, *revs, **opts)
3853 return _dograft(ui, repo, *revs, **opts)
3853
3854
3854 def _dograft(ui, repo, *revs, **opts):
3855 def _dograft(ui, repo, *revs, **opts):
3855 if revs and opts.get('rev'):
3856 if revs and opts.get('rev'):
3856 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
3857 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
3857 'revision ordering!\n'))
3858 'revision ordering!\n'))
3858
3859
3859 revs = list(revs)
3860 revs = list(revs)
3860 revs.extend(opts.get('rev'))
3861 revs.extend(opts.get('rev'))
3861
3862
3862 if not opts.get('user') and opts.get('currentuser'):
3863 if not opts.get('user') and opts.get('currentuser'):
3863 opts['user'] = ui.username()
3864 opts['user'] = ui.username()
3864 if not opts.get('date') and opts.get('currentdate'):
3865 if not opts.get('date') and opts.get('currentdate'):
3865 opts['date'] = "%d %d" % util.makedate()
3866 opts['date'] = "%d %d" % util.makedate()
3866
3867
3867 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3868 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3868
3869
3869 cont = False
3870 cont = False
3870 if opts.get('continue'):
3871 if opts.get('continue'):
3871 cont = True
3872 cont = True
3872 if revs:
3873 if revs:
3873 raise error.Abort(_("can't specify --continue and revisions"))
3874 raise error.Abort(_("can't specify --continue and revisions"))
3874 # read in unfinished revisions
3875 # read in unfinished revisions
3875 try:
3876 try:
3876 nodes = repo.vfs.read('graftstate').splitlines()
3877 nodes = repo.vfs.read('graftstate').splitlines()
3877 revs = [repo[node].rev() for node in nodes]
3878 revs = [repo[node].rev() for node in nodes]
3878 except IOError as inst:
3879 except IOError as inst:
3879 if inst.errno != errno.ENOENT:
3880 if inst.errno != errno.ENOENT:
3880 raise
3881 raise
3881 cmdutil.wrongtooltocontinue(repo, _('graft'))
3882 cmdutil.wrongtooltocontinue(repo, _('graft'))
3882 else:
3883 else:
3883 cmdutil.checkunfinished(repo)
3884 cmdutil.checkunfinished(repo)
3884 cmdutil.bailifchanged(repo)
3885 cmdutil.bailifchanged(repo)
3885 if not revs:
3886 if not revs:
3886 raise error.Abort(_('no revisions specified'))
3887 raise error.Abort(_('no revisions specified'))
3887 revs = scmutil.revrange(repo, revs)
3888 revs = scmutil.revrange(repo, revs)
3888
3889
3889 skipped = set()
3890 skipped = set()
3890 # check for merges
3891 # check for merges
3891 for rev in repo.revs('%ld and merge()', revs):
3892 for rev in repo.revs('%ld and merge()', revs):
3892 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3893 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3893 skipped.add(rev)
3894 skipped.add(rev)
3894 revs = [r for r in revs if r not in skipped]
3895 revs = [r for r in revs if r not in skipped]
3895 if not revs:
3896 if not revs:
3896 return -1
3897 return -1
3897
3898
3898 # Don't check in the --continue case, in effect retaining --force across
3899 # Don't check in the --continue case, in effect retaining --force across
3899 # --continues. That's because without --force, any revisions we decided to
3900 # --continues. That's because without --force, any revisions we decided to
3900 # skip would have been filtered out here, so they wouldn't have made their
3901 # skip would have been filtered out here, so they wouldn't have made their
3901 # way to the graftstate. With --force, any revisions we would have otherwise
3902 # way to the graftstate. With --force, any revisions we would have otherwise
3902 # skipped would not have been filtered out, and if they hadn't been applied
3903 # skipped would not have been filtered out, and if they hadn't been applied
3903 # already, they'd have been in the graftstate.
3904 # already, they'd have been in the graftstate.
3904 if not (cont or opts.get('force')):
3905 if not (cont or opts.get('force')):
3905 # check for ancestors of dest branch
3906 # check for ancestors of dest branch
3906 crev = repo['.'].rev()
3907 crev = repo['.'].rev()
3907 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3908 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3908 # XXX make this lazy in the future
3909 # XXX make this lazy in the future
3909 # don't mutate while iterating, create a copy
3910 # don't mutate while iterating, create a copy
3910 for rev in list(revs):
3911 for rev in list(revs):
3911 if rev in ancestors:
3912 if rev in ancestors:
3912 ui.warn(_('skipping ancestor revision %d:%s\n') %
3913 ui.warn(_('skipping ancestor revision %d:%s\n') %
3913 (rev, repo[rev]))
3914 (rev, repo[rev]))
3914 # XXX remove on list is slow
3915 # XXX remove on list is slow
3915 revs.remove(rev)
3916 revs.remove(rev)
3916 if not revs:
3917 if not revs:
3917 return -1
3918 return -1
3918
3919
3919 # analyze revs for earlier grafts
3920 # analyze revs for earlier grafts
3920 ids = {}
3921 ids = {}
3921 for ctx in repo.set("%ld", revs):
3922 for ctx in repo.set("%ld", revs):
3922 ids[ctx.hex()] = ctx.rev()
3923 ids[ctx.hex()] = ctx.rev()
3923 n = ctx.extra().get('source')
3924 n = ctx.extra().get('source')
3924 if n:
3925 if n:
3925 ids[n] = ctx.rev()
3926 ids[n] = ctx.rev()
3926
3927
3927 # check ancestors for earlier grafts
3928 # check ancestors for earlier grafts
3928 ui.debug('scanning for duplicate grafts\n')
3929 ui.debug('scanning for duplicate grafts\n')
3929
3930
3930 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3931 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3931 ctx = repo[rev]
3932 ctx = repo[rev]
3932 n = ctx.extra().get('source')
3933 n = ctx.extra().get('source')
3933 if n in ids:
3934 if n in ids:
3934 try:
3935 try:
3935 r = repo[n].rev()
3936 r = repo[n].rev()
3936 except error.RepoLookupError:
3937 except error.RepoLookupError:
3937 r = None
3938 r = None
3938 if r in revs:
3939 if r in revs:
3939 ui.warn(_('skipping revision %d:%s '
3940 ui.warn(_('skipping revision %d:%s '
3940 '(already grafted to %d:%s)\n')
3941 '(already grafted to %d:%s)\n')
3941 % (r, repo[r], rev, ctx))
3942 % (r, repo[r], rev, ctx))
3942 revs.remove(r)
3943 revs.remove(r)
3943 elif ids[n] in revs:
3944 elif ids[n] in revs:
3944 if r is None:
3945 if r is None:
3945 ui.warn(_('skipping already grafted revision %d:%s '
3946 ui.warn(_('skipping already grafted revision %d:%s '
3946 '(%d:%s also has unknown origin %s)\n')
3947 '(%d:%s also has unknown origin %s)\n')
3947 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3948 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3948 else:
3949 else:
3949 ui.warn(_('skipping already grafted revision %d:%s '
3950 ui.warn(_('skipping already grafted revision %d:%s '
3950 '(%d:%s also has origin %d:%s)\n')
3951 '(%d:%s also has origin %d:%s)\n')
3951 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3952 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3952 revs.remove(ids[n])
3953 revs.remove(ids[n])
3953 elif ctx.hex() in ids:
3954 elif ctx.hex() in ids:
3954 r = ids[ctx.hex()]
3955 r = ids[ctx.hex()]
3955 ui.warn(_('skipping already grafted revision %d:%s '
3956 ui.warn(_('skipping already grafted revision %d:%s '
3956 '(was grafted from %d:%s)\n') %
3957 '(was grafted from %d:%s)\n') %
3957 (r, repo[r], rev, ctx))
3958 (r, repo[r], rev, ctx))
3958 revs.remove(r)
3959 revs.remove(r)
3959 if not revs:
3960 if not revs:
3960 return -1
3961 return -1
3961
3962
3962 for pos, ctx in enumerate(repo.set("%ld", revs)):
3963 for pos, ctx in enumerate(repo.set("%ld", revs)):
3963 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3964 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3964 ctx.description().split('\n', 1)[0])
3965 ctx.description().split('\n', 1)[0])
3965 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3966 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3966 if names:
3967 if names:
3967 desc += ' (%s)' % ' '.join(names)
3968 desc += ' (%s)' % ' '.join(names)
3968 ui.status(_('grafting %s\n') % desc)
3969 ui.status(_('grafting %s\n') % desc)
3969 if opts.get('dry_run'):
3970 if opts.get('dry_run'):
3970 continue
3971 continue
3971
3972
3972 source = ctx.extra().get('source')
3973 source = ctx.extra().get('source')
3973 extra = {}
3974 extra = {}
3974 if source:
3975 if source:
3975 extra['source'] = source
3976 extra['source'] = source
3976 extra['intermediate-source'] = ctx.hex()
3977 extra['intermediate-source'] = ctx.hex()
3977 else:
3978 else:
3978 extra['source'] = ctx.hex()
3979 extra['source'] = ctx.hex()
3979 user = ctx.user()
3980 user = ctx.user()
3980 if opts.get('user'):
3981 if opts.get('user'):
3981 user = opts['user']
3982 user = opts['user']
3982 date = ctx.date()
3983 date = ctx.date()
3983 if opts.get('date'):
3984 if opts.get('date'):
3984 date = opts['date']
3985 date = opts['date']
3985 message = ctx.description()
3986 message = ctx.description()
3986 if opts.get('log'):
3987 if opts.get('log'):
3987 message += '\n(grafted from %s)' % ctx.hex()
3988 message += '\n(grafted from %s)' % ctx.hex()
3988
3989
3989 # we don't merge the first commit when continuing
3990 # we don't merge the first commit when continuing
3990 if not cont:
3991 if not cont:
3991 # perform the graft merge with p1(rev) as 'ancestor'
3992 # perform the graft merge with p1(rev) as 'ancestor'
3992 try:
3993 try:
3993 # ui.forcemerge is an internal variable, do not document
3994 # ui.forcemerge is an internal variable, do not document
3994 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3995 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3995 'graft')
3996 'graft')
3996 stats = mergemod.graft(repo, ctx, ctx.p1(),
3997 stats = mergemod.graft(repo, ctx, ctx.p1(),
3997 ['local', 'graft'])
3998 ['local', 'graft'])
3998 finally:
3999 finally:
3999 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
4000 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
4000 # report any conflicts
4001 # report any conflicts
4001 if stats and stats[3] > 0:
4002 if stats and stats[3] > 0:
4002 # write out state for --continue
4003 # write out state for --continue
4003 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
4004 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
4004 repo.vfs.write('graftstate', ''.join(nodelines))
4005 repo.vfs.write('graftstate', ''.join(nodelines))
4005 extra = ''
4006 extra = ''
4006 if opts.get('user'):
4007 if opts.get('user'):
4007 extra += ' --user %s' % util.shellquote(opts['user'])
4008 extra += ' --user %s' % util.shellquote(opts['user'])
4008 if opts.get('date'):
4009 if opts.get('date'):
4009 extra += ' --date %s' % util.shellquote(opts['date'])
4010 extra += ' --date %s' % util.shellquote(opts['date'])
4010 if opts.get('log'):
4011 if opts.get('log'):
4011 extra += ' --log'
4012 extra += ' --log'
4012 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
4013 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
4013 raise error.Abort(
4014 raise error.Abort(
4014 _("unresolved conflicts, can't continue"),
4015 _("unresolved conflicts, can't continue"),
4015 hint=hint)
4016 hint=hint)
4016 else:
4017 else:
4017 cont = False
4018 cont = False
4018
4019
4019 # commit
4020 # commit
4020 node = repo.commit(text=message, user=user,
4021 node = repo.commit(text=message, user=user,
4021 date=date, extra=extra, editor=editor)
4022 date=date, extra=extra, editor=editor)
4022 if node is None:
4023 if node is None:
4023 ui.warn(
4024 ui.warn(
4024 _('note: graft of %d:%s created no changes to commit\n') %
4025 _('note: graft of %d:%s created no changes to commit\n') %
4025 (ctx.rev(), ctx))
4026 (ctx.rev(), ctx))
4026
4027
4027 # remove state when we complete successfully
4028 # remove state when we complete successfully
4028 if not opts.get('dry_run'):
4029 if not opts.get('dry_run'):
4029 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
4030 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
4030
4031
4031 return 0
4032 return 0
4032
4033
4033 @command('grep',
4034 @command('grep',
4034 [('0', 'print0', None, _('end fields with NUL')),
4035 [('0', 'print0', None, _('end fields with NUL')),
4035 ('', 'all', None, _('print all revisions that match')),
4036 ('', 'all', None, _('print all revisions that match')),
4036 ('a', 'text', None, _('treat all files as text')),
4037 ('a', 'text', None, _('treat all files as text')),
4037 ('f', 'follow', None,
4038 ('f', 'follow', None,
4038 _('follow changeset history,'
4039 _('follow changeset history,'
4039 ' or file history across copies and renames')),
4040 ' or file history across copies and renames')),
4040 ('i', 'ignore-case', None, _('ignore case when matching')),
4041 ('i', 'ignore-case', None, _('ignore case when matching')),
4041 ('l', 'files-with-matches', None,
4042 ('l', 'files-with-matches', None,
4042 _('print only filenames and revisions that match')),
4043 _('print only filenames and revisions that match')),
4043 ('n', 'line-number', None, _('print matching line numbers')),
4044 ('n', 'line-number', None, _('print matching line numbers')),
4044 ('r', 'rev', [],
4045 ('r', 'rev', [],
4045 _('only search files changed within revision range'), _('REV')),
4046 _('only search files changed within revision range'), _('REV')),
4046 ('u', 'user', None, _('list the author (long with -v)')),
4047 ('u', 'user', None, _('list the author (long with -v)')),
4047 ('d', 'date', None, _('list the date (short with -q)')),
4048 ('d', 'date', None, _('list the date (short with -q)')),
4048 ] + formatteropts + walkopts,
4049 ] + formatteropts + walkopts,
4049 _('[OPTION]... PATTERN [FILE]...'),
4050 _('[OPTION]... PATTERN [FILE]...'),
4050 inferrepo=True)
4051 inferrepo=True)
4051 def grep(ui, repo, pattern, *pats, **opts):
4052 def grep(ui, repo, pattern, *pats, **opts):
4052 """search revision history for a pattern in specified files
4053 """search revision history for a pattern in specified files
4053
4054
4054 Search revision history for a regular expression in the specified
4055 Search revision history for a regular expression in the specified
4055 files or the entire project.
4056 files or the entire project.
4056
4057
4057 By default, grep prints the most recent revision number for each
4058 By default, grep prints the most recent revision number for each
4058 file in which it finds a match. To get it to print every revision
4059 file in which it finds a match. To get it to print every revision
4059 that contains a change in match status ("-" for a match that becomes
4060 that contains a change in match status ("-" for a match that becomes
4060 a non-match, or "+" for a non-match that becomes a match), use the
4061 a non-match, or "+" for a non-match that becomes a match), use the
4061 --all flag.
4062 --all flag.
4062
4063
4063 PATTERN can be any Python (roughly Perl-compatible) regular
4064 PATTERN can be any Python (roughly Perl-compatible) regular
4064 expression.
4065 expression.
4065
4066
4066 If no FILEs are specified (and -f/--follow isn't set), all files in
4067 If no FILEs are specified (and -f/--follow isn't set), all files in
4067 the repository are searched, including those that don't exist in the
4068 the repository are searched, including those that don't exist in the
4068 current branch or have been deleted in a prior changeset.
4069 current branch or have been deleted in a prior changeset.
4069
4070
4070 Returns 0 if a match is found, 1 otherwise.
4071 Returns 0 if a match is found, 1 otherwise.
4071 """
4072 """
4072 reflags = re.M
4073 reflags = re.M
4073 if opts.get('ignore_case'):
4074 if opts.get('ignore_case'):
4074 reflags |= re.I
4075 reflags |= re.I
4075 try:
4076 try:
4076 regexp = util.re.compile(pattern, reflags)
4077 regexp = util.re.compile(pattern, reflags)
4077 except re.error as inst:
4078 except re.error as inst:
4078 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
4079 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
4079 return 1
4080 return 1
4080 sep, eol = ':', '\n'
4081 sep, eol = ':', '\n'
4081 if opts.get('print0'):
4082 if opts.get('print0'):
4082 sep = eol = '\0'
4083 sep = eol = '\0'
4083
4084
4084 getfile = util.lrucachefunc(repo.file)
4085 getfile = util.lrucachefunc(repo.file)
4085
4086
4086 def matchlines(body):
4087 def matchlines(body):
4087 begin = 0
4088 begin = 0
4088 linenum = 0
4089 linenum = 0
4089 while begin < len(body):
4090 while begin < len(body):
4090 match = regexp.search(body, begin)
4091 match = regexp.search(body, begin)
4091 if not match:
4092 if not match:
4092 break
4093 break
4093 mstart, mend = match.span()
4094 mstart, mend = match.span()
4094 linenum += body.count('\n', begin, mstart) + 1
4095 linenum += body.count('\n', begin, mstart) + 1
4095 lstart = body.rfind('\n', begin, mstart) + 1 or begin
4096 lstart = body.rfind('\n', begin, mstart) + 1 or begin
4096 begin = body.find('\n', mend) + 1 or len(body) + 1
4097 begin = body.find('\n', mend) + 1 or len(body) + 1
4097 lend = begin - 1
4098 lend = begin - 1
4098 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
4099 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
4099
4100
4100 class linestate(object):
4101 class linestate(object):
4101 def __init__(self, line, linenum, colstart, colend):
4102 def __init__(self, line, linenum, colstart, colend):
4102 self.line = line
4103 self.line = line
4103 self.linenum = linenum
4104 self.linenum = linenum
4104 self.colstart = colstart
4105 self.colstart = colstart
4105 self.colend = colend
4106 self.colend = colend
4106
4107
4107 def __hash__(self):
4108 def __hash__(self):
4108 return hash((self.linenum, self.line))
4109 return hash((self.linenum, self.line))
4109
4110
4110 def __eq__(self, other):
4111 def __eq__(self, other):
4111 return self.line == other.line
4112 return self.line == other.line
4112
4113
4113 def findpos(self):
4114 def findpos(self):
4114 """Iterate all (start, end) indices of matches"""
4115 """Iterate all (start, end) indices of matches"""
4115 yield self.colstart, self.colend
4116 yield self.colstart, self.colend
4116 p = self.colend
4117 p = self.colend
4117 while p < len(self.line):
4118 while p < len(self.line):
4118 m = regexp.search(self.line, p)
4119 m = regexp.search(self.line, p)
4119 if not m:
4120 if not m:
4120 break
4121 break
4121 yield m.span()
4122 yield m.span()
4122 p = m.end()
4123 p = m.end()
4123
4124
4124 matches = {}
4125 matches = {}
4125 copies = {}
4126 copies = {}
4126 def grepbody(fn, rev, body):
4127 def grepbody(fn, rev, body):
4127 matches[rev].setdefault(fn, [])
4128 matches[rev].setdefault(fn, [])
4128 m = matches[rev][fn]
4129 m = matches[rev][fn]
4129 for lnum, cstart, cend, line in matchlines(body):
4130 for lnum, cstart, cend, line in matchlines(body):
4130 s = linestate(line, lnum, cstart, cend)
4131 s = linestate(line, lnum, cstart, cend)
4131 m.append(s)
4132 m.append(s)
4132
4133
4133 def difflinestates(a, b):
4134 def difflinestates(a, b):
4134 sm = difflib.SequenceMatcher(None, a, b)
4135 sm = difflib.SequenceMatcher(None, a, b)
4135 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
4136 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
4136 if tag == 'insert':
4137 if tag == 'insert':
4137 for i in xrange(blo, bhi):
4138 for i in xrange(blo, bhi):
4138 yield ('+', b[i])
4139 yield ('+', b[i])
4139 elif tag == 'delete':
4140 elif tag == 'delete':
4140 for i in xrange(alo, ahi):
4141 for i in xrange(alo, ahi):
4141 yield ('-', a[i])
4142 yield ('-', a[i])
4142 elif tag == 'replace':
4143 elif tag == 'replace':
4143 for i in xrange(alo, ahi):
4144 for i in xrange(alo, ahi):
4144 yield ('-', a[i])
4145 yield ('-', a[i])
4145 for i in xrange(blo, bhi):
4146 for i in xrange(blo, bhi):
4146 yield ('+', b[i])
4147 yield ('+', b[i])
4147
4148
4148 def display(fm, fn, ctx, pstates, states):
4149 def display(fm, fn, ctx, pstates, states):
4149 rev = ctx.rev()
4150 rev = ctx.rev()
4150 if fm.isplain():
4151 if fm.isplain():
4151 formatuser = ui.shortuser
4152 formatuser = ui.shortuser
4152 else:
4153 else:
4153 formatuser = str
4154 formatuser = str
4154 if ui.quiet:
4155 if ui.quiet:
4155 datefmt = '%Y-%m-%d'
4156 datefmt = '%Y-%m-%d'
4156 else:
4157 else:
4157 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
4158 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
4158 found = False
4159 found = False
4159 @util.cachefunc
4160 @util.cachefunc
4160 def binary():
4161 def binary():
4161 flog = getfile(fn)
4162 flog = getfile(fn)
4162 return util.binary(flog.read(ctx.filenode(fn)))
4163 return util.binary(flog.read(ctx.filenode(fn)))
4163
4164
4164 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
4165 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
4165 if opts.get('all'):
4166 if opts.get('all'):
4166 iter = difflinestates(pstates, states)
4167 iter = difflinestates(pstates, states)
4167 else:
4168 else:
4168 iter = [('', l) for l in states]
4169 iter = [('', l) for l in states]
4169 for change, l in iter:
4170 for change, l in iter:
4170 fm.startitem()
4171 fm.startitem()
4171 fm.data(node=fm.hexfunc(ctx.node()))
4172 fm.data(node=fm.hexfunc(ctx.node()))
4172 cols = [
4173 cols = [
4173 ('filename', fn, True),
4174 ('filename', fn, True),
4174 ('rev', rev, True),
4175 ('rev', rev, True),
4175 ('linenumber', l.linenum, opts.get('line_number')),
4176 ('linenumber', l.linenum, opts.get('line_number')),
4176 ]
4177 ]
4177 if opts.get('all'):
4178 if opts.get('all'):
4178 cols.append(('change', change, True))
4179 cols.append(('change', change, True))
4179 cols.extend([
4180 cols.extend([
4180 ('user', formatuser(ctx.user()), opts.get('user')),
4181 ('user', formatuser(ctx.user()), opts.get('user')),
4181 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
4182 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
4182 ])
4183 ])
4183 lastcol = next(name for name, data, cond in reversed(cols) if cond)
4184 lastcol = next(name for name, data, cond in reversed(cols) if cond)
4184 for name, data, cond in cols:
4185 for name, data, cond in cols:
4185 field = fieldnamemap.get(name, name)
4186 field = fieldnamemap.get(name, name)
4186 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
4187 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
4187 if cond and name != lastcol:
4188 if cond and name != lastcol:
4188 fm.plain(sep, label='grep.sep')
4189 fm.plain(sep, label='grep.sep')
4189 if not opts.get('files_with_matches'):
4190 if not opts.get('files_with_matches'):
4190 fm.plain(sep, label='grep.sep')
4191 fm.plain(sep, label='grep.sep')
4191 if not opts.get('text') and binary():
4192 if not opts.get('text') and binary():
4192 fm.plain(_(" Binary file matches"))
4193 fm.plain(_(" Binary file matches"))
4193 else:
4194 else:
4194 displaymatches(fm.nested('texts'), l)
4195 displaymatches(fm.nested('texts'), l)
4195 fm.plain(eol)
4196 fm.plain(eol)
4196 found = True
4197 found = True
4197 if opts.get('files_with_matches'):
4198 if opts.get('files_with_matches'):
4198 break
4199 break
4199 return found
4200 return found
4200
4201
4201 def displaymatches(fm, l):
4202 def displaymatches(fm, l):
4202 p = 0
4203 p = 0
4203 for s, e in l.findpos():
4204 for s, e in l.findpos():
4204 if p < s:
4205 if p < s:
4205 fm.startitem()
4206 fm.startitem()
4206 fm.write('text', '%s', l.line[p:s])
4207 fm.write('text', '%s', l.line[p:s])
4207 fm.data(matched=False)
4208 fm.data(matched=False)
4208 fm.startitem()
4209 fm.startitem()
4209 fm.write('text', '%s', l.line[s:e], label='grep.match')
4210 fm.write('text', '%s', l.line[s:e], label='grep.match')
4210 fm.data(matched=True)
4211 fm.data(matched=True)
4211 p = e
4212 p = e
4212 if p < len(l.line):
4213 if p < len(l.line):
4213 fm.startitem()
4214 fm.startitem()
4214 fm.write('text', '%s', l.line[p:])
4215 fm.write('text', '%s', l.line[p:])
4215 fm.data(matched=False)
4216 fm.data(matched=False)
4216 fm.end()
4217 fm.end()
4217
4218
4218 skip = {}
4219 skip = {}
4219 revfiles = {}
4220 revfiles = {}
4220 matchfn = scmutil.match(repo[None], pats, opts)
4221 matchfn = scmutil.match(repo[None], pats, opts)
4221 found = False
4222 found = False
4222 follow = opts.get('follow')
4223 follow = opts.get('follow')
4223
4224
4224 def prep(ctx, fns):
4225 def prep(ctx, fns):
4225 rev = ctx.rev()
4226 rev = ctx.rev()
4226 pctx = ctx.p1()
4227 pctx = ctx.p1()
4227 parent = pctx.rev()
4228 parent = pctx.rev()
4228 matches.setdefault(rev, {})
4229 matches.setdefault(rev, {})
4229 matches.setdefault(parent, {})
4230 matches.setdefault(parent, {})
4230 files = revfiles.setdefault(rev, [])
4231 files = revfiles.setdefault(rev, [])
4231 for fn in fns:
4232 for fn in fns:
4232 flog = getfile(fn)
4233 flog = getfile(fn)
4233 try:
4234 try:
4234 fnode = ctx.filenode(fn)
4235 fnode = ctx.filenode(fn)
4235 except error.LookupError:
4236 except error.LookupError:
4236 continue
4237 continue
4237
4238
4238 copied = flog.renamed(fnode)
4239 copied = flog.renamed(fnode)
4239 copy = follow and copied and copied[0]
4240 copy = follow and copied and copied[0]
4240 if copy:
4241 if copy:
4241 copies.setdefault(rev, {})[fn] = copy
4242 copies.setdefault(rev, {})[fn] = copy
4242 if fn in skip:
4243 if fn in skip:
4243 if copy:
4244 if copy:
4244 skip[copy] = True
4245 skip[copy] = True
4245 continue
4246 continue
4246 files.append(fn)
4247 files.append(fn)
4247
4248
4248 if fn not in matches[rev]:
4249 if fn not in matches[rev]:
4249 grepbody(fn, rev, flog.read(fnode))
4250 grepbody(fn, rev, flog.read(fnode))
4250
4251
4251 pfn = copy or fn
4252 pfn = copy or fn
4252 if pfn not in matches[parent]:
4253 if pfn not in matches[parent]:
4253 try:
4254 try:
4254 fnode = pctx.filenode(pfn)
4255 fnode = pctx.filenode(pfn)
4255 grepbody(pfn, parent, flog.read(fnode))
4256 grepbody(pfn, parent, flog.read(fnode))
4256 except error.LookupError:
4257 except error.LookupError:
4257 pass
4258 pass
4258
4259
4259 fm = ui.formatter('grep', opts)
4260 fm = ui.formatter('grep', opts)
4260 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
4261 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
4261 rev = ctx.rev()
4262 rev = ctx.rev()
4262 parent = ctx.p1().rev()
4263 parent = ctx.p1().rev()
4263 for fn in sorted(revfiles.get(rev, [])):
4264 for fn in sorted(revfiles.get(rev, [])):
4264 states = matches[rev][fn]
4265 states = matches[rev][fn]
4265 copy = copies.get(rev, {}).get(fn)
4266 copy = copies.get(rev, {}).get(fn)
4266 if fn in skip:
4267 if fn in skip:
4267 if copy:
4268 if copy:
4268 skip[copy] = True
4269 skip[copy] = True
4269 continue
4270 continue
4270 pstates = matches.get(parent, {}).get(copy or fn, [])
4271 pstates = matches.get(parent, {}).get(copy or fn, [])
4271 if pstates or states:
4272 if pstates or states:
4272 r = display(fm, fn, ctx, pstates, states)
4273 r = display(fm, fn, ctx, pstates, states)
4273 found = found or r
4274 found = found or r
4274 if r and not opts.get('all'):
4275 if r and not opts.get('all'):
4275 skip[fn] = True
4276 skip[fn] = True
4276 if copy:
4277 if copy:
4277 skip[copy] = True
4278 skip[copy] = True
4278 del matches[rev]
4279 del matches[rev]
4279 del revfiles[rev]
4280 del revfiles[rev]
4280 fm.end()
4281 fm.end()
4281
4282
4282 return not found
4283 return not found
4283
4284
4284 @command('heads',
4285 @command('heads',
4285 [('r', 'rev', '',
4286 [('r', 'rev', '',
4286 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
4287 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
4287 ('t', 'topo', False, _('show topological heads only')),
4288 ('t', 'topo', False, _('show topological heads only')),
4288 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
4289 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
4289 ('c', 'closed', False, _('show normal and closed branch heads')),
4290 ('c', 'closed', False, _('show normal and closed branch heads')),
4290 ] + templateopts,
4291 ] + templateopts,
4291 _('[-ct] [-r STARTREV] [REV]...'))
4292 _('[-ct] [-r STARTREV] [REV]...'))
4292 def heads(ui, repo, *branchrevs, **opts):
4293 def heads(ui, repo, *branchrevs, **opts):
4293 """show branch heads
4294 """show branch heads
4294
4295
4295 With no arguments, show all open branch heads in the repository.
4296 With no arguments, show all open branch heads in the repository.
4296 Branch heads are changesets that have no descendants on the
4297 Branch heads are changesets that have no descendants on the
4297 same branch. They are where development generally takes place and
4298 same branch. They are where development generally takes place and
4298 are the usual targets for update and merge operations.
4299 are the usual targets for update and merge operations.
4299
4300
4300 If one or more REVs are given, only open branch heads on the
4301 If one or more REVs are given, only open branch heads on the
4301 branches associated with the specified changesets are shown. This
4302 branches associated with the specified changesets are shown. This
4302 means that you can use :hg:`heads .` to see the heads on the
4303 means that you can use :hg:`heads .` to see the heads on the
4303 currently checked-out branch.
4304 currently checked-out branch.
4304
4305
4305 If -c/--closed is specified, also show branch heads marked closed
4306 If -c/--closed is specified, also show branch heads marked closed
4306 (see :hg:`commit --close-branch`).
4307 (see :hg:`commit --close-branch`).
4307
4308
4308 If STARTREV is specified, only those heads that are descendants of
4309 If STARTREV is specified, only those heads that are descendants of
4309 STARTREV will be displayed.
4310 STARTREV will be displayed.
4310
4311
4311 If -t/--topo is specified, named branch mechanics will be ignored and only
4312 If -t/--topo is specified, named branch mechanics will be ignored and only
4312 topological heads (changesets with no children) will be shown.
4313 topological heads (changesets with no children) will be shown.
4313
4314
4314 Returns 0 if matching heads are found, 1 if not.
4315 Returns 0 if matching heads are found, 1 if not.
4315 """
4316 """
4316
4317
4317 start = None
4318 start = None
4318 if 'rev' in opts:
4319 if 'rev' in opts:
4319 start = scmutil.revsingle(repo, opts['rev'], None).node()
4320 start = scmutil.revsingle(repo, opts['rev'], None).node()
4320
4321
4321 if opts.get('topo'):
4322 if opts.get('topo'):
4322 heads = [repo[h] for h in repo.heads(start)]
4323 heads = [repo[h] for h in repo.heads(start)]
4323 else:
4324 else:
4324 heads = []
4325 heads = []
4325 for branch in repo.branchmap():
4326 for branch in repo.branchmap():
4326 heads += repo.branchheads(branch, start, opts.get('closed'))
4327 heads += repo.branchheads(branch, start, opts.get('closed'))
4327 heads = [repo[h] for h in heads]
4328 heads = [repo[h] for h in heads]
4328
4329
4329 if branchrevs:
4330 if branchrevs:
4330 branches = set(repo[br].branch() for br in branchrevs)
4331 branches = set(repo[br].branch() for br in branchrevs)
4331 heads = [h for h in heads if h.branch() in branches]
4332 heads = [h for h in heads if h.branch() in branches]
4332
4333
4333 if opts.get('active') and branchrevs:
4334 if opts.get('active') and branchrevs:
4334 dagheads = repo.heads(start)
4335 dagheads = repo.heads(start)
4335 heads = [h for h in heads if h.node() in dagheads]
4336 heads = [h for h in heads if h.node() in dagheads]
4336
4337
4337 if branchrevs:
4338 if branchrevs:
4338 haveheads = set(h.branch() for h in heads)
4339 haveheads = set(h.branch() for h in heads)
4339 if branches - haveheads:
4340 if branches - haveheads:
4340 headless = ', '.join(b for b in branches - haveheads)
4341 headless = ', '.join(b for b in branches - haveheads)
4341 msg = _('no open branch heads found on branches %s')
4342 msg = _('no open branch heads found on branches %s')
4342 if opts.get('rev'):
4343 if opts.get('rev'):
4343 msg += _(' (started at %s)') % opts['rev']
4344 msg += _(' (started at %s)') % opts['rev']
4344 ui.warn((msg + '\n') % headless)
4345 ui.warn((msg + '\n') % headless)
4345
4346
4346 if not heads:
4347 if not heads:
4347 return 1
4348 return 1
4348
4349
4349 heads = sorted(heads, key=lambda x: -x.rev())
4350 heads = sorted(heads, key=lambda x: -x.rev())
4350 displayer = cmdutil.show_changeset(ui, repo, opts)
4351 displayer = cmdutil.show_changeset(ui, repo, opts)
4351 for ctx in heads:
4352 for ctx in heads:
4352 displayer.show(ctx)
4353 displayer.show(ctx)
4353 displayer.close()
4354 displayer.close()
4354
4355
4355 @command('help',
4356 @command('help',
4356 [('e', 'extension', None, _('show only help for extensions')),
4357 [('e', 'extension', None, _('show only help for extensions')),
4357 ('c', 'command', None, _('show only help for commands')),
4358 ('c', 'command', None, _('show only help for commands')),
4358 ('k', 'keyword', None, _('show topics matching keyword')),
4359 ('k', 'keyword', None, _('show topics matching keyword')),
4359 ('s', 'system', [], _('show help for specific platform(s)')),
4360 ('s', 'system', [], _('show help for specific platform(s)')),
4360 ],
4361 ],
4361 _('[-ecks] [TOPIC]'),
4362 _('[-ecks] [TOPIC]'),
4362 norepo=True)
4363 norepo=True)
4363 def help_(ui, name=None, **opts):
4364 def help_(ui, name=None, **opts):
4364 """show help for a given topic or a help overview
4365 """show help for a given topic or a help overview
4365
4366
4366 With no arguments, print a list of commands with short help messages.
4367 With no arguments, print a list of commands with short help messages.
4367
4368
4368 Given a topic, extension, or command name, print help for that
4369 Given a topic, extension, or command name, print help for that
4369 topic.
4370 topic.
4370
4371
4371 Returns 0 if successful.
4372 Returns 0 if successful.
4372 """
4373 """
4373
4374
4374 textwidth = ui.configint('ui', 'textwidth', 78)
4375 textwidth = ui.configint('ui', 'textwidth', 78)
4375 termwidth = ui.termwidth() - 2
4376 termwidth = ui.termwidth() - 2
4376 if textwidth <= 0 or termwidth < textwidth:
4377 if textwidth <= 0 or termwidth < textwidth:
4377 textwidth = termwidth
4378 textwidth = termwidth
4378
4379
4379 keep = opts.get('system') or []
4380 keep = opts.get('system') or []
4380 if len(keep) == 0:
4381 if len(keep) == 0:
4381 if sys.platform.startswith('win'):
4382 if sys.platform.startswith('win'):
4382 keep.append('windows')
4383 keep.append('windows')
4383 elif sys.platform == 'OpenVMS':
4384 elif sys.platform == 'OpenVMS':
4384 keep.append('vms')
4385 keep.append('vms')
4385 elif sys.platform == 'plan9':
4386 elif sys.platform == 'plan9':
4386 keep.append('plan9')
4387 keep.append('plan9')
4387 else:
4388 else:
4388 keep.append('unix')
4389 keep.append('unix')
4389 keep.append(sys.platform.lower())
4390 keep.append(sys.platform.lower())
4390 if ui.verbose:
4391 if ui.verbose:
4391 keep.append('verbose')
4392 keep.append('verbose')
4392
4393
4393 section = None
4394 section = None
4394 subtopic = None
4395 subtopic = None
4395 if name and '.' in name:
4396 if name and '.' in name:
4396 name, remaining = name.split('.', 1)
4397 name, remaining = name.split('.', 1)
4397 remaining = encoding.lower(remaining)
4398 remaining = encoding.lower(remaining)
4398 if '.' in remaining:
4399 if '.' in remaining:
4399 subtopic, section = remaining.split('.', 1)
4400 subtopic, section = remaining.split('.', 1)
4400 else:
4401 else:
4401 if name in help.subtopics:
4402 if name in help.subtopics:
4402 subtopic = remaining
4403 subtopic = remaining
4403 else:
4404 else:
4404 section = remaining
4405 section = remaining
4405
4406
4406 text = help.help_(ui, name, subtopic=subtopic, **opts)
4407 text = help.help_(ui, name, subtopic=subtopic, **opts)
4407
4408
4408 formatted, pruned = minirst.format(text, textwidth, keep=keep,
4409 formatted, pruned = minirst.format(text, textwidth, keep=keep,
4409 section=section)
4410 section=section)
4410
4411
4411 # We could have been given a weird ".foo" section without a name
4412 # We could have been given a weird ".foo" section without a name
4412 # to look for, or we could have simply failed to found "foo.bar"
4413 # to look for, or we could have simply failed to found "foo.bar"
4413 # because bar isn't a section of foo
4414 # because bar isn't a section of foo
4414 if section and not (formatted and name):
4415 if section and not (formatted and name):
4415 raise error.Abort(_("help section not found"))
4416 raise error.Abort(_("help section not found"))
4416
4417
4417 if 'verbose' in pruned:
4418 if 'verbose' in pruned:
4418 keep.append('omitted')
4419 keep.append('omitted')
4419 else:
4420 else:
4420 keep.append('notomitted')
4421 keep.append('notomitted')
4421 formatted, pruned = minirst.format(text, textwidth, keep=keep,
4422 formatted, pruned = minirst.format(text, textwidth, keep=keep,
4422 section=section)
4423 section=section)
4423 ui.write(formatted)
4424 ui.write(formatted)
4424
4425
4425
4426
4426 @command('identify|id',
4427 @command('identify|id',
4427 [('r', 'rev', '',
4428 [('r', 'rev', '',
4428 _('identify the specified revision'), _('REV')),
4429 _('identify the specified revision'), _('REV')),
4429 ('n', 'num', None, _('show local revision number')),
4430 ('n', 'num', None, _('show local revision number')),
4430 ('i', 'id', None, _('show global revision id')),
4431 ('i', 'id', None, _('show global revision id')),
4431 ('b', 'branch', None, _('show branch')),
4432 ('b', 'branch', None, _('show branch')),
4432 ('t', 'tags', None, _('show tags')),
4433 ('t', 'tags', None, _('show tags')),
4433 ('B', 'bookmarks', None, _('show bookmarks')),
4434 ('B', 'bookmarks', None, _('show bookmarks')),
4434 ] + remoteopts,
4435 ] + remoteopts,
4435 _('[-nibtB] [-r REV] [SOURCE]'),
4436 _('[-nibtB] [-r REV] [SOURCE]'),
4436 optionalrepo=True)
4437 optionalrepo=True)
4437 def identify(ui, repo, source=None, rev=None,
4438 def identify(ui, repo, source=None, rev=None,
4438 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
4439 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
4439 """identify the working directory or specified revision
4440 """identify the working directory or specified revision
4440
4441
4441 Print a summary identifying the repository state at REV using one or
4442 Print a summary identifying the repository state at REV using one or
4442 two parent hash identifiers, followed by a "+" if the working
4443 two parent hash identifiers, followed by a "+" if the working
4443 directory has uncommitted changes, the branch name (if not default),
4444 directory has uncommitted changes, the branch name (if not default),
4444 a list of tags, and a list of bookmarks.
4445 a list of tags, and a list of bookmarks.
4445
4446
4446 When REV is not given, print a summary of the current state of the
4447 When REV is not given, print a summary of the current state of the
4447 repository.
4448 repository.
4448
4449
4449 Specifying a path to a repository root or Mercurial bundle will
4450 Specifying a path to a repository root or Mercurial bundle will
4450 cause lookup to operate on that repository/bundle.
4451 cause lookup to operate on that repository/bundle.
4451
4452
4452 .. container:: verbose
4453 .. container:: verbose
4453
4454
4454 Examples:
4455 Examples:
4455
4456
4456 - generate a build identifier for the working directory::
4457 - generate a build identifier for the working directory::
4457
4458
4458 hg id --id > build-id.dat
4459 hg id --id > build-id.dat
4459
4460
4460 - find the revision corresponding to a tag::
4461 - find the revision corresponding to a tag::
4461
4462
4462 hg id -n -r 1.3
4463 hg id -n -r 1.3
4463
4464
4464 - check the most recent revision of a remote repository::
4465 - check the most recent revision of a remote repository::
4465
4466
4466 hg id -r tip https://www.mercurial-scm.org/repo/hg/
4467 hg id -r tip https://www.mercurial-scm.org/repo/hg/
4467
4468
4468 See :hg:`log` for generating more information about specific revisions,
4469 See :hg:`log` for generating more information about specific revisions,
4469 including full hash identifiers.
4470 including full hash identifiers.
4470
4471
4471 Returns 0 if successful.
4472 Returns 0 if successful.
4472 """
4473 """
4473
4474
4474 if not repo and not source:
4475 if not repo and not source:
4475 raise error.Abort(_("there is no Mercurial repository here "
4476 raise error.Abort(_("there is no Mercurial repository here "
4476 "(.hg not found)"))
4477 "(.hg not found)"))
4477
4478
4478 if ui.debugflag:
4479 if ui.debugflag:
4479 hexfunc = hex
4480 hexfunc = hex
4480 else:
4481 else:
4481 hexfunc = short
4482 hexfunc = short
4482 default = not (num or id or branch or tags or bookmarks)
4483 default = not (num or id or branch or tags or bookmarks)
4483 output = []
4484 output = []
4484 revs = []
4485 revs = []
4485
4486
4486 if source:
4487 if source:
4487 source, branches = hg.parseurl(ui.expandpath(source))
4488 source, branches = hg.parseurl(ui.expandpath(source))
4488 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
4489 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
4489 repo = peer.local()
4490 repo = peer.local()
4490 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
4491 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
4491
4492
4492 if not repo:
4493 if not repo:
4493 if num or branch or tags:
4494 if num or branch or tags:
4494 raise error.Abort(
4495 raise error.Abort(
4495 _("can't query remote revision number, branch, or tags"))
4496 _("can't query remote revision number, branch, or tags"))
4496 if not rev and revs:
4497 if not rev and revs:
4497 rev = revs[0]
4498 rev = revs[0]
4498 if not rev:
4499 if not rev:
4499 rev = "tip"
4500 rev = "tip"
4500
4501
4501 remoterev = peer.lookup(rev)
4502 remoterev = peer.lookup(rev)
4502 if default or id:
4503 if default or id:
4503 output = [hexfunc(remoterev)]
4504 output = [hexfunc(remoterev)]
4504
4505
4505 def getbms():
4506 def getbms():
4506 bms = []
4507 bms = []
4507
4508
4508 if 'bookmarks' in peer.listkeys('namespaces'):
4509 if 'bookmarks' in peer.listkeys('namespaces'):
4509 hexremoterev = hex(remoterev)
4510 hexremoterev = hex(remoterev)
4510 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
4511 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
4511 if bmr == hexremoterev]
4512 if bmr == hexremoterev]
4512
4513
4513 return sorted(bms)
4514 return sorted(bms)
4514
4515
4515 if bookmarks:
4516 if bookmarks:
4516 output.extend(getbms())
4517 output.extend(getbms())
4517 elif default and not ui.quiet:
4518 elif default and not ui.quiet:
4518 # multiple bookmarks for a single parent separated by '/'
4519 # multiple bookmarks for a single parent separated by '/'
4519 bm = '/'.join(getbms())
4520 bm = '/'.join(getbms())
4520 if bm:
4521 if bm:
4521 output.append(bm)
4522 output.append(bm)
4522 else:
4523 else:
4523 ctx = scmutil.revsingle(repo, rev, None)
4524 ctx = scmutil.revsingle(repo, rev, None)
4524
4525
4525 if ctx.rev() is None:
4526 if ctx.rev() is None:
4526 ctx = repo[None]
4527 ctx = repo[None]
4527 parents = ctx.parents()
4528 parents = ctx.parents()
4528 taglist = []
4529 taglist = []
4529 for p in parents:
4530 for p in parents:
4530 taglist.extend(p.tags())
4531 taglist.extend(p.tags())
4531
4532
4532 changed = ""
4533 changed = ""
4533 if default or id or num:
4534 if default or id or num:
4534 if (any(repo.status())
4535 if (any(repo.status())
4535 or any(ctx.sub(s).dirty() for s in ctx.substate)):
4536 or any(ctx.sub(s).dirty() for s in ctx.substate)):
4536 changed = '+'
4537 changed = '+'
4537 if default or id:
4538 if default or id:
4538 output = ["%s%s" %
4539 output = ["%s%s" %
4539 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4540 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4540 if num:
4541 if num:
4541 output.append("%s%s" %
4542 output.append("%s%s" %
4542 ('+'.join([str(p.rev()) for p in parents]), changed))
4543 ('+'.join([str(p.rev()) for p in parents]), changed))
4543 else:
4544 else:
4544 if default or id:
4545 if default or id:
4545 output = [hexfunc(ctx.node())]
4546 output = [hexfunc(ctx.node())]
4546 if num:
4547 if num:
4547 output.append(str(ctx.rev()))
4548 output.append(str(ctx.rev()))
4548 taglist = ctx.tags()
4549 taglist = ctx.tags()
4549
4550
4550 if default and not ui.quiet:
4551 if default and not ui.quiet:
4551 b = ctx.branch()
4552 b = ctx.branch()
4552 if b != 'default':
4553 if b != 'default':
4553 output.append("(%s)" % b)
4554 output.append("(%s)" % b)
4554
4555
4555 # multiple tags for a single parent separated by '/'
4556 # multiple tags for a single parent separated by '/'
4556 t = '/'.join(taglist)
4557 t = '/'.join(taglist)
4557 if t:
4558 if t:
4558 output.append(t)
4559 output.append(t)
4559
4560
4560 # multiple bookmarks for a single parent separated by '/'
4561 # multiple bookmarks for a single parent separated by '/'
4561 bm = '/'.join(ctx.bookmarks())
4562 bm = '/'.join(ctx.bookmarks())
4562 if bm:
4563 if bm:
4563 output.append(bm)
4564 output.append(bm)
4564 else:
4565 else:
4565 if branch:
4566 if branch:
4566 output.append(ctx.branch())
4567 output.append(ctx.branch())
4567
4568
4568 if tags:
4569 if tags:
4569 output.extend(taglist)
4570 output.extend(taglist)
4570
4571
4571 if bookmarks:
4572 if bookmarks:
4572 output.extend(ctx.bookmarks())
4573 output.extend(ctx.bookmarks())
4573
4574
4574 ui.write("%s\n" % ' '.join(output))
4575 ui.write("%s\n" % ' '.join(output))
4575
4576
4576 @command('import|patch',
4577 @command('import|patch',
4577 [('p', 'strip', 1,
4578 [('p', 'strip', 1,
4578 _('directory strip option for patch. This has the same '
4579 _('directory strip option for patch. This has the same '
4579 'meaning as the corresponding patch option'), _('NUM')),
4580 'meaning as the corresponding patch option'), _('NUM')),
4580 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4581 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4581 ('e', 'edit', False, _('invoke editor on commit messages')),
4582 ('e', 'edit', False, _('invoke editor on commit messages')),
4582 ('f', 'force', None,
4583 ('f', 'force', None,
4583 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4584 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4584 ('', 'no-commit', None,
4585 ('', 'no-commit', None,
4585 _("don't commit, just update the working directory")),
4586 _("don't commit, just update the working directory")),
4586 ('', 'bypass', None,
4587 ('', 'bypass', None,
4587 _("apply patch without touching the working directory")),
4588 _("apply patch without touching the working directory")),
4588 ('', 'partial', None,
4589 ('', 'partial', None,
4589 _('commit even if some hunks fail')),
4590 _('commit even if some hunks fail')),
4590 ('', 'exact', None,
4591 ('', 'exact', None,
4591 _('abort if patch would apply lossily')),
4592 _('abort if patch would apply lossily')),
4592 ('', 'prefix', '',
4593 ('', 'prefix', '',
4593 _('apply patch to subdirectory'), _('DIR')),
4594 _('apply patch to subdirectory'), _('DIR')),
4594 ('', 'import-branch', None,
4595 ('', 'import-branch', None,
4595 _('use any branch information in patch (implied by --exact)'))] +
4596 _('use any branch information in patch (implied by --exact)'))] +
4596 commitopts + commitopts2 + similarityopts,
4597 commitopts + commitopts2 + similarityopts,
4597 _('[OPTION]... PATCH...'))
4598 _('[OPTION]... PATCH...'))
4598 def import_(ui, repo, patch1=None, *patches, **opts):
4599 def import_(ui, repo, patch1=None, *patches, **opts):
4599 """import an ordered set of patches
4600 """import an ordered set of patches
4600
4601
4601 Import a list of patches and commit them individually (unless
4602 Import a list of patches and commit them individually (unless
4602 --no-commit is specified).
4603 --no-commit is specified).
4603
4604
4604 To read a patch from standard input, use "-" as the patch name. If
4605 To read a patch from standard input, use "-" as the patch name. If
4605 a URL is specified, the patch will be downloaded from there.
4606 a URL is specified, the patch will be downloaded from there.
4606
4607
4607 Import first applies changes to the working directory (unless
4608 Import first applies changes to the working directory (unless
4608 --bypass is specified), import will abort if there are outstanding
4609 --bypass is specified), import will abort if there are outstanding
4609 changes.
4610 changes.
4610
4611
4611 Use --bypass to apply and commit patches directly to the
4612 Use --bypass to apply and commit patches directly to the
4612 repository, without affecting the working directory. Without
4613 repository, without affecting the working directory. Without
4613 --exact, patches will be applied on top of the working directory
4614 --exact, patches will be applied on top of the working directory
4614 parent revision.
4615 parent revision.
4615
4616
4616 You can import a patch straight from a mail message. Even patches
4617 You can import a patch straight from a mail message. Even patches
4617 as attachments work (to use the body part, it must have type
4618 as attachments work (to use the body part, it must have type
4618 text/plain or text/x-patch). From and Subject headers of email
4619 text/plain or text/x-patch). From and Subject headers of email
4619 message are used as default committer and commit message. All
4620 message are used as default committer and commit message. All
4620 text/plain body parts before first diff are added to the commit
4621 text/plain body parts before first diff are added to the commit
4621 message.
4622 message.
4622
4623
4623 If the imported patch was generated by :hg:`export`, user and
4624 If the imported patch was generated by :hg:`export`, user and
4624 description from patch override values from message headers and
4625 description from patch override values from message headers and
4625 body. Values given on command line with -m/--message and -u/--user
4626 body. Values given on command line with -m/--message and -u/--user
4626 override these.
4627 override these.
4627
4628
4628 If --exact is specified, import will set the working directory to
4629 If --exact is specified, import will set the working directory to
4629 the parent of each patch before applying it, and will abort if the
4630 the parent of each patch before applying it, and will abort if the
4630 resulting changeset has a different ID than the one recorded in
4631 resulting changeset has a different ID than the one recorded in
4631 the patch. This will guard against various ways that portable
4632 the patch. This will guard against various ways that portable
4632 patch formats and mail systems might fail to transfer Mercurial
4633 patch formats and mail systems might fail to transfer Mercurial
4633 data or metadata. See :hg:`bundle` for lossless transmission.
4634 data or metadata. See :hg:`bundle` for lossless transmission.
4634
4635
4635 Use --partial to ensure a changeset will be created from the patch
4636 Use --partial to ensure a changeset will be created from the patch
4636 even if some hunks fail to apply. Hunks that fail to apply will be
4637 even if some hunks fail to apply. Hunks that fail to apply will be
4637 written to a <target-file>.rej file. Conflicts can then be resolved
4638 written to a <target-file>.rej file. Conflicts can then be resolved
4638 by hand before :hg:`commit --amend` is run to update the created
4639 by hand before :hg:`commit --amend` is run to update the created
4639 changeset. This flag exists to let people import patches that
4640 changeset. This flag exists to let people import patches that
4640 partially apply without losing the associated metadata (author,
4641 partially apply without losing the associated metadata (author,
4641 date, description, ...).
4642 date, description, ...).
4642
4643
4643 .. note::
4644 .. note::
4644
4645
4645 When no hunks apply cleanly, :hg:`import --partial` will create
4646 When no hunks apply cleanly, :hg:`import --partial` will create
4646 an empty changeset, importing only the patch metadata.
4647 an empty changeset, importing only the patch metadata.
4647
4648
4648 With -s/--similarity, hg will attempt to discover renames and
4649 With -s/--similarity, hg will attempt to discover renames and
4649 copies in the patch in the same way as :hg:`addremove`.
4650 copies in the patch in the same way as :hg:`addremove`.
4650
4651
4651 It is possible to use external patch programs to perform the patch
4652 It is possible to use external patch programs to perform the patch
4652 by setting the ``ui.patch`` configuration option. For the default
4653 by setting the ``ui.patch`` configuration option. For the default
4653 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4654 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4654 See :hg:`help config` for more information about configuration
4655 See :hg:`help config` for more information about configuration
4655 files and how to use these options.
4656 files and how to use these options.
4656
4657
4657 See :hg:`help dates` for a list of formats valid for -d/--date.
4658 See :hg:`help dates` for a list of formats valid for -d/--date.
4658
4659
4659 .. container:: verbose
4660 .. container:: verbose
4660
4661
4661 Examples:
4662 Examples:
4662
4663
4663 - import a traditional patch from a website and detect renames::
4664 - import a traditional patch from a website and detect renames::
4664
4665
4665 hg import -s 80 http://example.com/bugfix.patch
4666 hg import -s 80 http://example.com/bugfix.patch
4666
4667
4667 - import a changeset from an hgweb server::
4668 - import a changeset from an hgweb server::
4668
4669
4669 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4670 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4670
4671
4671 - import all the patches in an Unix-style mbox::
4672 - import all the patches in an Unix-style mbox::
4672
4673
4673 hg import incoming-patches.mbox
4674 hg import incoming-patches.mbox
4674
4675
4675 - attempt to exactly restore an exported changeset (not always
4676 - attempt to exactly restore an exported changeset (not always
4676 possible)::
4677 possible)::
4677
4678
4678 hg import --exact proposed-fix.patch
4679 hg import --exact proposed-fix.patch
4679
4680
4680 - use an external tool to apply a patch which is too fuzzy for
4681 - use an external tool to apply a patch which is too fuzzy for
4681 the default internal tool.
4682 the default internal tool.
4682
4683
4683 hg import --config ui.patch="patch --merge" fuzzy.patch
4684 hg import --config ui.patch="patch --merge" fuzzy.patch
4684
4685
4685 - change the default fuzzing from 2 to a less strict 7
4686 - change the default fuzzing from 2 to a less strict 7
4686
4687
4687 hg import --config ui.fuzz=7 fuzz.patch
4688 hg import --config ui.fuzz=7 fuzz.patch
4688
4689
4689 Returns 0 on success, 1 on partial success (see --partial).
4690 Returns 0 on success, 1 on partial success (see --partial).
4690 """
4691 """
4691
4692
4692 if not patch1:
4693 if not patch1:
4693 raise error.Abort(_('need at least one patch to import'))
4694 raise error.Abort(_('need at least one patch to import'))
4694
4695
4695 patches = (patch1,) + patches
4696 patches = (patch1,) + patches
4696
4697
4697 date = opts.get('date')
4698 date = opts.get('date')
4698 if date:
4699 if date:
4699 opts['date'] = util.parsedate(date)
4700 opts['date'] = util.parsedate(date)
4700
4701
4701 exact = opts.get('exact')
4702 exact = opts.get('exact')
4702 update = not opts.get('bypass')
4703 update = not opts.get('bypass')
4703 if not update and opts.get('no_commit'):
4704 if not update and opts.get('no_commit'):
4704 raise error.Abort(_('cannot use --no-commit with --bypass'))
4705 raise error.Abort(_('cannot use --no-commit with --bypass'))
4705 try:
4706 try:
4706 sim = float(opts.get('similarity') or 0)
4707 sim = float(opts.get('similarity') or 0)
4707 except ValueError:
4708 except ValueError:
4708 raise error.Abort(_('similarity must be a number'))
4709 raise error.Abort(_('similarity must be a number'))
4709 if sim < 0 or sim > 100:
4710 if sim < 0 or sim > 100:
4710 raise error.Abort(_('similarity must be between 0 and 100'))
4711 raise error.Abort(_('similarity must be between 0 and 100'))
4711 if sim and not update:
4712 if sim and not update:
4712 raise error.Abort(_('cannot use --similarity with --bypass'))
4713 raise error.Abort(_('cannot use --similarity with --bypass'))
4713 if exact:
4714 if exact:
4714 if opts.get('edit'):
4715 if opts.get('edit'):
4715 raise error.Abort(_('cannot use --exact with --edit'))
4716 raise error.Abort(_('cannot use --exact with --edit'))
4716 if opts.get('prefix'):
4717 if opts.get('prefix'):
4717 raise error.Abort(_('cannot use --exact with --prefix'))
4718 raise error.Abort(_('cannot use --exact with --prefix'))
4718
4719
4719 base = opts["base"]
4720 base = opts["base"]
4720 wlock = dsguard = lock = tr = None
4721 wlock = dsguard = lock = tr = None
4721 msgs = []
4722 msgs = []
4722 ret = 0
4723 ret = 0
4723
4724
4724
4725
4725 try:
4726 try:
4726 wlock = repo.wlock()
4727 wlock = repo.wlock()
4727
4728
4728 if update:
4729 if update:
4729 cmdutil.checkunfinished(repo)
4730 cmdutil.checkunfinished(repo)
4730 if (exact or not opts.get('force')):
4731 if (exact or not opts.get('force')):
4731 cmdutil.bailifchanged(repo)
4732 cmdutil.bailifchanged(repo)
4732
4733
4733 if not opts.get('no_commit'):
4734 if not opts.get('no_commit'):
4734 lock = repo.lock()
4735 lock = repo.lock()
4735 tr = repo.transaction('import')
4736 tr = repo.transaction('import')
4736 else:
4737 else:
4737 dsguard = dirstateguard.dirstateguard(repo, 'import')
4738 dsguard = dirstateguard.dirstateguard(repo, 'import')
4738 parents = repo[None].parents()
4739 parents = repo[None].parents()
4739 for patchurl in patches:
4740 for patchurl in patches:
4740 if patchurl == '-':
4741 if patchurl == '-':
4741 ui.status(_('applying patch from stdin\n'))
4742 ui.status(_('applying patch from stdin\n'))
4742 patchfile = ui.fin
4743 patchfile = ui.fin
4743 patchurl = 'stdin' # for error message
4744 patchurl = 'stdin' # for error message
4744 else:
4745 else:
4745 patchurl = os.path.join(base, patchurl)
4746 patchurl = os.path.join(base, patchurl)
4746 ui.status(_('applying %s\n') % patchurl)
4747 ui.status(_('applying %s\n') % patchurl)
4747 patchfile = hg.openpath(ui, patchurl)
4748 patchfile = hg.openpath(ui, patchurl)
4748
4749
4749 haspatch = False
4750 haspatch = False
4750 for hunk in patch.split(patchfile):
4751 for hunk in patch.split(patchfile):
4751 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4752 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4752 parents, opts,
4753 parents, opts,
4753 msgs, hg.clean)
4754 msgs, hg.clean)
4754 if msg:
4755 if msg:
4755 haspatch = True
4756 haspatch = True
4756 ui.note(msg + '\n')
4757 ui.note(msg + '\n')
4757 if update or exact:
4758 if update or exact:
4758 parents = repo[None].parents()
4759 parents = repo[None].parents()
4759 else:
4760 else:
4760 parents = [repo[node]]
4761 parents = [repo[node]]
4761 if rej:
4762 if rej:
4762 ui.write_err(_("patch applied partially\n"))
4763 ui.write_err(_("patch applied partially\n"))
4763 ui.write_err(_("(fix the .rej files and run "
4764 ui.write_err(_("(fix the .rej files and run "
4764 "`hg commit --amend`)\n"))
4765 "`hg commit --amend`)\n"))
4765 ret = 1
4766 ret = 1
4766 break
4767 break
4767
4768
4768 if not haspatch:
4769 if not haspatch:
4769 raise error.Abort(_('%s: no diffs found') % patchurl)
4770 raise error.Abort(_('%s: no diffs found') % patchurl)
4770
4771
4771 if tr:
4772 if tr:
4772 tr.close()
4773 tr.close()
4773 if msgs:
4774 if msgs:
4774 repo.savecommitmessage('\n* * *\n'.join(msgs))
4775 repo.savecommitmessage('\n* * *\n'.join(msgs))
4775 if dsguard:
4776 if dsguard:
4776 dsguard.close()
4777 dsguard.close()
4777 return ret
4778 return ret
4778 finally:
4779 finally:
4779 if tr:
4780 if tr:
4780 tr.release()
4781 tr.release()
4781 release(lock, dsguard, wlock)
4782 release(lock, dsguard, wlock)
4782
4783
4783 @command('incoming|in',
4784 @command('incoming|in',
4784 [('f', 'force', None,
4785 [('f', 'force', None,
4785 _('run even if remote repository is unrelated')),
4786 _('run even if remote repository is unrelated')),
4786 ('n', 'newest-first', None, _('show newest record first')),
4787 ('n', 'newest-first', None, _('show newest record first')),
4787 ('', 'bundle', '',
4788 ('', 'bundle', '',
4788 _('file to store the bundles into'), _('FILE')),
4789 _('file to store the bundles into'), _('FILE')),
4789 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4790 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4790 ('B', 'bookmarks', False, _("compare bookmarks")),
4791 ('B', 'bookmarks', False, _("compare bookmarks")),
4791 ('b', 'branch', [],
4792 ('b', 'branch', [],
4792 _('a specific branch you would like to pull'), _('BRANCH')),
4793 _('a specific branch you would like to pull'), _('BRANCH')),
4793 ] + logopts + remoteopts + subrepoopts,
4794 ] + logopts + remoteopts + subrepoopts,
4794 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4795 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4795 def incoming(ui, repo, source="default", **opts):
4796 def incoming(ui, repo, source="default", **opts):
4796 """show new changesets found in source
4797 """show new changesets found in source
4797
4798
4798 Show new changesets found in the specified path/URL or the default
4799 Show new changesets found in the specified path/URL or the default
4799 pull location. These are the changesets that would have been pulled
4800 pull location. These are the changesets that would have been pulled
4800 if a pull at the time you issued this command.
4801 if a pull at the time you issued this command.
4801
4802
4802 See pull for valid source format details.
4803 See pull for valid source format details.
4803
4804
4804 .. container:: verbose
4805 .. container:: verbose
4805
4806
4806 With -B/--bookmarks, the result of bookmark comparison between
4807 With -B/--bookmarks, the result of bookmark comparison between
4807 local and remote repositories is displayed. With -v/--verbose,
4808 local and remote repositories is displayed. With -v/--verbose,
4808 status is also displayed for each bookmark like below::
4809 status is also displayed for each bookmark like below::
4809
4810
4810 BM1 01234567890a added
4811 BM1 01234567890a added
4811 BM2 1234567890ab advanced
4812 BM2 1234567890ab advanced
4812 BM3 234567890abc diverged
4813 BM3 234567890abc diverged
4813 BM4 34567890abcd changed
4814 BM4 34567890abcd changed
4814
4815
4815 The action taken locally when pulling depends on the
4816 The action taken locally when pulling depends on the
4816 status of each bookmark:
4817 status of each bookmark:
4817
4818
4818 :``added``: pull will create it
4819 :``added``: pull will create it
4819 :``advanced``: pull will update it
4820 :``advanced``: pull will update it
4820 :``diverged``: pull will create a divergent bookmark
4821 :``diverged``: pull will create a divergent bookmark
4821 :``changed``: result depends on remote changesets
4822 :``changed``: result depends on remote changesets
4822
4823
4823 From the point of view of pulling behavior, bookmark
4824 From the point of view of pulling behavior, bookmark
4824 existing only in the remote repository are treated as ``added``,
4825 existing only in the remote repository are treated as ``added``,
4825 even if it is in fact locally deleted.
4826 even if it is in fact locally deleted.
4826
4827
4827 .. container:: verbose
4828 .. container:: verbose
4828
4829
4829 For remote repository, using --bundle avoids downloading the
4830 For remote repository, using --bundle avoids downloading the
4830 changesets twice if the incoming is followed by a pull.
4831 changesets twice if the incoming is followed by a pull.
4831
4832
4832 Examples:
4833 Examples:
4833
4834
4834 - show incoming changes with patches and full description::
4835 - show incoming changes with patches and full description::
4835
4836
4836 hg incoming -vp
4837 hg incoming -vp
4837
4838
4838 - show incoming changes excluding merges, store a bundle::
4839 - show incoming changes excluding merges, store a bundle::
4839
4840
4840 hg in -vpM --bundle incoming.hg
4841 hg in -vpM --bundle incoming.hg
4841 hg pull incoming.hg
4842 hg pull incoming.hg
4842
4843
4843 - briefly list changes inside a bundle::
4844 - briefly list changes inside a bundle::
4844
4845
4845 hg in changes.hg -T "{desc|firstline}\\n"
4846 hg in changes.hg -T "{desc|firstline}\\n"
4846
4847
4847 Returns 0 if there are incoming changes, 1 otherwise.
4848 Returns 0 if there are incoming changes, 1 otherwise.
4848 """
4849 """
4849 if opts.get('graph'):
4850 if opts.get('graph'):
4850 cmdutil.checkunsupportedgraphflags([], opts)
4851 cmdutil.checkunsupportedgraphflags([], opts)
4851 def display(other, chlist, displayer):
4852 def display(other, chlist, displayer):
4852 revdag = cmdutil.graphrevs(other, chlist, opts)
4853 revdag = cmdutil.graphrevs(other, chlist, opts)
4853 cmdutil.displaygraph(ui, repo, revdag, displayer,
4854 cmdutil.displaygraph(ui, repo, revdag, displayer,
4854 graphmod.asciiedges)
4855 graphmod.asciiedges)
4855
4856
4856 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4857 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4857 return 0
4858 return 0
4858
4859
4859 if opts.get('bundle') and opts.get('subrepos'):
4860 if opts.get('bundle') and opts.get('subrepos'):
4860 raise error.Abort(_('cannot combine --bundle and --subrepos'))
4861 raise error.Abort(_('cannot combine --bundle and --subrepos'))
4861
4862
4862 if opts.get('bookmarks'):
4863 if opts.get('bookmarks'):
4863 source, branches = hg.parseurl(ui.expandpath(source),
4864 source, branches = hg.parseurl(ui.expandpath(source),
4864 opts.get('branch'))
4865 opts.get('branch'))
4865 other = hg.peer(repo, opts, source)
4866 other = hg.peer(repo, opts, source)
4866 if 'bookmarks' not in other.listkeys('namespaces'):
4867 if 'bookmarks' not in other.listkeys('namespaces'):
4867 ui.warn(_("remote doesn't support bookmarks\n"))
4868 ui.warn(_("remote doesn't support bookmarks\n"))
4868 return 0
4869 return 0
4869 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4870 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4870 return bookmarks.incoming(ui, repo, other)
4871 return bookmarks.incoming(ui, repo, other)
4871
4872
4872 repo._subtoppath = ui.expandpath(source)
4873 repo._subtoppath = ui.expandpath(source)
4873 try:
4874 try:
4874 return hg.incoming(ui, repo, source, opts)
4875 return hg.incoming(ui, repo, source, opts)
4875 finally:
4876 finally:
4876 del repo._subtoppath
4877 del repo._subtoppath
4877
4878
4878
4879
4879 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4880 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4880 norepo=True)
4881 norepo=True)
4881 def init(ui, dest=".", **opts):
4882 def init(ui, dest=".", **opts):
4882 """create a new repository in the given directory
4883 """create a new repository in the given directory
4883
4884
4884 Initialize a new repository in the given directory. If the given
4885 Initialize a new repository in the given directory. If the given
4885 directory does not exist, it will be created.
4886 directory does not exist, it will be created.
4886
4887
4887 If no directory is given, the current directory is used.
4888 If no directory is given, the current directory is used.
4888
4889
4889 It is possible to specify an ``ssh://`` URL as the destination.
4890 It is possible to specify an ``ssh://`` URL as the destination.
4890 See :hg:`help urls` for more information.
4891 See :hg:`help urls` for more information.
4891
4892
4892 Returns 0 on success.
4893 Returns 0 on success.
4893 """
4894 """
4894 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4895 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4895
4896
4896 @command('locate',
4897 @command('locate',
4897 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4898 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4898 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4899 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4899 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4900 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4900 ] + walkopts,
4901 ] + walkopts,
4901 _('[OPTION]... [PATTERN]...'))
4902 _('[OPTION]... [PATTERN]...'))
4902 def locate(ui, repo, *pats, **opts):
4903 def locate(ui, repo, *pats, **opts):
4903 """locate files matching specific patterns (DEPRECATED)
4904 """locate files matching specific patterns (DEPRECATED)
4904
4905
4905 Print files under Mercurial control in the working directory whose
4906 Print files under Mercurial control in the working directory whose
4906 names match the given patterns.
4907 names match the given patterns.
4907
4908
4908 By default, this command searches all directories in the working
4909 By default, this command searches all directories in the working
4909 directory. To search just the current directory and its
4910 directory. To search just the current directory and its
4910 subdirectories, use "--include .".
4911 subdirectories, use "--include .".
4911
4912
4912 If no patterns are given to match, this command prints the names
4913 If no patterns are given to match, this command prints the names
4913 of all files under Mercurial control in the working directory.
4914 of all files under Mercurial control in the working directory.
4914
4915
4915 If you want to feed the output of this command into the "xargs"
4916 If you want to feed the output of this command into the "xargs"
4916 command, use the -0 option to both this command and "xargs". This
4917 command, use the -0 option to both this command and "xargs". This
4917 will avoid the problem of "xargs" treating single filenames that
4918 will avoid the problem of "xargs" treating single filenames that
4918 contain whitespace as multiple filenames.
4919 contain whitespace as multiple filenames.
4919
4920
4920 See :hg:`help files` for a more versatile command.
4921 See :hg:`help files` for a more versatile command.
4921
4922
4922 Returns 0 if a match is found, 1 otherwise.
4923 Returns 0 if a match is found, 1 otherwise.
4923 """
4924 """
4924 if opts.get('print0'):
4925 if opts.get('print0'):
4925 end = '\0'
4926 end = '\0'
4926 else:
4927 else:
4927 end = '\n'
4928 end = '\n'
4928 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4929 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4929
4930
4930 ret = 1
4931 ret = 1
4931 ctx = repo[rev]
4932 ctx = repo[rev]
4932 m = scmutil.match(ctx, pats, opts, default='relglob',
4933 m = scmutil.match(ctx, pats, opts, default='relglob',
4933 badfn=lambda x, y: False)
4934 badfn=lambda x, y: False)
4934
4935
4935 for abs in ctx.matches(m):
4936 for abs in ctx.matches(m):
4936 if opts.get('fullpath'):
4937 if opts.get('fullpath'):
4937 ui.write(repo.wjoin(abs), end)
4938 ui.write(repo.wjoin(abs), end)
4938 else:
4939 else:
4939 ui.write(((pats and m.rel(abs)) or abs), end)
4940 ui.write(((pats and m.rel(abs)) or abs), end)
4940 ret = 0
4941 ret = 0
4941
4942
4942 return ret
4943 return ret
4943
4944
4944 @command('^log|history',
4945 @command('^log|history',
4945 [('f', 'follow', None,
4946 [('f', 'follow', None,
4946 _('follow changeset history, or file history across copies and renames')),
4947 _('follow changeset history, or file history across copies and renames')),
4947 ('', 'follow-first', None,
4948 ('', 'follow-first', None,
4948 _('only follow the first parent of merge changesets (DEPRECATED)')),
4949 _('only follow the first parent of merge changesets (DEPRECATED)')),
4949 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4950 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4950 ('C', 'copies', None, _('show copied files')),
4951 ('C', 'copies', None, _('show copied files')),
4951 ('k', 'keyword', [],
4952 ('k', 'keyword', [],
4952 _('do case-insensitive search for a given text'), _('TEXT')),
4953 _('do case-insensitive search for a given text'), _('TEXT')),
4953 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4954 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4954 ('', 'removed', None, _('include revisions where files were removed')),
4955 ('', 'removed', None, _('include revisions where files were removed')),
4955 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4956 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4956 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4957 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4957 ('', 'only-branch', [],
4958 ('', 'only-branch', [],
4958 _('show only changesets within the given named branch (DEPRECATED)'),
4959 _('show only changesets within the given named branch (DEPRECATED)'),
4959 _('BRANCH')),
4960 _('BRANCH')),
4960 ('b', 'branch', [],
4961 ('b', 'branch', [],
4961 _('show changesets within the given named branch'), _('BRANCH')),
4962 _('show changesets within the given named branch'), _('BRANCH')),
4962 ('P', 'prune', [],
4963 ('P', 'prune', [],
4963 _('do not display revision or any of its ancestors'), _('REV')),
4964 _('do not display revision or any of its ancestors'), _('REV')),
4964 ] + logopts + walkopts,
4965 ] + logopts + walkopts,
4965 _('[OPTION]... [FILE]'),
4966 _('[OPTION]... [FILE]'),
4966 inferrepo=True)
4967 inferrepo=True)
4967 def log(ui, repo, *pats, **opts):
4968 def log(ui, repo, *pats, **opts):
4968 """show revision history of entire repository or files
4969 """show revision history of entire repository or files
4969
4970
4970 Print the revision history of the specified files or the entire
4971 Print the revision history of the specified files or the entire
4971 project.
4972 project.
4972
4973
4973 If no revision range is specified, the default is ``tip:0`` unless
4974 If no revision range is specified, the default is ``tip:0`` unless
4974 --follow is set, in which case the working directory parent is
4975 --follow is set, in which case the working directory parent is
4975 used as the starting revision.
4976 used as the starting revision.
4976
4977
4977 File history is shown without following rename or copy history of
4978 File history is shown without following rename or copy history of
4978 files. Use -f/--follow with a filename to follow history across
4979 files. Use -f/--follow with a filename to follow history across
4979 renames and copies. --follow without a filename will only show
4980 renames and copies. --follow without a filename will only show
4980 ancestors or descendants of the starting revision.
4981 ancestors or descendants of the starting revision.
4981
4982
4982 By default this command prints revision number and changeset id,
4983 By default this command prints revision number and changeset id,
4983 tags, non-trivial parents, user, date and time, and a summary for
4984 tags, non-trivial parents, user, date and time, and a summary for
4984 each commit. When the -v/--verbose switch is used, the list of
4985 each commit. When the -v/--verbose switch is used, the list of
4985 changed files and full commit message are shown.
4986 changed files and full commit message are shown.
4986
4987
4987 With --graph the revisions are shown as an ASCII art DAG with the most
4988 With --graph the revisions are shown as an ASCII art DAG with the most
4988 recent changeset at the top.
4989 recent changeset at the top.
4989 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4990 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4990 and '+' represents a fork where the changeset from the lines below is a
4991 and '+' represents a fork where the changeset from the lines below is a
4991 parent of the 'o' merge on the same line.
4992 parent of the 'o' merge on the same line.
4992
4993
4993 .. note::
4994 .. note::
4994
4995
4995 :hg:`log --patch` may generate unexpected diff output for merge
4996 :hg:`log --patch` may generate unexpected diff output for merge
4996 changesets, as it will only compare the merge changeset against
4997 changesets, as it will only compare the merge changeset against
4997 its first parent. Also, only files different from BOTH parents
4998 its first parent. Also, only files different from BOTH parents
4998 will appear in files:.
4999 will appear in files:.
4999
5000
5000 .. note::
5001 .. note::
5001
5002
5002 For performance reasons, :hg:`log FILE` may omit duplicate changes
5003 For performance reasons, :hg:`log FILE` may omit duplicate changes
5003 made on branches and will not show removals or mode changes. To
5004 made on branches and will not show removals or mode changes. To
5004 see all such changes, use the --removed switch.
5005 see all such changes, use the --removed switch.
5005
5006
5006 .. container:: verbose
5007 .. container:: verbose
5007
5008
5008 Some examples:
5009 Some examples:
5009
5010
5010 - changesets with full descriptions and file lists::
5011 - changesets with full descriptions and file lists::
5011
5012
5012 hg log -v
5013 hg log -v
5013
5014
5014 - changesets ancestral to the working directory::
5015 - changesets ancestral to the working directory::
5015
5016
5016 hg log -f
5017 hg log -f
5017
5018
5018 - last 10 commits on the current branch::
5019 - last 10 commits on the current branch::
5019
5020
5020 hg log -l 10 -b .
5021 hg log -l 10 -b .
5021
5022
5022 - changesets showing all modifications of a file, including removals::
5023 - changesets showing all modifications of a file, including removals::
5023
5024
5024 hg log --removed file.c
5025 hg log --removed file.c
5025
5026
5026 - all changesets that touch a directory, with diffs, excluding merges::
5027 - all changesets that touch a directory, with diffs, excluding merges::
5027
5028
5028 hg log -Mp lib/
5029 hg log -Mp lib/
5029
5030
5030 - all revision numbers that match a keyword::
5031 - all revision numbers that match a keyword::
5031
5032
5032 hg log -k bug --template "{rev}\\n"
5033 hg log -k bug --template "{rev}\\n"
5033
5034
5034 - the full hash identifier of the working directory parent::
5035 - the full hash identifier of the working directory parent::
5035
5036
5036 hg log -r . --template "{node}\\n"
5037 hg log -r . --template "{node}\\n"
5037
5038
5038 - list available log templates::
5039 - list available log templates::
5039
5040
5040 hg log -T list
5041 hg log -T list
5041
5042
5042 - check if a given changeset is included in a tagged release::
5043 - check if a given changeset is included in a tagged release::
5043
5044
5044 hg log -r "a21ccf and ancestor(1.9)"
5045 hg log -r "a21ccf and ancestor(1.9)"
5045
5046
5046 - find all changesets by some user in a date range::
5047 - find all changesets by some user in a date range::
5047
5048
5048 hg log -k alice -d "may 2008 to jul 2008"
5049 hg log -k alice -d "may 2008 to jul 2008"
5049
5050
5050 - summary of all changesets after the last tag::
5051 - summary of all changesets after the last tag::
5051
5052
5052 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
5053 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
5053
5054
5054 See :hg:`help dates` for a list of formats valid for -d/--date.
5055 See :hg:`help dates` for a list of formats valid for -d/--date.
5055
5056
5056 See :hg:`help revisions` and :hg:`help revsets` for more about
5057 See :hg:`help revisions` and :hg:`help revsets` for more about
5057 specifying and ordering revisions.
5058 specifying and ordering revisions.
5058
5059
5059 See :hg:`help templates` for more about pre-packaged styles and
5060 See :hg:`help templates` for more about pre-packaged styles and
5060 specifying custom templates.
5061 specifying custom templates.
5061
5062
5062 Returns 0 on success.
5063 Returns 0 on success.
5063
5064
5064 """
5065 """
5065 if opts.get('follow') and opts.get('rev'):
5066 if opts.get('follow') and opts.get('rev'):
5066 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
5067 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
5067 del opts['follow']
5068 del opts['follow']
5068
5069
5069 if opts.get('graph'):
5070 if opts.get('graph'):
5070 return cmdutil.graphlog(ui, repo, *pats, **opts)
5071 return cmdutil.graphlog(ui, repo, *pats, **opts)
5071
5072
5072 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
5073 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
5073 limit = cmdutil.loglimit(opts)
5074 limit = cmdutil.loglimit(opts)
5074 count = 0
5075 count = 0
5075
5076
5076 getrenamed = None
5077 getrenamed = None
5077 if opts.get('copies'):
5078 if opts.get('copies'):
5078 endrev = None
5079 endrev = None
5079 if opts.get('rev'):
5080 if opts.get('rev'):
5080 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
5081 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
5081 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
5082 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
5082
5083
5083 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
5084 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
5084 for rev in revs:
5085 for rev in revs:
5085 if count == limit:
5086 if count == limit:
5086 break
5087 break
5087 ctx = repo[rev]
5088 ctx = repo[rev]
5088 copies = None
5089 copies = None
5089 if getrenamed is not None and rev:
5090 if getrenamed is not None and rev:
5090 copies = []
5091 copies = []
5091 for fn in ctx.files():
5092 for fn in ctx.files():
5092 rename = getrenamed(fn, rev)
5093 rename = getrenamed(fn, rev)
5093 if rename:
5094 if rename:
5094 copies.append((fn, rename[0]))
5095 copies.append((fn, rename[0]))
5095 if filematcher:
5096 if filematcher:
5096 revmatchfn = filematcher(ctx.rev())
5097 revmatchfn = filematcher(ctx.rev())
5097 else:
5098 else:
5098 revmatchfn = None
5099 revmatchfn = None
5099 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
5100 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
5100 if displayer.flush(ctx):
5101 if displayer.flush(ctx):
5101 count += 1
5102 count += 1
5102
5103
5103 displayer.close()
5104 displayer.close()
5104
5105
5105 @command('manifest',
5106 @command('manifest',
5106 [('r', 'rev', '', _('revision to display'), _('REV')),
5107 [('r', 'rev', '', _('revision to display'), _('REV')),
5107 ('', 'all', False, _("list files from all revisions"))]
5108 ('', 'all', False, _("list files from all revisions"))]
5108 + formatteropts,
5109 + formatteropts,
5109 _('[-r REV]'))
5110 _('[-r REV]'))
5110 def manifest(ui, repo, node=None, rev=None, **opts):
5111 def manifest(ui, repo, node=None, rev=None, **opts):
5111 """output the current or given revision of the project manifest
5112 """output the current or given revision of the project manifest
5112
5113
5113 Print a list of version controlled files for the given revision.
5114 Print a list of version controlled files for the given revision.
5114 If no revision is given, the first parent of the working directory
5115 If no revision is given, the first parent of the working directory
5115 is used, or the null revision if no revision is checked out.
5116 is used, or the null revision if no revision is checked out.
5116
5117
5117 With -v, print file permissions, symlink and executable bits.
5118 With -v, print file permissions, symlink and executable bits.
5118 With --debug, print file revision hashes.
5119 With --debug, print file revision hashes.
5119
5120
5120 If option --all is specified, the list of all files from all revisions
5121 If option --all is specified, the list of all files from all revisions
5121 is printed. This includes deleted and renamed files.
5122 is printed. This includes deleted and renamed files.
5122
5123
5123 Returns 0 on success.
5124 Returns 0 on success.
5124 """
5125 """
5125
5126
5126 fm = ui.formatter('manifest', opts)
5127 fm = ui.formatter('manifest', opts)
5127
5128
5128 if opts.get('all'):
5129 if opts.get('all'):
5129 if rev or node:
5130 if rev or node:
5130 raise error.Abort(_("can't specify a revision with --all"))
5131 raise error.Abort(_("can't specify a revision with --all"))
5131
5132
5132 res = []
5133 res = []
5133 prefix = "data/"
5134 prefix = "data/"
5134 suffix = ".i"
5135 suffix = ".i"
5135 plen = len(prefix)
5136 plen = len(prefix)
5136 slen = len(suffix)
5137 slen = len(suffix)
5137 with repo.lock():
5138 with repo.lock():
5138 for fn, b, size in repo.store.datafiles():
5139 for fn, b, size in repo.store.datafiles():
5139 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
5140 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
5140 res.append(fn[plen:-slen])
5141 res.append(fn[plen:-slen])
5141 for f in res:
5142 for f in res:
5142 fm.startitem()
5143 fm.startitem()
5143 fm.write("path", '%s\n', f)
5144 fm.write("path", '%s\n', f)
5144 fm.end()
5145 fm.end()
5145 return
5146 return
5146
5147
5147 if rev and node:
5148 if rev and node:
5148 raise error.Abort(_("please specify just one revision"))
5149 raise error.Abort(_("please specify just one revision"))
5149
5150
5150 if not node:
5151 if not node:
5151 node = rev
5152 node = rev
5152
5153
5153 char = {'l': '@', 'x': '*', '': ''}
5154 char = {'l': '@', 'x': '*', '': ''}
5154 mode = {'l': '644', 'x': '755', '': '644'}
5155 mode = {'l': '644', 'x': '755', '': '644'}
5155 ctx = scmutil.revsingle(repo, node)
5156 ctx = scmutil.revsingle(repo, node)
5156 mf = ctx.manifest()
5157 mf = ctx.manifest()
5157 for f in ctx:
5158 for f in ctx:
5158 fm.startitem()
5159 fm.startitem()
5159 fl = ctx[f].flags()
5160 fl = ctx[f].flags()
5160 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
5161 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
5161 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
5162 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
5162 fm.write('path', '%s\n', f)
5163 fm.write('path', '%s\n', f)
5163 fm.end()
5164 fm.end()
5164
5165
5165 @command('^merge',
5166 @command('^merge',
5166 [('f', 'force', None,
5167 [('f', 'force', None,
5167 _('force a merge including outstanding changes (DEPRECATED)')),
5168 _('force a merge including outstanding changes (DEPRECATED)')),
5168 ('r', 'rev', '', _('revision to merge'), _('REV')),
5169 ('r', 'rev', '', _('revision to merge'), _('REV')),
5169 ('P', 'preview', None,
5170 ('P', 'preview', None,
5170 _('review revisions to merge (no merge is performed)'))
5171 _('review revisions to merge (no merge is performed)'))
5171 ] + mergetoolopts,
5172 ] + mergetoolopts,
5172 _('[-P] [[-r] REV]'))
5173 _('[-P] [[-r] REV]'))
5173 def merge(ui, repo, node=None, **opts):
5174 def merge(ui, repo, node=None, **opts):
5174 """merge another revision into working directory
5175 """merge another revision into working directory
5175
5176
5176 The current working directory is updated with all changes made in
5177 The current working directory is updated with all changes made in
5177 the requested revision since the last common predecessor revision.
5178 the requested revision since the last common predecessor revision.
5178
5179
5179 Files that changed between either parent are marked as changed for
5180 Files that changed between either parent are marked as changed for
5180 the next commit and a commit must be performed before any further
5181 the next commit and a commit must be performed before any further
5181 updates to the repository are allowed. The next commit will have
5182 updates to the repository are allowed. The next commit will have
5182 two parents.
5183 two parents.
5183
5184
5184 ``--tool`` can be used to specify the merge tool used for file
5185 ``--tool`` can be used to specify the merge tool used for file
5185 merges. It overrides the HGMERGE environment variable and your
5186 merges. It overrides the HGMERGE environment variable and your
5186 configuration files. See :hg:`help merge-tools` for options.
5187 configuration files. See :hg:`help merge-tools` for options.
5187
5188
5188 If no revision is specified, the working directory's parent is a
5189 If no revision is specified, the working directory's parent is a
5189 head revision, and the current branch contains exactly one other
5190 head revision, and the current branch contains exactly one other
5190 head, the other head is merged with by default. Otherwise, an
5191 head, the other head is merged with by default. Otherwise, an
5191 explicit revision with which to merge with must be provided.
5192 explicit revision with which to merge with must be provided.
5192
5193
5193 See :hg:`help resolve` for information on handling file conflicts.
5194 See :hg:`help resolve` for information on handling file conflicts.
5194
5195
5195 To undo an uncommitted merge, use :hg:`update --clean .` which
5196 To undo an uncommitted merge, use :hg:`update --clean .` which
5196 will check out a clean copy of the original merge parent, losing
5197 will check out a clean copy of the original merge parent, losing
5197 all changes.
5198 all changes.
5198
5199
5199 Returns 0 on success, 1 if there are unresolved files.
5200 Returns 0 on success, 1 if there are unresolved files.
5200 """
5201 """
5201
5202
5202 if opts.get('rev') and node:
5203 if opts.get('rev') and node:
5203 raise error.Abort(_("please specify just one revision"))
5204 raise error.Abort(_("please specify just one revision"))
5204 if not node:
5205 if not node:
5205 node = opts.get('rev')
5206 node = opts.get('rev')
5206
5207
5207 if node:
5208 if node:
5208 node = scmutil.revsingle(repo, node).node()
5209 node = scmutil.revsingle(repo, node).node()
5209
5210
5210 if not node:
5211 if not node:
5211 node = repo[destutil.destmerge(repo)].node()
5212 node = repo[destutil.destmerge(repo)].node()
5212
5213
5213 if opts.get('preview'):
5214 if opts.get('preview'):
5214 # find nodes that are ancestors of p2 but not of p1
5215 # find nodes that are ancestors of p2 but not of p1
5215 p1 = repo.lookup('.')
5216 p1 = repo.lookup('.')
5216 p2 = repo.lookup(node)
5217 p2 = repo.lookup(node)
5217 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
5218 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
5218
5219
5219 displayer = cmdutil.show_changeset(ui, repo, opts)
5220 displayer = cmdutil.show_changeset(ui, repo, opts)
5220 for node in nodes:
5221 for node in nodes:
5221 displayer.show(repo[node])
5222 displayer.show(repo[node])
5222 displayer.close()
5223 displayer.close()
5223 return 0
5224 return 0
5224
5225
5225 try:
5226 try:
5226 # ui.forcemerge is an internal variable, do not document
5227 # ui.forcemerge is an internal variable, do not document
5227 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
5228 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
5228 force = opts.get('force')
5229 force = opts.get('force')
5229 labels = ['working copy', 'merge rev']
5230 labels = ['working copy', 'merge rev']
5230 return hg.merge(repo, node, force=force, mergeforce=force,
5231 return hg.merge(repo, node, force=force, mergeforce=force,
5231 labels=labels)
5232 labels=labels)
5232 finally:
5233 finally:
5233 ui.setconfig('ui', 'forcemerge', '', 'merge')
5234 ui.setconfig('ui', 'forcemerge', '', 'merge')
5234
5235
5235 @command('outgoing|out',
5236 @command('outgoing|out',
5236 [('f', 'force', None, _('run even when the destination is unrelated')),
5237 [('f', 'force', None, _('run even when the destination is unrelated')),
5237 ('r', 'rev', [],
5238 ('r', 'rev', [],
5238 _('a changeset intended to be included in the destination'), _('REV')),
5239 _('a changeset intended to be included in the destination'), _('REV')),
5239 ('n', 'newest-first', None, _('show newest record first')),
5240 ('n', 'newest-first', None, _('show newest record first')),
5240 ('B', 'bookmarks', False, _('compare bookmarks')),
5241 ('B', 'bookmarks', False, _('compare bookmarks')),
5241 ('b', 'branch', [], _('a specific branch you would like to push'),
5242 ('b', 'branch', [], _('a specific branch you would like to push'),
5242 _('BRANCH')),
5243 _('BRANCH')),
5243 ] + logopts + remoteopts + subrepoopts,
5244 ] + logopts + remoteopts + subrepoopts,
5244 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
5245 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
5245 def outgoing(ui, repo, dest=None, **opts):
5246 def outgoing(ui, repo, dest=None, **opts):
5246 """show changesets not found in the destination
5247 """show changesets not found in the destination
5247
5248
5248 Show changesets not found in the specified destination repository
5249 Show changesets not found in the specified destination repository
5249 or the default push location. These are the changesets that would
5250 or the default push location. These are the changesets that would
5250 be pushed if a push was requested.
5251 be pushed if a push was requested.
5251
5252
5252 See pull for details of valid destination formats.
5253 See pull for details of valid destination formats.
5253
5254
5254 .. container:: verbose
5255 .. container:: verbose
5255
5256
5256 With -B/--bookmarks, the result of bookmark comparison between
5257 With -B/--bookmarks, the result of bookmark comparison between
5257 local and remote repositories is displayed. With -v/--verbose,
5258 local and remote repositories is displayed. With -v/--verbose,
5258 status is also displayed for each bookmark like below::
5259 status is also displayed for each bookmark like below::
5259
5260
5260 BM1 01234567890a added
5261 BM1 01234567890a added
5261 BM2 deleted
5262 BM2 deleted
5262 BM3 234567890abc advanced
5263 BM3 234567890abc advanced
5263 BM4 34567890abcd diverged
5264 BM4 34567890abcd diverged
5264 BM5 4567890abcde changed
5265 BM5 4567890abcde changed
5265
5266
5266 The action taken when pushing depends on the
5267 The action taken when pushing depends on the
5267 status of each bookmark:
5268 status of each bookmark:
5268
5269
5269 :``added``: push with ``-B`` will create it
5270 :``added``: push with ``-B`` will create it
5270 :``deleted``: push with ``-B`` will delete it
5271 :``deleted``: push with ``-B`` will delete it
5271 :``advanced``: push will update it
5272 :``advanced``: push will update it
5272 :``diverged``: push with ``-B`` will update it
5273 :``diverged``: push with ``-B`` will update it
5273 :``changed``: push with ``-B`` will update it
5274 :``changed``: push with ``-B`` will update it
5274
5275
5275 From the point of view of pushing behavior, bookmarks
5276 From the point of view of pushing behavior, bookmarks
5276 existing only in the remote repository are treated as
5277 existing only in the remote repository are treated as
5277 ``deleted``, even if it is in fact added remotely.
5278 ``deleted``, even if it is in fact added remotely.
5278
5279
5279 Returns 0 if there are outgoing changes, 1 otherwise.
5280 Returns 0 if there are outgoing changes, 1 otherwise.
5280 """
5281 """
5281 if opts.get('graph'):
5282 if opts.get('graph'):
5282 cmdutil.checkunsupportedgraphflags([], opts)
5283 cmdutil.checkunsupportedgraphflags([], opts)
5283 o, other = hg._outgoing(ui, repo, dest, opts)
5284 o, other = hg._outgoing(ui, repo, dest, opts)
5284 if not o:
5285 if not o:
5285 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5286 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5286 return
5287 return
5287
5288
5288 revdag = cmdutil.graphrevs(repo, o, opts)
5289 revdag = cmdutil.graphrevs(repo, o, opts)
5289 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
5290 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
5290 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
5291 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
5291 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5292 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5292 return 0
5293 return 0
5293
5294
5294 if opts.get('bookmarks'):
5295 if opts.get('bookmarks'):
5295 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5296 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5296 dest, branches = hg.parseurl(dest, opts.get('branch'))
5297 dest, branches = hg.parseurl(dest, opts.get('branch'))
5297 other = hg.peer(repo, opts, dest)
5298 other = hg.peer(repo, opts, dest)
5298 if 'bookmarks' not in other.listkeys('namespaces'):
5299 if 'bookmarks' not in other.listkeys('namespaces'):
5299 ui.warn(_("remote doesn't support bookmarks\n"))
5300 ui.warn(_("remote doesn't support bookmarks\n"))
5300 return 0
5301 return 0
5301 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
5302 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
5302 return bookmarks.outgoing(ui, repo, other)
5303 return bookmarks.outgoing(ui, repo, other)
5303
5304
5304 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
5305 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
5305 try:
5306 try:
5306 return hg.outgoing(ui, repo, dest, opts)
5307 return hg.outgoing(ui, repo, dest, opts)
5307 finally:
5308 finally:
5308 del repo._subtoppath
5309 del repo._subtoppath
5309
5310
5310 @command('parents',
5311 @command('parents',
5311 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
5312 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
5312 ] + templateopts,
5313 ] + templateopts,
5313 _('[-r REV] [FILE]'),
5314 _('[-r REV] [FILE]'),
5314 inferrepo=True)
5315 inferrepo=True)
5315 def parents(ui, repo, file_=None, **opts):
5316 def parents(ui, repo, file_=None, **opts):
5316 """show the parents of the working directory or revision (DEPRECATED)
5317 """show the parents of the working directory or revision (DEPRECATED)
5317
5318
5318 Print the working directory's parent revisions. If a revision is
5319 Print the working directory's parent revisions. If a revision is
5319 given via -r/--rev, the parent of that revision will be printed.
5320 given via -r/--rev, the parent of that revision will be printed.
5320 If a file argument is given, the revision in which the file was
5321 If a file argument is given, the revision in which the file was
5321 last changed (before the working directory revision or the
5322 last changed (before the working directory revision or the
5322 argument to --rev if given) is printed.
5323 argument to --rev if given) is printed.
5323
5324
5324 This command is equivalent to::
5325 This command is equivalent to::
5325
5326
5326 hg log -r "p1()+p2()" or
5327 hg log -r "p1()+p2()" or
5327 hg log -r "p1(REV)+p2(REV)" or
5328 hg log -r "p1(REV)+p2(REV)" or
5328 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5329 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5329 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5330 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5330
5331
5331 See :hg:`summary` and :hg:`help revsets` for related information.
5332 See :hg:`summary` and :hg:`help revsets` for related information.
5332
5333
5333 Returns 0 on success.
5334 Returns 0 on success.
5334 """
5335 """
5335
5336
5336 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
5337 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
5337
5338
5338 if file_:
5339 if file_:
5339 m = scmutil.match(ctx, (file_,), opts)
5340 m = scmutil.match(ctx, (file_,), opts)
5340 if m.anypats() or len(m.files()) != 1:
5341 if m.anypats() or len(m.files()) != 1:
5341 raise error.Abort(_('can only specify an explicit filename'))
5342 raise error.Abort(_('can only specify an explicit filename'))
5342 file_ = m.files()[0]
5343 file_ = m.files()[0]
5343 filenodes = []
5344 filenodes = []
5344 for cp in ctx.parents():
5345 for cp in ctx.parents():
5345 if not cp:
5346 if not cp:
5346 continue
5347 continue
5347 try:
5348 try:
5348 filenodes.append(cp.filenode(file_))
5349 filenodes.append(cp.filenode(file_))
5349 except error.LookupError:
5350 except error.LookupError:
5350 pass
5351 pass
5351 if not filenodes:
5352 if not filenodes:
5352 raise error.Abort(_("'%s' not found in manifest!") % file_)
5353 raise error.Abort(_("'%s' not found in manifest!") % file_)
5353 p = []
5354 p = []
5354 for fn in filenodes:
5355 for fn in filenodes:
5355 fctx = repo.filectx(file_, fileid=fn)
5356 fctx = repo.filectx(file_, fileid=fn)
5356 p.append(fctx.node())
5357 p.append(fctx.node())
5357 else:
5358 else:
5358 p = [cp.node() for cp in ctx.parents()]
5359 p = [cp.node() for cp in ctx.parents()]
5359
5360
5360 displayer = cmdutil.show_changeset(ui, repo, opts)
5361 displayer = cmdutil.show_changeset(ui, repo, opts)
5361 for n in p:
5362 for n in p:
5362 if n != nullid:
5363 if n != nullid:
5363 displayer.show(repo[n])
5364 displayer.show(repo[n])
5364 displayer.close()
5365 displayer.close()
5365
5366
5366 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
5367 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
5367 def paths(ui, repo, search=None, **opts):
5368 def paths(ui, repo, search=None, **opts):
5368 """show aliases for remote repositories
5369 """show aliases for remote repositories
5369
5370
5370 Show definition of symbolic path name NAME. If no name is given,
5371 Show definition of symbolic path name NAME. If no name is given,
5371 show definition of all available names.
5372 show definition of all available names.
5372
5373
5373 Option -q/--quiet suppresses all output when searching for NAME
5374 Option -q/--quiet suppresses all output when searching for NAME
5374 and shows only the path names when listing all definitions.
5375 and shows only the path names when listing all definitions.
5375
5376
5376 Path names are defined in the [paths] section of your
5377 Path names are defined in the [paths] section of your
5377 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5378 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5378 repository, ``.hg/hgrc`` is used, too.
5379 repository, ``.hg/hgrc`` is used, too.
5379
5380
5380 The path names ``default`` and ``default-push`` have a special
5381 The path names ``default`` and ``default-push`` have a special
5381 meaning. When performing a push or pull operation, they are used
5382 meaning. When performing a push or pull operation, they are used
5382 as fallbacks if no location is specified on the command-line.
5383 as fallbacks if no location is specified on the command-line.
5383 When ``default-push`` is set, it will be used for push and
5384 When ``default-push`` is set, it will be used for push and
5384 ``default`` will be used for pull; otherwise ``default`` is used
5385 ``default`` will be used for pull; otherwise ``default`` is used
5385 as the fallback for both. When cloning a repository, the clone
5386 as the fallback for both. When cloning a repository, the clone
5386 source is written as ``default`` in ``.hg/hgrc``.
5387 source is written as ``default`` in ``.hg/hgrc``.
5387
5388
5388 .. note::
5389 .. note::
5389
5390
5390 ``default`` and ``default-push`` apply to all inbound (e.g.
5391 ``default`` and ``default-push`` apply to all inbound (e.g.
5391 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5392 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5392 and :hg:`bundle`) operations.
5393 and :hg:`bundle`) operations.
5393
5394
5394 See :hg:`help urls` for more information.
5395 See :hg:`help urls` for more information.
5395
5396
5396 Returns 0 on success.
5397 Returns 0 on success.
5397 """
5398 """
5398 if search:
5399 if search:
5399 pathitems = [(name, path) for name, path in ui.paths.iteritems()
5400 pathitems = [(name, path) for name, path in ui.paths.iteritems()
5400 if name == search]
5401 if name == search]
5401 else:
5402 else:
5402 pathitems = sorted(ui.paths.iteritems())
5403 pathitems = sorted(ui.paths.iteritems())
5403
5404
5404 fm = ui.formatter('paths', opts)
5405 fm = ui.formatter('paths', opts)
5405 if fm.isplain():
5406 if fm.isplain():
5406 hidepassword = util.hidepassword
5407 hidepassword = util.hidepassword
5407 else:
5408 else:
5408 hidepassword = str
5409 hidepassword = str
5409 if ui.quiet:
5410 if ui.quiet:
5410 namefmt = '%s\n'
5411 namefmt = '%s\n'
5411 else:
5412 else:
5412 namefmt = '%s = '
5413 namefmt = '%s = '
5413 showsubopts = not search and not ui.quiet
5414 showsubopts = not search and not ui.quiet
5414
5415
5415 for name, path in pathitems:
5416 for name, path in pathitems:
5416 fm.startitem()
5417 fm.startitem()
5417 fm.condwrite(not search, 'name', namefmt, name)
5418 fm.condwrite(not search, 'name', namefmt, name)
5418 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
5419 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
5419 for subopt, value in sorted(path.suboptions.items()):
5420 for subopt, value in sorted(path.suboptions.items()):
5420 assert subopt not in ('name', 'url')
5421 assert subopt not in ('name', 'url')
5421 if showsubopts:
5422 if showsubopts:
5422 fm.plain('%s:%s = ' % (name, subopt))
5423 fm.plain('%s:%s = ' % (name, subopt))
5423 fm.condwrite(showsubopts, subopt, '%s\n', value)
5424 fm.condwrite(showsubopts, subopt, '%s\n', value)
5424
5425
5425 fm.end()
5426 fm.end()
5426
5427
5427 if search and not pathitems:
5428 if search and not pathitems:
5428 if not ui.quiet:
5429 if not ui.quiet:
5429 ui.warn(_("not found!\n"))
5430 ui.warn(_("not found!\n"))
5430 return 1
5431 return 1
5431 else:
5432 else:
5432 return 0
5433 return 0
5433
5434
5434 @command('phase',
5435 @command('phase',
5435 [('p', 'public', False, _('set changeset phase to public')),
5436 [('p', 'public', False, _('set changeset phase to public')),
5436 ('d', 'draft', False, _('set changeset phase to draft')),
5437 ('d', 'draft', False, _('set changeset phase to draft')),
5437 ('s', 'secret', False, _('set changeset phase to secret')),
5438 ('s', 'secret', False, _('set changeset phase to secret')),
5438 ('f', 'force', False, _('allow to move boundary backward')),
5439 ('f', 'force', False, _('allow to move boundary backward')),
5439 ('r', 'rev', [], _('target revision'), _('REV')),
5440 ('r', 'rev', [], _('target revision'), _('REV')),
5440 ],
5441 ],
5441 _('[-p|-d|-s] [-f] [-r] [REV...]'))
5442 _('[-p|-d|-s] [-f] [-r] [REV...]'))
5442 def phase(ui, repo, *revs, **opts):
5443 def phase(ui, repo, *revs, **opts):
5443 """set or show the current phase name
5444 """set or show the current phase name
5444
5445
5445 With no argument, show the phase name of the current revision(s).
5446 With no argument, show the phase name of the current revision(s).
5446
5447
5447 With one of -p/--public, -d/--draft or -s/--secret, change the
5448 With one of -p/--public, -d/--draft or -s/--secret, change the
5448 phase value of the specified revisions.
5449 phase value of the specified revisions.
5449
5450
5450 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
5451 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
5451 lower phase to an higher phase. Phases are ordered as follows::
5452 lower phase to an higher phase. Phases are ordered as follows::
5452
5453
5453 public < draft < secret
5454 public < draft < secret
5454
5455
5455 Returns 0 on success, 1 if some phases could not be changed.
5456 Returns 0 on success, 1 if some phases could not be changed.
5456
5457
5457 (For more information about the phases concept, see :hg:`help phases`.)
5458 (For more information about the phases concept, see :hg:`help phases`.)
5458 """
5459 """
5459 # search for a unique phase argument
5460 # search for a unique phase argument
5460 targetphase = None
5461 targetphase = None
5461 for idx, name in enumerate(phases.phasenames):
5462 for idx, name in enumerate(phases.phasenames):
5462 if opts[name]:
5463 if opts[name]:
5463 if targetphase is not None:
5464 if targetphase is not None:
5464 raise error.Abort(_('only one phase can be specified'))
5465 raise error.Abort(_('only one phase can be specified'))
5465 targetphase = idx
5466 targetphase = idx
5466
5467
5467 # look for specified revision
5468 # look for specified revision
5468 revs = list(revs)
5469 revs = list(revs)
5469 revs.extend(opts['rev'])
5470 revs.extend(opts['rev'])
5470 if not revs:
5471 if not revs:
5471 # display both parents as the second parent phase can influence
5472 # display both parents as the second parent phase can influence
5472 # the phase of a merge commit
5473 # the phase of a merge commit
5473 revs = [c.rev() for c in repo[None].parents()]
5474 revs = [c.rev() for c in repo[None].parents()]
5474
5475
5475 revs = scmutil.revrange(repo, revs)
5476 revs = scmutil.revrange(repo, revs)
5476
5477
5477 lock = None
5478 lock = None
5478 ret = 0
5479 ret = 0
5479 if targetphase is None:
5480 if targetphase is None:
5480 # display
5481 # display
5481 for r in revs:
5482 for r in revs:
5482 ctx = repo[r]
5483 ctx = repo[r]
5483 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5484 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5484 else:
5485 else:
5485 tr = None
5486 tr = None
5486 lock = repo.lock()
5487 lock = repo.lock()
5487 try:
5488 try:
5488 tr = repo.transaction("phase")
5489 tr = repo.transaction("phase")
5489 # set phase
5490 # set phase
5490 if not revs:
5491 if not revs:
5491 raise error.Abort(_('empty revision set'))
5492 raise error.Abort(_('empty revision set'))
5492 nodes = [repo[r].node() for r in revs]
5493 nodes = [repo[r].node() for r in revs]
5493 # moving revision from public to draft may hide them
5494 # moving revision from public to draft may hide them
5494 # We have to check result on an unfiltered repository
5495 # We have to check result on an unfiltered repository
5495 unfi = repo.unfiltered()
5496 unfi = repo.unfiltered()
5496 getphase = unfi._phasecache.phase
5497 getphase = unfi._phasecache.phase
5497 olddata = [getphase(unfi, r) for r in unfi]
5498 olddata = [getphase(unfi, r) for r in unfi]
5498 phases.advanceboundary(repo, tr, targetphase, nodes)
5499 phases.advanceboundary(repo, tr, targetphase, nodes)
5499 if opts['force']:
5500 if opts['force']:
5500 phases.retractboundary(repo, tr, targetphase, nodes)
5501 phases.retractboundary(repo, tr, targetphase, nodes)
5501 tr.close()
5502 tr.close()
5502 finally:
5503 finally:
5503 if tr is not None:
5504 if tr is not None:
5504 tr.release()
5505 tr.release()
5505 lock.release()
5506 lock.release()
5506 getphase = unfi._phasecache.phase
5507 getphase = unfi._phasecache.phase
5507 newdata = [getphase(unfi, r) for r in unfi]
5508 newdata = [getphase(unfi, r) for r in unfi]
5508 changes = sum(newdata[r] != olddata[r] for r in unfi)
5509 changes = sum(newdata[r] != olddata[r] for r in unfi)
5509 cl = unfi.changelog
5510 cl = unfi.changelog
5510 rejected = [n for n in nodes
5511 rejected = [n for n in nodes
5511 if newdata[cl.rev(n)] < targetphase]
5512 if newdata[cl.rev(n)] < targetphase]
5512 if rejected:
5513 if rejected:
5513 ui.warn(_('cannot move %i changesets to a higher '
5514 ui.warn(_('cannot move %i changesets to a higher '
5514 'phase, use --force\n') % len(rejected))
5515 'phase, use --force\n') % len(rejected))
5515 ret = 1
5516 ret = 1
5516 if changes:
5517 if changes:
5517 msg = _('phase changed for %i changesets\n') % changes
5518 msg = _('phase changed for %i changesets\n') % changes
5518 if ret:
5519 if ret:
5519 ui.status(msg)
5520 ui.status(msg)
5520 else:
5521 else:
5521 ui.note(msg)
5522 ui.note(msg)
5522 else:
5523 else:
5523 ui.warn(_('no phases changed\n'))
5524 ui.warn(_('no phases changed\n'))
5524 return ret
5525 return ret
5525
5526
5526 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5527 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5527 """Run after a changegroup has been added via pull/unbundle
5528 """Run after a changegroup has been added via pull/unbundle
5528
5529
5529 This takes arguments below:
5530 This takes arguments below:
5530
5531
5531 :modheads: change of heads by pull/unbundle
5532 :modheads: change of heads by pull/unbundle
5532 :optupdate: updating working directory is needed or not
5533 :optupdate: updating working directory is needed or not
5533 :checkout: update destination revision (or None to default destination)
5534 :checkout: update destination revision (or None to default destination)
5534 :brev: a name, which might be a bookmark to be activated after updating
5535 :brev: a name, which might be a bookmark to be activated after updating
5535 """
5536 """
5536 if modheads == 0:
5537 if modheads == 0:
5537 return
5538 return
5538 if optupdate:
5539 if optupdate:
5539 try:
5540 try:
5540 return hg.updatetotally(ui, repo, checkout, brev)
5541 return hg.updatetotally(ui, repo, checkout, brev)
5541 except error.UpdateAbort as inst:
5542 except error.UpdateAbort as inst:
5542 msg = _("not updating: %s") % str(inst)
5543 msg = _("not updating: %s") % str(inst)
5543 hint = inst.hint
5544 hint = inst.hint
5544 raise error.UpdateAbort(msg, hint=hint)
5545 raise error.UpdateAbort(msg, hint=hint)
5545 if modheads > 1:
5546 if modheads > 1:
5546 currentbranchheads = len(repo.branchheads())
5547 currentbranchheads = len(repo.branchheads())
5547 if currentbranchheads == modheads:
5548 if currentbranchheads == modheads:
5548 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
5549 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
5549 elif currentbranchheads > 1:
5550 elif currentbranchheads > 1:
5550 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
5551 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
5551 "merge)\n"))
5552 "merge)\n"))
5552 else:
5553 else:
5553 ui.status(_("(run 'hg heads' to see heads)\n"))
5554 ui.status(_("(run 'hg heads' to see heads)\n"))
5554 else:
5555 else:
5555 ui.status(_("(run 'hg update' to get a working copy)\n"))
5556 ui.status(_("(run 'hg update' to get a working copy)\n"))
5556
5557
5557 @command('^pull',
5558 @command('^pull',
5558 [('u', 'update', None,
5559 [('u', 'update', None,
5559 _('update to new branch head if changesets were pulled')),
5560 _('update to new branch head if changesets were pulled')),
5560 ('f', 'force', None, _('run even when remote repository is unrelated')),
5561 ('f', 'force', None, _('run even when remote repository is unrelated')),
5561 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
5562 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
5562 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
5563 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
5563 ('b', 'branch', [], _('a specific branch you would like to pull'),
5564 ('b', 'branch', [], _('a specific branch you would like to pull'),
5564 _('BRANCH')),
5565 _('BRANCH')),
5565 ] + remoteopts,
5566 ] + remoteopts,
5566 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
5567 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
5567 def pull(ui, repo, source="default", **opts):
5568 def pull(ui, repo, source="default", **opts):
5568 """pull changes from the specified source
5569 """pull changes from the specified source
5569
5570
5570 Pull changes from a remote repository to a local one.
5571 Pull changes from a remote repository to a local one.
5571
5572
5572 This finds all changes from the repository at the specified path
5573 This finds all changes from the repository at the specified path
5573 or URL and adds them to a local repository (the current one unless
5574 or URL and adds them to a local repository (the current one unless
5574 -R is specified). By default, this does not update the copy of the
5575 -R is specified). By default, this does not update the copy of the
5575 project in the working directory.
5576 project in the working directory.
5576
5577
5577 Use :hg:`incoming` if you want to see what would have been added
5578 Use :hg:`incoming` if you want to see what would have been added
5578 by a pull at the time you issued this command. If you then decide
5579 by a pull at the time you issued this command. If you then decide
5579 to add those changes to the repository, you should use :hg:`pull
5580 to add those changes to the repository, you should use :hg:`pull
5580 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5581 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5581
5582
5582 If SOURCE is omitted, the 'default' path will be used.
5583 If SOURCE is omitted, the 'default' path will be used.
5583 See :hg:`help urls` for more information.
5584 See :hg:`help urls` for more information.
5584
5585
5585 Specifying bookmark as ``.`` is equivalent to specifying the active
5586 Specifying bookmark as ``.`` is equivalent to specifying the active
5586 bookmark's name.
5587 bookmark's name.
5587
5588
5588 Returns 0 on success, 1 if an update had unresolved files.
5589 Returns 0 on success, 1 if an update had unresolved files.
5589 """
5590 """
5590 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5591 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5591 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5592 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5592 other = hg.peer(repo, opts, source)
5593 other = hg.peer(repo, opts, source)
5593 try:
5594 try:
5594 revs, checkout = hg.addbranchrevs(repo, other, branches,
5595 revs, checkout = hg.addbranchrevs(repo, other, branches,
5595 opts.get('rev'))
5596 opts.get('rev'))
5596
5597
5597
5598
5598 pullopargs = {}
5599 pullopargs = {}
5599 if opts.get('bookmark'):
5600 if opts.get('bookmark'):
5600 if not revs:
5601 if not revs:
5601 revs = []
5602 revs = []
5602 # The list of bookmark used here is not the one used to actually
5603 # The list of bookmark used here is not the one used to actually
5603 # update the bookmark name. This can result in the revision pulled
5604 # update the bookmark name. This can result in the revision pulled
5604 # not ending up with the name of the bookmark because of a race
5605 # not ending up with the name of the bookmark because of a race
5605 # condition on the server. (See issue 4689 for details)
5606 # condition on the server. (See issue 4689 for details)
5606 remotebookmarks = other.listkeys('bookmarks')
5607 remotebookmarks = other.listkeys('bookmarks')
5607 pullopargs['remotebookmarks'] = remotebookmarks
5608 pullopargs['remotebookmarks'] = remotebookmarks
5608 for b in opts['bookmark']:
5609 for b in opts['bookmark']:
5609 b = repo._bookmarks.expandname(b)
5610 b = repo._bookmarks.expandname(b)
5610 if b not in remotebookmarks:
5611 if b not in remotebookmarks:
5611 raise error.Abort(_('remote bookmark %s not found!') % b)
5612 raise error.Abort(_('remote bookmark %s not found!') % b)
5612 revs.append(remotebookmarks[b])
5613 revs.append(remotebookmarks[b])
5613
5614
5614 if revs:
5615 if revs:
5615 try:
5616 try:
5616 # When 'rev' is a bookmark name, we cannot guarantee that it
5617 # When 'rev' is a bookmark name, we cannot guarantee that it
5617 # will be updated with that name because of a race condition
5618 # will be updated with that name because of a race condition
5618 # server side. (See issue 4689 for details)
5619 # server side. (See issue 4689 for details)
5619 oldrevs = revs
5620 oldrevs = revs
5620 revs = [] # actually, nodes
5621 revs = [] # actually, nodes
5621 for r in oldrevs:
5622 for r in oldrevs:
5622 node = other.lookup(r)
5623 node = other.lookup(r)
5623 revs.append(node)
5624 revs.append(node)
5624 if r == checkout:
5625 if r == checkout:
5625 checkout = node
5626 checkout = node
5626 except error.CapabilityError:
5627 except error.CapabilityError:
5627 err = _("other repository doesn't support revision lookup, "
5628 err = _("other repository doesn't support revision lookup, "
5628 "so a rev cannot be specified.")
5629 "so a rev cannot be specified.")
5629 raise error.Abort(err)
5630 raise error.Abort(err)
5630
5631
5631 pullopargs.update(opts.get('opargs', {}))
5632 pullopargs.update(opts.get('opargs', {}))
5632 modheads = exchange.pull(repo, other, heads=revs,
5633 modheads = exchange.pull(repo, other, heads=revs,
5633 force=opts.get('force'),
5634 force=opts.get('force'),
5634 bookmarks=opts.get('bookmark', ()),
5635 bookmarks=opts.get('bookmark', ()),
5635 opargs=pullopargs).cgresult
5636 opargs=pullopargs).cgresult
5636
5637
5637 # brev is a name, which might be a bookmark to be activated at
5638 # brev is a name, which might be a bookmark to be activated at
5638 # the end of the update. In other words, it is an explicit
5639 # the end of the update. In other words, it is an explicit
5639 # destination of the update
5640 # destination of the update
5640 brev = None
5641 brev = None
5641
5642
5642 if checkout:
5643 if checkout:
5643 checkout = str(repo.changelog.rev(checkout))
5644 checkout = str(repo.changelog.rev(checkout))
5644
5645
5645 # order below depends on implementation of
5646 # order below depends on implementation of
5646 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5647 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5647 # because 'checkout' is determined without it.
5648 # because 'checkout' is determined without it.
5648 if opts.get('rev'):
5649 if opts.get('rev'):
5649 brev = opts['rev'][0]
5650 brev = opts['rev'][0]
5650 elif opts.get('branch'):
5651 elif opts.get('branch'):
5651 brev = opts['branch'][0]
5652 brev = opts['branch'][0]
5652 else:
5653 else:
5653 brev = branches[0]
5654 brev = branches[0]
5654 repo._subtoppath = source
5655 repo._subtoppath = source
5655 try:
5656 try:
5656 ret = postincoming(ui, repo, modheads, opts.get('update'),
5657 ret = postincoming(ui, repo, modheads, opts.get('update'),
5657 checkout, brev)
5658 checkout, brev)
5658
5659
5659 finally:
5660 finally:
5660 del repo._subtoppath
5661 del repo._subtoppath
5661
5662
5662 finally:
5663 finally:
5663 other.close()
5664 other.close()
5664 return ret
5665 return ret
5665
5666
5666 @command('^push',
5667 @command('^push',
5667 [('f', 'force', None, _('force push')),
5668 [('f', 'force', None, _('force push')),
5668 ('r', 'rev', [],
5669 ('r', 'rev', [],
5669 _('a changeset intended to be included in the destination'),
5670 _('a changeset intended to be included in the destination'),
5670 _('REV')),
5671 _('REV')),
5671 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5672 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5672 ('b', 'branch', [],
5673 ('b', 'branch', [],
5673 _('a specific branch you would like to push'), _('BRANCH')),
5674 _('a specific branch you would like to push'), _('BRANCH')),
5674 ('', 'new-branch', False, _('allow pushing a new branch')),
5675 ('', 'new-branch', False, _('allow pushing a new branch')),
5675 ] + remoteopts,
5676 ] + remoteopts,
5676 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5677 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5677 def push(ui, repo, dest=None, **opts):
5678 def push(ui, repo, dest=None, **opts):
5678 """push changes to the specified destination
5679 """push changes to the specified destination
5679
5680
5680 Push changesets from the local repository to the specified
5681 Push changesets from the local repository to the specified
5681 destination.
5682 destination.
5682
5683
5683 This operation is symmetrical to pull: it is identical to a pull
5684 This operation is symmetrical to pull: it is identical to a pull
5684 in the destination repository from the current one.
5685 in the destination repository from the current one.
5685
5686
5686 By default, push will not allow creation of new heads at the
5687 By default, push will not allow creation of new heads at the
5687 destination, since multiple heads would make it unclear which head
5688 destination, since multiple heads would make it unclear which head
5688 to use. In this situation, it is recommended to pull and merge
5689 to use. In this situation, it is recommended to pull and merge
5689 before pushing.
5690 before pushing.
5690
5691
5691 Use --new-branch if you want to allow push to create a new named
5692 Use --new-branch if you want to allow push to create a new named
5692 branch that is not present at the destination. This allows you to
5693 branch that is not present at the destination. This allows you to
5693 only create a new branch without forcing other changes.
5694 only create a new branch without forcing other changes.
5694
5695
5695 .. note::
5696 .. note::
5696
5697
5697 Extra care should be taken with the -f/--force option,
5698 Extra care should be taken with the -f/--force option,
5698 which will push all new heads on all branches, an action which will
5699 which will push all new heads on all branches, an action which will
5699 almost always cause confusion for collaborators.
5700 almost always cause confusion for collaborators.
5700
5701
5701 If -r/--rev is used, the specified revision and all its ancestors
5702 If -r/--rev is used, the specified revision and all its ancestors
5702 will be pushed to the remote repository.
5703 will be pushed to the remote repository.
5703
5704
5704 If -B/--bookmark is used, the specified bookmarked revision, its
5705 If -B/--bookmark is used, the specified bookmarked revision, its
5705 ancestors, and the bookmark will be pushed to the remote
5706 ancestors, and the bookmark will be pushed to the remote
5706 repository. Specifying ``.`` is equivalent to specifying the active
5707 repository. Specifying ``.`` is equivalent to specifying the active
5707 bookmark's name.
5708 bookmark's name.
5708
5709
5709 Please see :hg:`help urls` for important details about ``ssh://``
5710 Please see :hg:`help urls` for important details about ``ssh://``
5710 URLs. If DESTINATION is omitted, a default path will be used.
5711 URLs. If DESTINATION is omitted, a default path will be used.
5711
5712
5712 Returns 0 if push was successful, 1 if nothing to push.
5713 Returns 0 if push was successful, 1 if nothing to push.
5713 """
5714 """
5714
5715
5715 if opts.get('bookmark'):
5716 if opts.get('bookmark'):
5716 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5717 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5717 for b in opts['bookmark']:
5718 for b in opts['bookmark']:
5718 # translate -B options to -r so changesets get pushed
5719 # translate -B options to -r so changesets get pushed
5719 b = repo._bookmarks.expandname(b)
5720 b = repo._bookmarks.expandname(b)
5720 if b in repo._bookmarks:
5721 if b in repo._bookmarks:
5721 opts.setdefault('rev', []).append(b)
5722 opts.setdefault('rev', []).append(b)
5722 else:
5723 else:
5723 # if we try to push a deleted bookmark, translate it to null
5724 # if we try to push a deleted bookmark, translate it to null
5724 # this lets simultaneous -r, -b options continue working
5725 # this lets simultaneous -r, -b options continue working
5725 opts.setdefault('rev', []).append("null")
5726 opts.setdefault('rev', []).append("null")
5726
5727
5727 path = ui.paths.getpath(dest, default=('default-push', 'default'))
5728 path = ui.paths.getpath(dest, default=('default-push', 'default'))
5728 if not path:
5729 if not path:
5729 raise error.Abort(_('default repository not configured!'),
5730 raise error.Abort(_('default repository not configured!'),
5730 hint=_("see 'hg help config.paths'"))
5731 hint=_("see 'hg help config.paths'"))
5731 dest = path.pushloc or path.loc
5732 dest = path.pushloc or path.loc
5732 branches = (path.branch, opts.get('branch') or [])
5733 branches = (path.branch, opts.get('branch') or [])
5733 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5734 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5734 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5735 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5735 other = hg.peer(repo, opts, dest)
5736 other = hg.peer(repo, opts, dest)
5736
5737
5737 if revs:
5738 if revs:
5738 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5739 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5739 if not revs:
5740 if not revs:
5740 raise error.Abort(_("specified revisions evaluate to an empty set"),
5741 raise error.Abort(_("specified revisions evaluate to an empty set"),
5741 hint=_("use different revision arguments"))
5742 hint=_("use different revision arguments"))
5742 elif path.pushrev:
5743 elif path.pushrev:
5743 # It doesn't make any sense to specify ancestor revisions. So limit
5744 # It doesn't make any sense to specify ancestor revisions. So limit
5744 # to DAG heads to make discovery simpler.
5745 # to DAG heads to make discovery simpler.
5745 expr = revset.formatspec('heads(%r)', path.pushrev)
5746 expr = revset.formatspec('heads(%r)', path.pushrev)
5746 revs = scmutil.revrange(repo, [expr])
5747 revs = scmutil.revrange(repo, [expr])
5747 revs = [repo[rev].node() for rev in revs]
5748 revs = [repo[rev].node() for rev in revs]
5748 if not revs:
5749 if not revs:
5749 raise error.Abort(_('default push revset for path evaluates to an '
5750 raise error.Abort(_('default push revset for path evaluates to an '
5750 'empty set'))
5751 'empty set'))
5751
5752
5752 repo._subtoppath = dest
5753 repo._subtoppath = dest
5753 try:
5754 try:
5754 # push subrepos depth-first for coherent ordering
5755 # push subrepos depth-first for coherent ordering
5755 c = repo['']
5756 c = repo['']
5756 subs = c.substate # only repos that are committed
5757 subs = c.substate # only repos that are committed
5757 for s in sorted(subs):
5758 for s in sorted(subs):
5758 result = c.sub(s).push(opts)
5759 result = c.sub(s).push(opts)
5759 if result == 0:
5760 if result == 0:
5760 return not result
5761 return not result
5761 finally:
5762 finally:
5762 del repo._subtoppath
5763 del repo._subtoppath
5763 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5764 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5764 newbranch=opts.get('new_branch'),
5765 newbranch=opts.get('new_branch'),
5765 bookmarks=opts.get('bookmark', ()),
5766 bookmarks=opts.get('bookmark', ()),
5766 opargs=opts.get('opargs'))
5767 opargs=opts.get('opargs'))
5767
5768
5768 result = not pushop.cgresult
5769 result = not pushop.cgresult
5769
5770
5770 if pushop.bkresult is not None:
5771 if pushop.bkresult is not None:
5771 if pushop.bkresult == 2:
5772 if pushop.bkresult == 2:
5772 result = 2
5773 result = 2
5773 elif not result and pushop.bkresult:
5774 elif not result and pushop.bkresult:
5774 result = 2
5775 result = 2
5775
5776
5776 return result
5777 return result
5777
5778
5778 @command('recover', [])
5779 @command('recover', [])
5779 def recover(ui, repo):
5780 def recover(ui, repo):
5780 """roll back an interrupted transaction
5781 """roll back an interrupted transaction
5781
5782
5782 Recover from an interrupted commit or pull.
5783 Recover from an interrupted commit or pull.
5783
5784
5784 This command tries to fix the repository status after an
5785 This command tries to fix the repository status after an
5785 interrupted operation. It should only be necessary when Mercurial
5786 interrupted operation. It should only be necessary when Mercurial
5786 suggests it.
5787 suggests it.
5787
5788
5788 Returns 0 if successful, 1 if nothing to recover or verify fails.
5789 Returns 0 if successful, 1 if nothing to recover or verify fails.
5789 """
5790 """
5790 if repo.recover():
5791 if repo.recover():
5791 return hg.verify(repo)
5792 return hg.verify(repo)
5792 return 1
5793 return 1
5793
5794
5794 @command('^remove|rm',
5795 @command('^remove|rm',
5795 [('A', 'after', None, _('record delete for missing files')),
5796 [('A', 'after', None, _('record delete for missing files')),
5796 ('f', 'force', None,
5797 ('f', 'force', None,
5797 _('forget added files, delete modified files')),
5798 _('forget added files, delete modified files')),
5798 ] + subrepoopts + walkopts,
5799 ] + subrepoopts + walkopts,
5799 _('[OPTION]... FILE...'),
5800 _('[OPTION]... FILE...'),
5800 inferrepo=True)
5801 inferrepo=True)
5801 def remove(ui, repo, *pats, **opts):
5802 def remove(ui, repo, *pats, **opts):
5802 """remove the specified files on the next commit
5803 """remove the specified files on the next commit
5803
5804
5804 Schedule the indicated files for removal from the current branch.
5805 Schedule the indicated files for removal from the current branch.
5805
5806
5806 This command schedules the files to be removed at the next commit.
5807 This command schedules the files to be removed at the next commit.
5807 To undo a remove before that, see :hg:`revert`. To undo added
5808 To undo a remove before that, see :hg:`revert`. To undo added
5808 files, see :hg:`forget`.
5809 files, see :hg:`forget`.
5809
5810
5810 .. container:: verbose
5811 .. container:: verbose
5811
5812
5812 -A/--after can be used to remove only files that have already
5813 -A/--after can be used to remove only files that have already
5813 been deleted, -f/--force can be used to force deletion, and -Af
5814 been deleted, -f/--force can be used to force deletion, and -Af
5814 can be used to remove files from the next revision without
5815 can be used to remove files from the next revision without
5815 deleting them from the working directory.
5816 deleting them from the working directory.
5816
5817
5817 The following table details the behavior of remove for different
5818 The following table details the behavior of remove for different
5818 file states (columns) and option combinations (rows). The file
5819 file states (columns) and option combinations (rows). The file
5819 states are Added [A], Clean [C], Modified [M] and Missing [!]
5820 states are Added [A], Clean [C], Modified [M] and Missing [!]
5820 (as reported by :hg:`status`). The actions are Warn, Remove
5821 (as reported by :hg:`status`). The actions are Warn, Remove
5821 (from branch) and Delete (from disk):
5822 (from branch) and Delete (from disk):
5822
5823
5823 ========= == == == ==
5824 ========= == == == ==
5824 opt/state A C M !
5825 opt/state A C M !
5825 ========= == == == ==
5826 ========= == == == ==
5826 none W RD W R
5827 none W RD W R
5827 -f R RD RD R
5828 -f R RD RD R
5828 -A W W W R
5829 -A W W W R
5829 -Af R R R R
5830 -Af R R R R
5830 ========= == == == ==
5831 ========= == == == ==
5831
5832
5832 .. note::
5833 .. note::
5833
5834
5834 :hg:`remove` never deletes files in Added [A] state from the
5835 :hg:`remove` never deletes files in Added [A] state from the
5835 working directory, not even if ``--force`` is specified.
5836 working directory, not even if ``--force`` is specified.
5836
5837
5837 Returns 0 on success, 1 if any warnings encountered.
5838 Returns 0 on success, 1 if any warnings encountered.
5838 """
5839 """
5839
5840
5840 after, force = opts.get('after'), opts.get('force')
5841 after, force = opts.get('after'), opts.get('force')
5841 if not pats and not after:
5842 if not pats and not after:
5842 raise error.Abort(_('no files specified'))
5843 raise error.Abort(_('no files specified'))
5843
5844
5844 m = scmutil.match(repo[None], pats, opts)
5845 m = scmutil.match(repo[None], pats, opts)
5845 subrepos = opts.get('subrepos')
5846 subrepos = opts.get('subrepos')
5846 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5847 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5847
5848
5848 @command('rename|move|mv',
5849 @command('rename|move|mv',
5849 [('A', 'after', None, _('record a rename that has already occurred')),
5850 [('A', 'after', None, _('record a rename that has already occurred')),
5850 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5851 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5851 ] + walkopts + dryrunopts,
5852 ] + walkopts + dryrunopts,
5852 _('[OPTION]... SOURCE... DEST'))
5853 _('[OPTION]... SOURCE... DEST'))
5853 def rename(ui, repo, *pats, **opts):
5854 def rename(ui, repo, *pats, **opts):
5854 """rename files; equivalent of copy + remove
5855 """rename files; equivalent of copy + remove
5855
5856
5856 Mark dest as copies of sources; mark sources for deletion. If dest
5857 Mark dest as copies of sources; mark sources for deletion. If dest
5857 is a directory, copies are put in that directory. If dest is a
5858 is a directory, copies are put in that directory. If dest is a
5858 file, there can only be one source.
5859 file, there can only be one source.
5859
5860
5860 By default, this command copies the contents of files as they
5861 By default, this command copies the contents of files as they
5861 exist in the working directory. If invoked with -A/--after, the
5862 exist in the working directory. If invoked with -A/--after, the
5862 operation is recorded, but no copying is performed.
5863 operation is recorded, but no copying is performed.
5863
5864
5864 This command takes effect at the next commit. To undo a rename
5865 This command takes effect at the next commit. To undo a rename
5865 before that, see :hg:`revert`.
5866 before that, see :hg:`revert`.
5866
5867
5867 Returns 0 on success, 1 if errors are encountered.
5868 Returns 0 on success, 1 if errors are encountered.
5868 """
5869 """
5869 with repo.wlock(False):
5870 with repo.wlock(False):
5870 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5871 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5871
5872
5872 @command('resolve',
5873 @command('resolve',
5873 [('a', 'all', None, _('select all unresolved files')),
5874 [('a', 'all', None, _('select all unresolved files')),
5874 ('l', 'list', None, _('list state of files needing merge')),
5875 ('l', 'list', None, _('list state of files needing merge')),
5875 ('m', 'mark', None, _('mark files as resolved')),
5876 ('m', 'mark', None, _('mark files as resolved')),
5876 ('u', 'unmark', None, _('mark files as unresolved')),
5877 ('u', 'unmark', None, _('mark files as unresolved')),
5877 ('n', 'no-status', None, _('hide status prefix'))]
5878 ('n', 'no-status', None, _('hide status prefix'))]
5878 + mergetoolopts + walkopts + formatteropts,
5879 + mergetoolopts + walkopts + formatteropts,
5879 _('[OPTION]... [FILE]...'),
5880 _('[OPTION]... [FILE]...'),
5880 inferrepo=True)
5881 inferrepo=True)
5881 def resolve(ui, repo, *pats, **opts):
5882 def resolve(ui, repo, *pats, **opts):
5882 """redo merges or set/view the merge status of files
5883 """redo merges or set/view the merge status of files
5883
5884
5884 Merges with unresolved conflicts are often the result of
5885 Merges with unresolved conflicts are often the result of
5885 non-interactive merging using the ``internal:merge`` configuration
5886 non-interactive merging using the ``internal:merge`` configuration
5886 setting, or a command-line merge tool like ``diff3``. The resolve
5887 setting, or a command-line merge tool like ``diff3``. The resolve
5887 command is used to manage the files involved in a merge, after
5888 command is used to manage the files involved in a merge, after
5888 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5889 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5889 working directory must have two parents). See :hg:`help
5890 working directory must have two parents). See :hg:`help
5890 merge-tools` for information on configuring merge tools.
5891 merge-tools` for information on configuring merge tools.
5891
5892
5892 The resolve command can be used in the following ways:
5893 The resolve command can be used in the following ways:
5893
5894
5894 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5895 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5895 files, discarding any previous merge attempts. Re-merging is not
5896 files, discarding any previous merge attempts. Re-merging is not
5896 performed for files already marked as resolved. Use ``--all/-a``
5897 performed for files already marked as resolved. Use ``--all/-a``
5897 to select all unresolved files. ``--tool`` can be used to specify
5898 to select all unresolved files. ``--tool`` can be used to specify
5898 the merge tool used for the given files. It overrides the HGMERGE
5899 the merge tool used for the given files. It overrides the HGMERGE
5899 environment variable and your configuration files. Previous file
5900 environment variable and your configuration files. Previous file
5900 contents are saved with a ``.orig`` suffix.
5901 contents are saved with a ``.orig`` suffix.
5901
5902
5902 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5903 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5903 (e.g. after having manually fixed-up the files). The default is
5904 (e.g. after having manually fixed-up the files). The default is
5904 to mark all unresolved files.
5905 to mark all unresolved files.
5905
5906
5906 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5907 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5907 default is to mark all resolved files.
5908 default is to mark all resolved files.
5908
5909
5909 - :hg:`resolve -l`: list files which had or still have conflicts.
5910 - :hg:`resolve -l`: list files which had or still have conflicts.
5910 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5911 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5911
5912
5912 .. note::
5913 .. note::
5913
5914
5914 Mercurial will not let you commit files with unresolved merge
5915 Mercurial will not let you commit files with unresolved merge
5915 conflicts. You must use :hg:`resolve -m ...` before you can
5916 conflicts. You must use :hg:`resolve -m ...` before you can
5916 commit after a conflicting merge.
5917 commit after a conflicting merge.
5917
5918
5918 Returns 0 on success, 1 if any files fail a resolve attempt.
5919 Returns 0 on success, 1 if any files fail a resolve attempt.
5919 """
5920 """
5920
5921
5921 flaglist = 'all mark unmark list no_status'.split()
5922 flaglist = 'all mark unmark list no_status'.split()
5922 all, mark, unmark, show, nostatus = \
5923 all, mark, unmark, show, nostatus = \
5923 [opts.get(o) for o in flaglist]
5924 [opts.get(o) for o in flaglist]
5924
5925
5925 if (show and (mark or unmark)) or (mark and unmark):
5926 if (show and (mark or unmark)) or (mark and unmark):
5926 raise error.Abort(_("too many options specified"))
5927 raise error.Abort(_("too many options specified"))
5927 if pats and all:
5928 if pats and all:
5928 raise error.Abort(_("can't specify --all and patterns"))
5929 raise error.Abort(_("can't specify --all and patterns"))
5929 if not (all or pats or show or mark or unmark):
5930 if not (all or pats or show or mark or unmark):
5930 raise error.Abort(_('no files or directories specified'),
5931 raise error.Abort(_('no files or directories specified'),
5931 hint=('use --all to re-merge all unresolved files'))
5932 hint=('use --all to re-merge all unresolved files'))
5932
5933
5933 if show:
5934 if show:
5934 fm = ui.formatter('resolve', opts)
5935 fm = ui.formatter('resolve', opts)
5935 ms = mergemod.mergestate.read(repo)
5936 ms = mergemod.mergestate.read(repo)
5936 m = scmutil.match(repo[None], pats, opts)
5937 m = scmutil.match(repo[None], pats, opts)
5937 for f in ms:
5938 for f in ms:
5938 if not m(f):
5939 if not m(f):
5939 continue
5940 continue
5940 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
5941 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
5941 'd': 'driverresolved'}[ms[f]]
5942 'd': 'driverresolved'}[ms[f]]
5942 fm.startitem()
5943 fm.startitem()
5943 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
5944 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
5944 fm.write('path', '%s\n', f, label=l)
5945 fm.write('path', '%s\n', f, label=l)
5945 fm.end()
5946 fm.end()
5946 return 0
5947 return 0
5947
5948
5948 with repo.wlock():
5949 with repo.wlock():
5949 ms = mergemod.mergestate.read(repo)
5950 ms = mergemod.mergestate.read(repo)
5950
5951
5951 if not (ms.active() or repo.dirstate.p2() != nullid):
5952 if not (ms.active() or repo.dirstate.p2() != nullid):
5952 raise error.Abort(
5953 raise error.Abort(
5953 _('resolve command not applicable when not merging'))
5954 _('resolve command not applicable when not merging'))
5954
5955
5955 wctx = repo[None]
5956 wctx = repo[None]
5956
5957
5957 if ms.mergedriver and ms.mdstate() == 'u':
5958 if ms.mergedriver and ms.mdstate() == 'u':
5958 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5959 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5959 ms.commit()
5960 ms.commit()
5960 # allow mark and unmark to go through
5961 # allow mark and unmark to go through
5961 if not mark and not unmark and not proceed:
5962 if not mark and not unmark and not proceed:
5962 return 1
5963 return 1
5963
5964
5964 m = scmutil.match(wctx, pats, opts)
5965 m = scmutil.match(wctx, pats, opts)
5965 ret = 0
5966 ret = 0
5966 didwork = False
5967 didwork = False
5967 runconclude = False
5968 runconclude = False
5968
5969
5969 tocomplete = []
5970 tocomplete = []
5970 for f in ms:
5971 for f in ms:
5971 if not m(f):
5972 if not m(f):
5972 continue
5973 continue
5973
5974
5974 didwork = True
5975 didwork = True
5975
5976
5976 # don't let driver-resolved files be marked, and run the conclude
5977 # don't let driver-resolved files be marked, and run the conclude
5977 # step if asked to resolve
5978 # step if asked to resolve
5978 if ms[f] == "d":
5979 if ms[f] == "d":
5979 exact = m.exact(f)
5980 exact = m.exact(f)
5980 if mark:
5981 if mark:
5981 if exact:
5982 if exact:
5982 ui.warn(_('not marking %s as it is driver-resolved\n')
5983 ui.warn(_('not marking %s as it is driver-resolved\n')
5983 % f)
5984 % f)
5984 elif unmark:
5985 elif unmark:
5985 if exact:
5986 if exact:
5986 ui.warn(_('not unmarking %s as it is driver-resolved\n')
5987 ui.warn(_('not unmarking %s as it is driver-resolved\n')
5987 % f)
5988 % f)
5988 else:
5989 else:
5989 runconclude = True
5990 runconclude = True
5990 continue
5991 continue
5991
5992
5992 if mark:
5993 if mark:
5993 ms.mark(f, "r")
5994 ms.mark(f, "r")
5994 elif unmark:
5995 elif unmark:
5995 ms.mark(f, "u")
5996 ms.mark(f, "u")
5996 else:
5997 else:
5997 # backup pre-resolve (merge uses .orig for its own purposes)
5998 # backup pre-resolve (merge uses .orig for its own purposes)
5998 a = repo.wjoin(f)
5999 a = repo.wjoin(f)
5999 try:
6000 try:
6000 util.copyfile(a, a + ".resolve")
6001 util.copyfile(a, a + ".resolve")
6001 except (IOError, OSError) as inst:
6002 except (IOError, OSError) as inst:
6002 if inst.errno != errno.ENOENT:
6003 if inst.errno != errno.ENOENT:
6003 raise
6004 raise
6004
6005
6005 try:
6006 try:
6006 # preresolve file
6007 # preresolve file
6007 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
6008 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
6008 'resolve')
6009 'resolve')
6009 complete, r = ms.preresolve(f, wctx)
6010 complete, r = ms.preresolve(f, wctx)
6010 if not complete:
6011 if not complete:
6011 tocomplete.append(f)
6012 tocomplete.append(f)
6012 elif r:
6013 elif r:
6013 ret = 1
6014 ret = 1
6014 finally:
6015 finally:
6015 ui.setconfig('ui', 'forcemerge', '', 'resolve')
6016 ui.setconfig('ui', 'forcemerge', '', 'resolve')
6016 ms.commit()
6017 ms.commit()
6017
6018
6018 # replace filemerge's .orig file with our resolve file, but only
6019 # replace filemerge's .orig file with our resolve file, but only
6019 # for merges that are complete
6020 # for merges that are complete
6020 if complete:
6021 if complete:
6021 try:
6022 try:
6022 util.rename(a + ".resolve",
6023 util.rename(a + ".resolve",
6023 scmutil.origpath(ui, repo, a))
6024 scmutil.origpath(ui, repo, a))
6024 except OSError as inst:
6025 except OSError as inst:
6025 if inst.errno != errno.ENOENT:
6026 if inst.errno != errno.ENOENT:
6026 raise
6027 raise
6027
6028
6028 for f in tocomplete:
6029 for f in tocomplete:
6029 try:
6030 try:
6030 # resolve file
6031 # resolve file
6031 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
6032 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
6032 'resolve')
6033 'resolve')
6033 r = ms.resolve(f, wctx)
6034 r = ms.resolve(f, wctx)
6034 if r:
6035 if r:
6035 ret = 1
6036 ret = 1
6036 finally:
6037 finally:
6037 ui.setconfig('ui', 'forcemerge', '', 'resolve')
6038 ui.setconfig('ui', 'forcemerge', '', 'resolve')
6038 ms.commit()
6039 ms.commit()
6039
6040
6040 # replace filemerge's .orig file with our resolve file
6041 # replace filemerge's .orig file with our resolve file
6041 a = repo.wjoin(f)
6042 a = repo.wjoin(f)
6042 try:
6043 try:
6043 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
6044 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
6044 except OSError as inst:
6045 except OSError as inst:
6045 if inst.errno != errno.ENOENT:
6046 if inst.errno != errno.ENOENT:
6046 raise
6047 raise
6047
6048
6048 ms.commit()
6049 ms.commit()
6049 ms.recordactions()
6050 ms.recordactions()
6050
6051
6051 if not didwork and pats:
6052 if not didwork and pats:
6052 hint = None
6053 hint = None
6053 if not any([p for p in pats if p.find(':') >= 0]):
6054 if not any([p for p in pats if p.find(':') >= 0]):
6054 pats = ['path:%s' % p for p in pats]
6055 pats = ['path:%s' % p for p in pats]
6055 m = scmutil.match(wctx, pats, opts)
6056 m = scmutil.match(wctx, pats, opts)
6056 for f in ms:
6057 for f in ms:
6057 if not m(f):
6058 if not m(f):
6058 continue
6059 continue
6059 flags = ''.join(['-%s ' % o[0] for o in flaglist
6060 flags = ''.join(['-%s ' % o[0] for o in flaglist
6060 if opts.get(o)])
6061 if opts.get(o)])
6061 hint = _("(try: hg resolve %s%s)\n") % (
6062 hint = _("(try: hg resolve %s%s)\n") % (
6062 flags,
6063 flags,
6063 ' '.join(pats))
6064 ' '.join(pats))
6064 break
6065 break
6065 ui.warn(_("arguments do not match paths that need resolving\n"))
6066 ui.warn(_("arguments do not match paths that need resolving\n"))
6066 if hint:
6067 if hint:
6067 ui.warn(hint)
6068 ui.warn(hint)
6068 elif ms.mergedriver and ms.mdstate() != 's':
6069 elif ms.mergedriver and ms.mdstate() != 's':
6069 # run conclude step when either a driver-resolved file is requested
6070 # run conclude step when either a driver-resolved file is requested
6070 # or there are no driver-resolved files
6071 # or there are no driver-resolved files
6071 # we can't use 'ret' to determine whether any files are unresolved
6072 # we can't use 'ret' to determine whether any files are unresolved
6072 # because we might not have tried to resolve some
6073 # because we might not have tried to resolve some
6073 if ((runconclude or not list(ms.driverresolved()))
6074 if ((runconclude or not list(ms.driverresolved()))
6074 and not list(ms.unresolved())):
6075 and not list(ms.unresolved())):
6075 proceed = mergemod.driverconclude(repo, ms, wctx)
6076 proceed = mergemod.driverconclude(repo, ms, wctx)
6076 ms.commit()
6077 ms.commit()
6077 if not proceed:
6078 if not proceed:
6078 return 1
6079 return 1
6079
6080
6080 # Nudge users into finishing an unfinished operation
6081 # Nudge users into finishing an unfinished operation
6081 unresolvedf = list(ms.unresolved())
6082 unresolvedf = list(ms.unresolved())
6082 driverresolvedf = list(ms.driverresolved())
6083 driverresolvedf = list(ms.driverresolved())
6083 if not unresolvedf and not driverresolvedf:
6084 if not unresolvedf and not driverresolvedf:
6084 ui.status(_('(no more unresolved files)\n'))
6085 ui.status(_('(no more unresolved files)\n'))
6085 cmdutil.checkafterresolved(repo)
6086 cmdutil.checkafterresolved(repo)
6086 elif not unresolvedf:
6087 elif not unresolvedf:
6087 ui.status(_('(no more unresolved files -- '
6088 ui.status(_('(no more unresolved files -- '
6088 'run "hg resolve --all" to conclude)\n'))
6089 'run "hg resolve --all" to conclude)\n'))
6089
6090
6090 return ret
6091 return ret
6091
6092
6092 @command('revert',
6093 @command('revert',
6093 [('a', 'all', None, _('revert all changes when no arguments given')),
6094 [('a', 'all', None, _('revert all changes when no arguments given')),
6094 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6095 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6095 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
6096 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
6096 ('C', 'no-backup', None, _('do not save backup copies of files')),
6097 ('C', 'no-backup', None, _('do not save backup copies of files')),
6097 ('i', 'interactive', None,
6098 ('i', 'interactive', None,
6098 _('interactively select the changes (EXPERIMENTAL)')),
6099 _('interactively select the changes (EXPERIMENTAL)')),
6099 ] + walkopts + dryrunopts,
6100 ] + walkopts + dryrunopts,
6100 _('[OPTION]... [-r REV] [NAME]...'))
6101 _('[OPTION]... [-r REV] [NAME]...'))
6101 def revert(ui, repo, *pats, **opts):
6102 def revert(ui, repo, *pats, **opts):
6102 """restore files to their checkout state
6103 """restore files to their checkout state
6103
6104
6104 .. note::
6105 .. note::
6105
6106
6106 To check out earlier revisions, you should use :hg:`update REV`.
6107 To check out earlier revisions, you should use :hg:`update REV`.
6107 To cancel an uncommitted merge (and lose your changes),
6108 To cancel an uncommitted merge (and lose your changes),
6108 use :hg:`update --clean .`.
6109 use :hg:`update --clean .`.
6109
6110
6110 With no revision specified, revert the specified files or directories
6111 With no revision specified, revert the specified files or directories
6111 to the contents they had in the parent of the working directory.
6112 to the contents they had in the parent of the working directory.
6112 This restores the contents of files to an unmodified
6113 This restores the contents of files to an unmodified
6113 state and unschedules adds, removes, copies, and renames. If the
6114 state and unschedules adds, removes, copies, and renames. If the
6114 working directory has two parents, you must explicitly specify a
6115 working directory has two parents, you must explicitly specify a
6115 revision.
6116 revision.
6116
6117
6117 Using the -r/--rev or -d/--date options, revert the given files or
6118 Using the -r/--rev or -d/--date options, revert the given files or
6118 directories to their states as of a specific revision. Because
6119 directories to their states as of a specific revision. Because
6119 revert does not change the working directory parents, this will
6120 revert does not change the working directory parents, this will
6120 cause these files to appear modified. This can be helpful to "back
6121 cause these files to appear modified. This can be helpful to "back
6121 out" some or all of an earlier change. See :hg:`backout` for a
6122 out" some or all of an earlier change. See :hg:`backout` for a
6122 related method.
6123 related method.
6123
6124
6124 Modified files are saved with a .orig suffix before reverting.
6125 Modified files are saved with a .orig suffix before reverting.
6125 To disable these backups, use --no-backup. It is possible to store
6126 To disable these backups, use --no-backup. It is possible to store
6126 the backup files in a custom directory relative to the root of the
6127 the backup files in a custom directory relative to the root of the
6127 repository by setting the ``ui.origbackuppath`` configuration
6128 repository by setting the ``ui.origbackuppath`` configuration
6128 option.
6129 option.
6129
6130
6130 See :hg:`help dates` for a list of formats valid for -d/--date.
6131 See :hg:`help dates` for a list of formats valid for -d/--date.
6131
6132
6132 See :hg:`help backout` for a way to reverse the effect of an
6133 See :hg:`help backout` for a way to reverse the effect of an
6133 earlier changeset.
6134 earlier changeset.
6134
6135
6135 Returns 0 on success.
6136 Returns 0 on success.
6136 """
6137 """
6137
6138
6138 if opts.get("date"):
6139 if opts.get("date"):
6139 if opts.get("rev"):
6140 if opts.get("rev"):
6140 raise error.Abort(_("you can't specify a revision and a date"))
6141 raise error.Abort(_("you can't specify a revision and a date"))
6141 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
6142 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
6142
6143
6143 parent, p2 = repo.dirstate.parents()
6144 parent, p2 = repo.dirstate.parents()
6144 if not opts.get('rev') and p2 != nullid:
6145 if not opts.get('rev') and p2 != nullid:
6145 # revert after merge is a trap for new users (issue2915)
6146 # revert after merge is a trap for new users (issue2915)
6146 raise error.Abort(_('uncommitted merge with no revision specified'),
6147 raise error.Abort(_('uncommitted merge with no revision specified'),
6147 hint=_("use 'hg update' or see 'hg help revert'"))
6148 hint=_("use 'hg update' or see 'hg help revert'"))
6148
6149
6149 ctx = scmutil.revsingle(repo, opts.get('rev'))
6150 ctx = scmutil.revsingle(repo, opts.get('rev'))
6150
6151
6151 if (not (pats or opts.get('include') or opts.get('exclude') or
6152 if (not (pats or opts.get('include') or opts.get('exclude') or
6152 opts.get('all') or opts.get('interactive'))):
6153 opts.get('all') or opts.get('interactive'))):
6153 msg = _("no files or directories specified")
6154 msg = _("no files or directories specified")
6154 if p2 != nullid:
6155 if p2 != nullid:
6155 hint = _("uncommitted merge, use --all to discard all changes,"
6156 hint = _("uncommitted merge, use --all to discard all changes,"
6156 " or 'hg update -C .' to abort the merge")
6157 " or 'hg update -C .' to abort the merge")
6157 raise error.Abort(msg, hint=hint)
6158 raise error.Abort(msg, hint=hint)
6158 dirty = any(repo.status())
6159 dirty = any(repo.status())
6159 node = ctx.node()
6160 node = ctx.node()
6160 if node != parent:
6161 if node != parent:
6161 if dirty:
6162 if dirty:
6162 hint = _("uncommitted changes, use --all to discard all"
6163 hint = _("uncommitted changes, use --all to discard all"
6163 " changes, or 'hg update %s' to update") % ctx.rev()
6164 " changes, or 'hg update %s' to update") % ctx.rev()
6164 else:
6165 else:
6165 hint = _("use --all to revert all files,"
6166 hint = _("use --all to revert all files,"
6166 " or 'hg update %s' to update") % ctx.rev()
6167 " or 'hg update %s' to update") % ctx.rev()
6167 elif dirty:
6168 elif dirty:
6168 hint = _("uncommitted changes, use --all to discard all changes")
6169 hint = _("uncommitted changes, use --all to discard all changes")
6169 else:
6170 else:
6170 hint = _("use --all to revert all files")
6171 hint = _("use --all to revert all files")
6171 raise error.Abort(msg, hint=hint)
6172 raise error.Abort(msg, hint=hint)
6172
6173
6173 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
6174 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
6174
6175
6175 @command('rollback', dryrunopts +
6176 @command('rollback', dryrunopts +
6176 [('f', 'force', False, _('ignore safety measures'))])
6177 [('f', 'force', False, _('ignore safety measures'))])
6177 def rollback(ui, repo, **opts):
6178 def rollback(ui, repo, **opts):
6178 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6179 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6179
6180
6180 Please use :hg:`commit --amend` instead of rollback to correct
6181 Please use :hg:`commit --amend` instead of rollback to correct
6181 mistakes in the last commit.
6182 mistakes in the last commit.
6182
6183
6183 This command should be used with care. There is only one level of
6184 This command should be used with care. There is only one level of
6184 rollback, and there is no way to undo a rollback. It will also
6185 rollback, and there is no way to undo a rollback. It will also
6185 restore the dirstate at the time of the last transaction, losing
6186 restore the dirstate at the time of the last transaction, losing
6186 any dirstate changes since that time. This command does not alter
6187 any dirstate changes since that time. This command does not alter
6187 the working directory.
6188 the working directory.
6188
6189
6189 Transactions are used to encapsulate the effects of all commands
6190 Transactions are used to encapsulate the effects of all commands
6190 that create new changesets or propagate existing changesets into a
6191 that create new changesets or propagate existing changesets into a
6191 repository.
6192 repository.
6192
6193
6193 .. container:: verbose
6194 .. container:: verbose
6194
6195
6195 For example, the following commands are transactional, and their
6196 For example, the following commands are transactional, and their
6196 effects can be rolled back:
6197 effects can be rolled back:
6197
6198
6198 - commit
6199 - commit
6199 - import
6200 - import
6200 - pull
6201 - pull
6201 - push (with this repository as the destination)
6202 - push (with this repository as the destination)
6202 - unbundle
6203 - unbundle
6203
6204
6204 To avoid permanent data loss, rollback will refuse to rollback a
6205 To avoid permanent data loss, rollback will refuse to rollback a
6205 commit transaction if it isn't checked out. Use --force to
6206 commit transaction if it isn't checked out. Use --force to
6206 override this protection.
6207 override this protection.
6207
6208
6208 The rollback command can be entirely disabled by setting the
6209 The rollback command can be entirely disabled by setting the
6209 ``ui.rollback`` configuration setting to false. If you're here
6210 ``ui.rollback`` configuration setting to false. If you're here
6210 because you want to use rollback and it's disabled, you can
6211 because you want to use rollback and it's disabled, you can
6211 re-enable the command by setting ``ui.rollback`` to true.
6212 re-enable the command by setting ``ui.rollback`` to true.
6212
6213
6213 This command is not intended for use on public repositories. Once
6214 This command is not intended for use on public repositories. Once
6214 changes are visible for pull by other users, rolling a transaction
6215 changes are visible for pull by other users, rolling a transaction
6215 back locally is ineffective (someone else may already have pulled
6216 back locally is ineffective (someone else may already have pulled
6216 the changes). Furthermore, a race is possible with readers of the
6217 the changes). Furthermore, a race is possible with readers of the
6217 repository; for example an in-progress pull from the repository
6218 repository; for example an in-progress pull from the repository
6218 may fail if a rollback is performed.
6219 may fail if a rollback is performed.
6219
6220
6220 Returns 0 on success, 1 if no rollback data is available.
6221 Returns 0 on success, 1 if no rollback data is available.
6221 """
6222 """
6222 if not ui.configbool('ui', 'rollback', True):
6223 if not ui.configbool('ui', 'rollback', True):
6223 raise error.Abort(_('rollback is disabled because it is unsafe'),
6224 raise error.Abort(_('rollback is disabled because it is unsafe'),
6224 hint=('see `hg help -v rollback` for information'))
6225 hint=('see `hg help -v rollback` for information'))
6225 return repo.rollback(dryrun=opts.get('dry_run'),
6226 return repo.rollback(dryrun=opts.get('dry_run'),
6226 force=opts.get('force'))
6227 force=opts.get('force'))
6227
6228
6228 @command('root', [])
6229 @command('root', [])
6229 def root(ui, repo):
6230 def root(ui, repo):
6230 """print the root (top) of the current working directory
6231 """print the root (top) of the current working directory
6231
6232
6232 Print the root directory of the current repository.
6233 Print the root directory of the current repository.
6233
6234
6234 Returns 0 on success.
6235 Returns 0 on success.
6235 """
6236 """
6236 ui.write(repo.root + "\n")
6237 ui.write(repo.root + "\n")
6237
6238
6238 @command('^serve',
6239 @command('^serve',
6239 [('A', 'accesslog', '', _('name of access log file to write to'),
6240 [('A', 'accesslog', '', _('name of access log file to write to'),
6240 _('FILE')),
6241 _('FILE')),
6241 ('d', 'daemon', None, _('run server in background')),
6242 ('d', 'daemon', None, _('run server in background')),
6242 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
6243 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
6243 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
6244 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
6244 # use string type, then we can check if something was passed
6245 # use string type, then we can check if something was passed
6245 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
6246 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
6246 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
6247 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
6247 _('ADDR')),
6248 _('ADDR')),
6248 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
6249 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
6249 _('PREFIX')),
6250 _('PREFIX')),
6250 ('n', 'name', '',
6251 ('n', 'name', '',
6251 _('name to show in web pages (default: working directory)'), _('NAME')),
6252 _('name to show in web pages (default: working directory)'), _('NAME')),
6252 ('', 'web-conf', '',
6253 ('', 'web-conf', '',
6253 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
6254 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
6254 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
6255 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
6255 _('FILE')),
6256 _('FILE')),
6256 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
6257 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
6257 ('', 'stdio', None, _('for remote clients')),
6258 ('', 'stdio', None, _('for remote clients')),
6258 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
6259 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
6259 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
6260 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
6260 ('', 'style', '', _('template style to use'), _('STYLE')),
6261 ('', 'style', '', _('template style to use'), _('STYLE')),
6261 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
6262 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
6262 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
6263 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
6263 _('[OPTION]...'),
6264 _('[OPTION]...'),
6264 optionalrepo=True)
6265 optionalrepo=True)
6265 def serve(ui, repo, **opts):
6266 def serve(ui, repo, **opts):
6266 """start stand-alone webserver
6267 """start stand-alone webserver
6267
6268
6268 Start a local HTTP repository browser and pull server. You can use
6269 Start a local HTTP repository browser and pull server. You can use
6269 this for ad-hoc sharing and browsing of repositories. It is
6270 this for ad-hoc sharing and browsing of repositories. It is
6270 recommended to use a real web server to serve a repository for
6271 recommended to use a real web server to serve a repository for
6271 longer periods of time.
6272 longer periods of time.
6272
6273
6273 Please note that the server does not implement access control.
6274 Please note that the server does not implement access control.
6274 This means that, by default, anybody can read from the server and
6275 This means that, by default, anybody can read from the server and
6275 nobody can write to it by default. Set the ``web.allow_push``
6276 nobody can write to it by default. Set the ``web.allow_push``
6276 option to ``*`` to allow everybody to push to the server. You
6277 option to ``*`` to allow everybody to push to the server. You
6277 should use a real web server if you need to authenticate users.
6278 should use a real web server if you need to authenticate users.
6278
6279
6279 By default, the server logs accesses to stdout and errors to
6280 By default, the server logs accesses to stdout and errors to
6280 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6281 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6281 files.
6282 files.
6282
6283
6283 To have the server choose a free port number to listen on, specify
6284 To have the server choose a free port number to listen on, specify
6284 a port number of 0; in this case, the server will print the port
6285 a port number of 0; in this case, the server will print the port
6285 number it uses.
6286 number it uses.
6286
6287
6287 Returns 0 on success.
6288 Returns 0 on success.
6288 """
6289 """
6289
6290
6290 if opts["stdio"] and opts["cmdserver"]:
6291 if opts["stdio"] and opts["cmdserver"]:
6291 raise error.Abort(_("cannot use --stdio with --cmdserver"))
6292 raise error.Abort(_("cannot use --stdio with --cmdserver"))
6292
6293
6293 if opts["stdio"]:
6294 if opts["stdio"]:
6294 if repo is None:
6295 if repo is None:
6295 raise error.RepoError(_("there is no Mercurial repository here"
6296 raise error.RepoError(_("there is no Mercurial repository here"
6296 " (.hg not found)"))
6297 " (.hg not found)"))
6297 s = sshserver.sshserver(ui, repo)
6298 s = sshserver.sshserver(ui, repo)
6298 s.serve_forever()
6299 s.serve_forever()
6299
6300
6300 if opts["cmdserver"]:
6301 if opts["cmdserver"]:
6301 service = commandserver.createservice(ui, repo, opts)
6302 service = commandserver.createservice(ui, repo, opts)
6302 else:
6303 else:
6303 service = hgweb.createservice(ui, repo, opts)
6304 service = hgweb.createservice(ui, repo, opts)
6304 return cmdutil.service(opts, initfn=service.init, runfn=service.run)
6305 return server.runservice(opts, initfn=service.init, runfn=service.run)
6305
6306
6306 @command('^status|st',
6307 @command('^status|st',
6307 [('A', 'all', None, _('show status of all files')),
6308 [('A', 'all', None, _('show status of all files')),
6308 ('m', 'modified', None, _('show only modified files')),
6309 ('m', 'modified', None, _('show only modified files')),
6309 ('a', 'added', None, _('show only added files')),
6310 ('a', 'added', None, _('show only added files')),
6310 ('r', 'removed', None, _('show only removed files')),
6311 ('r', 'removed', None, _('show only removed files')),
6311 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
6312 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
6312 ('c', 'clean', None, _('show only files without changes')),
6313 ('c', 'clean', None, _('show only files without changes')),
6313 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
6314 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
6314 ('i', 'ignored', None, _('show only ignored files')),
6315 ('i', 'ignored', None, _('show only ignored files')),
6315 ('n', 'no-status', None, _('hide status prefix')),
6316 ('n', 'no-status', None, _('hide status prefix')),
6316 ('C', 'copies', None, _('show source of copied files')),
6317 ('C', 'copies', None, _('show source of copied files')),
6317 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
6318 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
6318 ('', 'rev', [], _('show difference from revision'), _('REV')),
6319 ('', 'rev', [], _('show difference from revision'), _('REV')),
6319 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
6320 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
6320 ] + walkopts + subrepoopts + formatteropts,
6321 ] + walkopts + subrepoopts + formatteropts,
6321 _('[OPTION]... [FILE]...'),
6322 _('[OPTION]... [FILE]...'),
6322 inferrepo=True)
6323 inferrepo=True)
6323 def status(ui, repo, *pats, **opts):
6324 def status(ui, repo, *pats, **opts):
6324 """show changed files in the working directory
6325 """show changed files in the working directory
6325
6326
6326 Show status of files in the repository. If names are given, only
6327 Show status of files in the repository. If names are given, only
6327 files that match are shown. Files that are clean or ignored or
6328 files that match are shown. Files that are clean or ignored or
6328 the source of a copy/move operation, are not listed unless
6329 the source of a copy/move operation, are not listed unless
6329 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6330 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6330 Unless options described with "show only ..." are given, the
6331 Unless options described with "show only ..." are given, the
6331 options -mardu are used.
6332 options -mardu are used.
6332
6333
6333 Option -q/--quiet hides untracked (unknown and ignored) files
6334 Option -q/--quiet hides untracked (unknown and ignored) files
6334 unless explicitly requested with -u/--unknown or -i/--ignored.
6335 unless explicitly requested with -u/--unknown or -i/--ignored.
6335
6336
6336 .. note::
6337 .. note::
6337
6338
6338 :hg:`status` may appear to disagree with diff if permissions have
6339 :hg:`status` may appear to disagree with diff if permissions have
6339 changed or a merge has occurred. The standard diff format does
6340 changed or a merge has occurred. The standard diff format does
6340 not report permission changes and diff only reports changes
6341 not report permission changes and diff only reports changes
6341 relative to one merge parent.
6342 relative to one merge parent.
6342
6343
6343 If one revision is given, it is used as the base revision.
6344 If one revision is given, it is used as the base revision.
6344 If two revisions are given, the differences between them are
6345 If two revisions are given, the differences between them are
6345 shown. The --change option can also be used as a shortcut to list
6346 shown. The --change option can also be used as a shortcut to list
6346 the changed files of a revision from its first parent.
6347 the changed files of a revision from its first parent.
6347
6348
6348 The codes used to show the status of files are::
6349 The codes used to show the status of files are::
6349
6350
6350 M = modified
6351 M = modified
6351 A = added
6352 A = added
6352 R = removed
6353 R = removed
6353 C = clean
6354 C = clean
6354 ! = missing (deleted by non-hg command, but still tracked)
6355 ! = missing (deleted by non-hg command, but still tracked)
6355 ? = not tracked
6356 ? = not tracked
6356 I = ignored
6357 I = ignored
6357 = origin of the previous file (with --copies)
6358 = origin of the previous file (with --copies)
6358
6359
6359 .. container:: verbose
6360 .. container:: verbose
6360
6361
6361 Examples:
6362 Examples:
6362
6363
6363 - show changes in the working directory relative to a
6364 - show changes in the working directory relative to a
6364 changeset::
6365 changeset::
6365
6366
6366 hg status --rev 9353
6367 hg status --rev 9353
6367
6368
6368 - show changes in the working directory relative to the
6369 - show changes in the working directory relative to the
6369 current directory (see :hg:`help patterns` for more information)::
6370 current directory (see :hg:`help patterns` for more information)::
6370
6371
6371 hg status re:
6372 hg status re:
6372
6373
6373 - show all changes including copies in an existing changeset::
6374 - show all changes including copies in an existing changeset::
6374
6375
6375 hg status --copies --change 9353
6376 hg status --copies --change 9353
6376
6377
6377 - get a NUL separated list of added files, suitable for xargs::
6378 - get a NUL separated list of added files, suitable for xargs::
6378
6379
6379 hg status -an0
6380 hg status -an0
6380
6381
6381 Returns 0 on success.
6382 Returns 0 on success.
6382 """
6383 """
6383
6384
6384 revs = opts.get('rev')
6385 revs = opts.get('rev')
6385 change = opts.get('change')
6386 change = opts.get('change')
6386
6387
6387 if revs and change:
6388 if revs and change:
6388 msg = _('cannot specify --rev and --change at the same time')
6389 msg = _('cannot specify --rev and --change at the same time')
6389 raise error.Abort(msg)
6390 raise error.Abort(msg)
6390 elif change:
6391 elif change:
6391 node2 = scmutil.revsingle(repo, change, None).node()
6392 node2 = scmutil.revsingle(repo, change, None).node()
6392 node1 = repo[node2].p1().node()
6393 node1 = repo[node2].p1().node()
6393 else:
6394 else:
6394 node1, node2 = scmutil.revpair(repo, revs)
6395 node1, node2 = scmutil.revpair(repo, revs)
6395
6396
6396 if pats:
6397 if pats:
6397 cwd = repo.getcwd()
6398 cwd = repo.getcwd()
6398 else:
6399 else:
6399 cwd = ''
6400 cwd = ''
6400
6401
6401 if opts.get('print0'):
6402 if opts.get('print0'):
6402 end = '\0'
6403 end = '\0'
6403 else:
6404 else:
6404 end = '\n'
6405 end = '\n'
6405 copy = {}
6406 copy = {}
6406 states = 'modified added removed deleted unknown ignored clean'.split()
6407 states = 'modified added removed deleted unknown ignored clean'.split()
6407 show = [k for k in states if opts.get(k)]
6408 show = [k for k in states if opts.get(k)]
6408 if opts.get('all'):
6409 if opts.get('all'):
6409 show += ui.quiet and (states[:4] + ['clean']) or states
6410 show += ui.quiet and (states[:4] + ['clean']) or states
6410 if not show:
6411 if not show:
6411 if ui.quiet:
6412 if ui.quiet:
6412 show = states[:4]
6413 show = states[:4]
6413 else:
6414 else:
6414 show = states[:5]
6415 show = states[:5]
6415
6416
6416 m = scmutil.match(repo[node2], pats, opts)
6417 m = scmutil.match(repo[node2], pats, opts)
6417 stat = repo.status(node1, node2, m,
6418 stat = repo.status(node1, node2, m,
6418 'ignored' in show, 'clean' in show, 'unknown' in show,
6419 'ignored' in show, 'clean' in show, 'unknown' in show,
6419 opts.get('subrepos'))
6420 opts.get('subrepos'))
6420 changestates = zip(states, 'MAR!?IC', stat)
6421 changestates = zip(states, 'MAR!?IC', stat)
6421
6422
6422 if (opts.get('all') or opts.get('copies')
6423 if (opts.get('all') or opts.get('copies')
6423 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
6424 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
6424 copy = copies.pathcopies(repo[node1], repo[node2], m)
6425 copy = copies.pathcopies(repo[node1], repo[node2], m)
6425
6426
6426 fm = ui.formatter('status', opts)
6427 fm = ui.formatter('status', opts)
6427 fmt = '%s' + end
6428 fmt = '%s' + end
6428 showchar = not opts.get('no_status')
6429 showchar = not opts.get('no_status')
6429
6430
6430 for state, char, files in changestates:
6431 for state, char, files in changestates:
6431 if state in show:
6432 if state in show:
6432 label = 'status.' + state
6433 label = 'status.' + state
6433 for f in files:
6434 for f in files:
6434 fm.startitem()
6435 fm.startitem()
6435 fm.condwrite(showchar, 'status', '%s ', char, label=label)
6436 fm.condwrite(showchar, 'status', '%s ', char, label=label)
6436 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
6437 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
6437 if f in copy:
6438 if f in copy:
6438 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
6439 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
6439 label='status.copied')
6440 label='status.copied')
6440 fm.end()
6441 fm.end()
6441
6442
6442 @command('^summary|sum',
6443 @command('^summary|sum',
6443 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
6444 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
6444 def summary(ui, repo, **opts):
6445 def summary(ui, repo, **opts):
6445 """summarize working directory state
6446 """summarize working directory state
6446
6447
6447 This generates a brief summary of the working directory state,
6448 This generates a brief summary of the working directory state,
6448 including parents, branch, commit status, phase and available updates.
6449 including parents, branch, commit status, phase and available updates.
6449
6450
6450 With the --remote option, this will check the default paths for
6451 With the --remote option, this will check the default paths for
6451 incoming and outgoing changes. This can be time-consuming.
6452 incoming and outgoing changes. This can be time-consuming.
6452
6453
6453 Returns 0 on success.
6454 Returns 0 on success.
6454 """
6455 """
6455
6456
6456 ctx = repo[None]
6457 ctx = repo[None]
6457 parents = ctx.parents()
6458 parents = ctx.parents()
6458 pnode = parents[0].node()
6459 pnode = parents[0].node()
6459 marks = []
6460 marks = []
6460
6461
6461 ms = None
6462 ms = None
6462 try:
6463 try:
6463 ms = mergemod.mergestate.read(repo)
6464 ms = mergemod.mergestate.read(repo)
6464 except error.UnsupportedMergeRecords as e:
6465 except error.UnsupportedMergeRecords as e:
6465 s = ' '.join(e.recordtypes)
6466 s = ' '.join(e.recordtypes)
6466 ui.warn(
6467 ui.warn(
6467 _('warning: merge state has unsupported record types: %s\n') % s)
6468 _('warning: merge state has unsupported record types: %s\n') % s)
6468 unresolved = 0
6469 unresolved = 0
6469 else:
6470 else:
6470 unresolved = [f for f in ms if ms[f] == 'u']
6471 unresolved = [f for f in ms if ms[f] == 'u']
6471
6472
6472 for p in parents:
6473 for p in parents:
6473 # label with log.changeset (instead of log.parent) since this
6474 # label with log.changeset (instead of log.parent) since this
6474 # shows a working directory parent *changeset*:
6475 # shows a working directory parent *changeset*:
6475 # i18n: column positioning for "hg summary"
6476 # i18n: column positioning for "hg summary"
6476 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
6477 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
6477 label='log.changeset changeset.%s' % p.phasestr())
6478 label='log.changeset changeset.%s' % p.phasestr())
6478 ui.write(' '.join(p.tags()), label='log.tag')
6479 ui.write(' '.join(p.tags()), label='log.tag')
6479 if p.bookmarks():
6480 if p.bookmarks():
6480 marks.extend(p.bookmarks())
6481 marks.extend(p.bookmarks())
6481 if p.rev() == -1:
6482 if p.rev() == -1:
6482 if not len(repo):
6483 if not len(repo):
6483 ui.write(_(' (empty repository)'))
6484 ui.write(_(' (empty repository)'))
6484 else:
6485 else:
6485 ui.write(_(' (no revision checked out)'))
6486 ui.write(_(' (no revision checked out)'))
6486 ui.write('\n')
6487 ui.write('\n')
6487 if p.description():
6488 if p.description():
6488 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
6489 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
6489 label='log.summary')
6490 label='log.summary')
6490
6491
6491 branch = ctx.branch()
6492 branch = ctx.branch()
6492 bheads = repo.branchheads(branch)
6493 bheads = repo.branchheads(branch)
6493 # i18n: column positioning for "hg summary"
6494 # i18n: column positioning for "hg summary"
6494 m = _('branch: %s\n') % branch
6495 m = _('branch: %s\n') % branch
6495 if branch != 'default':
6496 if branch != 'default':
6496 ui.write(m, label='log.branch')
6497 ui.write(m, label='log.branch')
6497 else:
6498 else:
6498 ui.status(m, label='log.branch')
6499 ui.status(m, label='log.branch')
6499
6500
6500 if marks:
6501 if marks:
6501 active = repo._activebookmark
6502 active = repo._activebookmark
6502 # i18n: column positioning for "hg summary"
6503 # i18n: column positioning for "hg summary"
6503 ui.write(_('bookmarks:'), label='log.bookmark')
6504 ui.write(_('bookmarks:'), label='log.bookmark')
6504 if active is not None:
6505 if active is not None:
6505 if active in marks:
6506 if active in marks:
6506 ui.write(' *' + active, label=activebookmarklabel)
6507 ui.write(' *' + active, label=activebookmarklabel)
6507 marks.remove(active)
6508 marks.remove(active)
6508 else:
6509 else:
6509 ui.write(' [%s]' % active, label=activebookmarklabel)
6510 ui.write(' [%s]' % active, label=activebookmarklabel)
6510 for m in marks:
6511 for m in marks:
6511 ui.write(' ' + m, label='log.bookmark')
6512 ui.write(' ' + m, label='log.bookmark')
6512 ui.write('\n', label='log.bookmark')
6513 ui.write('\n', label='log.bookmark')
6513
6514
6514 status = repo.status(unknown=True)
6515 status = repo.status(unknown=True)
6515
6516
6516 c = repo.dirstate.copies()
6517 c = repo.dirstate.copies()
6517 copied, renamed = [], []
6518 copied, renamed = [], []
6518 for d, s in c.iteritems():
6519 for d, s in c.iteritems():
6519 if s in status.removed:
6520 if s in status.removed:
6520 status.removed.remove(s)
6521 status.removed.remove(s)
6521 renamed.append(d)
6522 renamed.append(d)
6522 else:
6523 else:
6523 copied.append(d)
6524 copied.append(d)
6524 if d in status.added:
6525 if d in status.added:
6525 status.added.remove(d)
6526 status.added.remove(d)
6526
6527
6527 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6528 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6528
6529
6529 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
6530 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
6530 (ui.label(_('%d added'), 'status.added'), status.added),
6531 (ui.label(_('%d added'), 'status.added'), status.added),
6531 (ui.label(_('%d removed'), 'status.removed'), status.removed),
6532 (ui.label(_('%d removed'), 'status.removed'), status.removed),
6532 (ui.label(_('%d renamed'), 'status.copied'), renamed),
6533 (ui.label(_('%d renamed'), 'status.copied'), renamed),
6533 (ui.label(_('%d copied'), 'status.copied'), copied),
6534 (ui.label(_('%d copied'), 'status.copied'), copied),
6534 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
6535 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
6535 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
6536 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
6536 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
6537 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
6537 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
6538 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
6538 t = []
6539 t = []
6539 for l, s in labels:
6540 for l, s in labels:
6540 if s:
6541 if s:
6541 t.append(l % len(s))
6542 t.append(l % len(s))
6542
6543
6543 t = ', '.join(t)
6544 t = ', '.join(t)
6544 cleanworkdir = False
6545 cleanworkdir = False
6545
6546
6546 if repo.vfs.exists('graftstate'):
6547 if repo.vfs.exists('graftstate'):
6547 t += _(' (graft in progress)')
6548 t += _(' (graft in progress)')
6548 if repo.vfs.exists('updatestate'):
6549 if repo.vfs.exists('updatestate'):
6549 t += _(' (interrupted update)')
6550 t += _(' (interrupted update)')
6550 elif len(parents) > 1:
6551 elif len(parents) > 1:
6551 t += _(' (merge)')
6552 t += _(' (merge)')
6552 elif branch != parents[0].branch():
6553 elif branch != parents[0].branch():
6553 t += _(' (new branch)')
6554 t += _(' (new branch)')
6554 elif (parents[0].closesbranch() and
6555 elif (parents[0].closesbranch() and
6555 pnode in repo.branchheads(branch, closed=True)):
6556 pnode in repo.branchheads(branch, closed=True)):
6556 t += _(' (head closed)')
6557 t += _(' (head closed)')
6557 elif not (status.modified or status.added or status.removed or renamed or
6558 elif not (status.modified or status.added or status.removed or renamed or
6558 copied or subs):
6559 copied or subs):
6559 t += _(' (clean)')
6560 t += _(' (clean)')
6560 cleanworkdir = True
6561 cleanworkdir = True
6561 elif pnode not in bheads:
6562 elif pnode not in bheads:
6562 t += _(' (new branch head)')
6563 t += _(' (new branch head)')
6563
6564
6564 if parents:
6565 if parents:
6565 pendingphase = max(p.phase() for p in parents)
6566 pendingphase = max(p.phase() for p in parents)
6566 else:
6567 else:
6567 pendingphase = phases.public
6568 pendingphase = phases.public
6568
6569
6569 if pendingphase > phases.newcommitphase(ui):
6570 if pendingphase > phases.newcommitphase(ui):
6570 t += ' (%s)' % phases.phasenames[pendingphase]
6571 t += ' (%s)' % phases.phasenames[pendingphase]
6571
6572
6572 if cleanworkdir:
6573 if cleanworkdir:
6573 # i18n: column positioning for "hg summary"
6574 # i18n: column positioning for "hg summary"
6574 ui.status(_('commit: %s\n') % t.strip())
6575 ui.status(_('commit: %s\n') % t.strip())
6575 else:
6576 else:
6576 # i18n: column positioning for "hg summary"
6577 # i18n: column positioning for "hg summary"
6577 ui.write(_('commit: %s\n') % t.strip())
6578 ui.write(_('commit: %s\n') % t.strip())
6578
6579
6579 # all ancestors of branch heads - all ancestors of parent = new csets
6580 # all ancestors of branch heads - all ancestors of parent = new csets
6580 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
6581 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
6581 bheads))
6582 bheads))
6582
6583
6583 if new == 0:
6584 if new == 0:
6584 # i18n: column positioning for "hg summary"
6585 # i18n: column positioning for "hg summary"
6585 ui.status(_('update: (current)\n'))
6586 ui.status(_('update: (current)\n'))
6586 elif pnode not in bheads:
6587 elif pnode not in bheads:
6587 # i18n: column positioning for "hg summary"
6588 # i18n: column positioning for "hg summary"
6588 ui.write(_('update: %d new changesets (update)\n') % new)
6589 ui.write(_('update: %d new changesets (update)\n') % new)
6589 else:
6590 else:
6590 # i18n: column positioning for "hg summary"
6591 # i18n: column positioning for "hg summary"
6591 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
6592 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
6592 (new, len(bheads)))
6593 (new, len(bheads)))
6593
6594
6594 t = []
6595 t = []
6595 draft = len(repo.revs('draft()'))
6596 draft = len(repo.revs('draft()'))
6596 if draft:
6597 if draft:
6597 t.append(_('%d draft') % draft)
6598 t.append(_('%d draft') % draft)
6598 secret = len(repo.revs('secret()'))
6599 secret = len(repo.revs('secret()'))
6599 if secret:
6600 if secret:
6600 t.append(_('%d secret') % secret)
6601 t.append(_('%d secret') % secret)
6601
6602
6602 if draft or secret:
6603 if draft or secret:
6603 ui.status(_('phases: %s\n') % ', '.join(t))
6604 ui.status(_('phases: %s\n') % ', '.join(t))
6604
6605
6605 if obsolete.isenabled(repo, obsolete.createmarkersopt):
6606 if obsolete.isenabled(repo, obsolete.createmarkersopt):
6606 for trouble in ("unstable", "divergent", "bumped"):
6607 for trouble in ("unstable", "divergent", "bumped"):
6607 numtrouble = len(repo.revs(trouble + "()"))
6608 numtrouble = len(repo.revs(trouble + "()"))
6608 # We write all the possibilities to ease translation
6609 # We write all the possibilities to ease translation
6609 troublemsg = {
6610 troublemsg = {
6610 "unstable": _("unstable: %d changesets"),
6611 "unstable": _("unstable: %d changesets"),
6611 "divergent": _("divergent: %d changesets"),
6612 "divergent": _("divergent: %d changesets"),
6612 "bumped": _("bumped: %d changesets"),
6613 "bumped": _("bumped: %d changesets"),
6613 }
6614 }
6614 if numtrouble > 0:
6615 if numtrouble > 0:
6615 ui.status(troublemsg[trouble] % numtrouble + "\n")
6616 ui.status(troublemsg[trouble] % numtrouble + "\n")
6616
6617
6617 cmdutil.summaryhooks(ui, repo)
6618 cmdutil.summaryhooks(ui, repo)
6618
6619
6619 if opts.get('remote'):
6620 if opts.get('remote'):
6620 needsincoming, needsoutgoing = True, True
6621 needsincoming, needsoutgoing = True, True
6621 else:
6622 else:
6622 needsincoming, needsoutgoing = False, False
6623 needsincoming, needsoutgoing = False, False
6623 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
6624 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
6624 if i:
6625 if i:
6625 needsincoming = True
6626 needsincoming = True
6626 if o:
6627 if o:
6627 needsoutgoing = True
6628 needsoutgoing = True
6628 if not needsincoming and not needsoutgoing:
6629 if not needsincoming and not needsoutgoing:
6629 return
6630 return
6630
6631
6631 def getincoming():
6632 def getincoming():
6632 source, branches = hg.parseurl(ui.expandpath('default'))
6633 source, branches = hg.parseurl(ui.expandpath('default'))
6633 sbranch = branches[0]
6634 sbranch = branches[0]
6634 try:
6635 try:
6635 other = hg.peer(repo, {}, source)
6636 other = hg.peer(repo, {}, source)
6636 except error.RepoError:
6637 except error.RepoError:
6637 if opts.get('remote'):
6638 if opts.get('remote'):
6638 raise
6639 raise
6639 return source, sbranch, None, None, None
6640 return source, sbranch, None, None, None
6640 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6641 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6641 if revs:
6642 if revs:
6642 revs = [other.lookup(rev) for rev in revs]
6643 revs = [other.lookup(rev) for rev in revs]
6643 ui.debug('comparing with %s\n' % util.hidepassword(source))
6644 ui.debug('comparing with %s\n' % util.hidepassword(source))
6644 repo.ui.pushbuffer()
6645 repo.ui.pushbuffer()
6645 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6646 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6646 repo.ui.popbuffer()
6647 repo.ui.popbuffer()
6647 return source, sbranch, other, commoninc, commoninc[1]
6648 return source, sbranch, other, commoninc, commoninc[1]
6648
6649
6649 if needsincoming:
6650 if needsincoming:
6650 source, sbranch, sother, commoninc, incoming = getincoming()
6651 source, sbranch, sother, commoninc, incoming = getincoming()
6651 else:
6652 else:
6652 source = sbranch = sother = commoninc = incoming = None
6653 source = sbranch = sother = commoninc = incoming = None
6653
6654
6654 def getoutgoing():
6655 def getoutgoing():
6655 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
6656 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
6656 dbranch = branches[0]
6657 dbranch = branches[0]
6657 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
6658 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
6658 if source != dest:
6659 if source != dest:
6659 try:
6660 try:
6660 dother = hg.peer(repo, {}, dest)
6661 dother = hg.peer(repo, {}, dest)
6661 except error.RepoError:
6662 except error.RepoError:
6662 if opts.get('remote'):
6663 if opts.get('remote'):
6663 raise
6664 raise
6664 return dest, dbranch, None, None
6665 return dest, dbranch, None, None
6665 ui.debug('comparing with %s\n' % util.hidepassword(dest))
6666 ui.debug('comparing with %s\n' % util.hidepassword(dest))
6666 elif sother is None:
6667 elif sother is None:
6667 # there is no explicit destination peer, but source one is invalid
6668 # there is no explicit destination peer, but source one is invalid
6668 return dest, dbranch, None, None
6669 return dest, dbranch, None, None
6669 else:
6670 else:
6670 dother = sother
6671 dother = sother
6671 if (source != dest or (sbranch is not None and sbranch != dbranch)):
6672 if (source != dest or (sbranch is not None and sbranch != dbranch)):
6672 common = None
6673 common = None
6673 else:
6674 else:
6674 common = commoninc
6675 common = commoninc
6675 if revs:
6676 if revs:
6676 revs = [repo.lookup(rev) for rev in revs]
6677 revs = [repo.lookup(rev) for rev in revs]
6677 repo.ui.pushbuffer()
6678 repo.ui.pushbuffer()
6678 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
6679 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
6679 commoninc=common)
6680 commoninc=common)
6680 repo.ui.popbuffer()
6681 repo.ui.popbuffer()
6681 return dest, dbranch, dother, outgoing
6682 return dest, dbranch, dother, outgoing
6682
6683
6683 if needsoutgoing:
6684 if needsoutgoing:
6684 dest, dbranch, dother, outgoing = getoutgoing()
6685 dest, dbranch, dother, outgoing = getoutgoing()
6685 else:
6686 else:
6686 dest = dbranch = dother = outgoing = None
6687 dest = dbranch = dother = outgoing = None
6687
6688
6688 if opts.get('remote'):
6689 if opts.get('remote'):
6689 t = []
6690 t = []
6690 if incoming:
6691 if incoming:
6691 t.append(_('1 or more incoming'))
6692 t.append(_('1 or more incoming'))
6692 o = outgoing.missing
6693 o = outgoing.missing
6693 if o:
6694 if o:
6694 t.append(_('%d outgoing') % len(o))
6695 t.append(_('%d outgoing') % len(o))
6695 other = dother or sother
6696 other = dother or sother
6696 if 'bookmarks' in other.listkeys('namespaces'):
6697 if 'bookmarks' in other.listkeys('namespaces'):
6697 counts = bookmarks.summary(repo, other)
6698 counts = bookmarks.summary(repo, other)
6698 if counts[0] > 0:
6699 if counts[0] > 0:
6699 t.append(_('%d incoming bookmarks') % counts[0])
6700 t.append(_('%d incoming bookmarks') % counts[0])
6700 if counts[1] > 0:
6701 if counts[1] > 0:
6701 t.append(_('%d outgoing bookmarks') % counts[1])
6702 t.append(_('%d outgoing bookmarks') % counts[1])
6702
6703
6703 if t:
6704 if t:
6704 # i18n: column positioning for "hg summary"
6705 # i18n: column positioning for "hg summary"
6705 ui.write(_('remote: %s\n') % (', '.join(t)))
6706 ui.write(_('remote: %s\n') % (', '.join(t)))
6706 else:
6707 else:
6707 # i18n: column positioning for "hg summary"
6708 # i18n: column positioning for "hg summary"
6708 ui.status(_('remote: (synced)\n'))
6709 ui.status(_('remote: (synced)\n'))
6709
6710
6710 cmdutil.summaryremotehooks(ui, repo, opts,
6711 cmdutil.summaryremotehooks(ui, repo, opts,
6711 ((source, sbranch, sother, commoninc),
6712 ((source, sbranch, sother, commoninc),
6712 (dest, dbranch, dother, outgoing)))
6713 (dest, dbranch, dother, outgoing)))
6713
6714
6714 @command('tag',
6715 @command('tag',
6715 [('f', 'force', None, _('force tag')),
6716 [('f', 'force', None, _('force tag')),
6716 ('l', 'local', None, _('make the tag local')),
6717 ('l', 'local', None, _('make the tag local')),
6717 ('r', 'rev', '', _('revision to tag'), _('REV')),
6718 ('r', 'rev', '', _('revision to tag'), _('REV')),
6718 ('', 'remove', None, _('remove a tag')),
6719 ('', 'remove', None, _('remove a tag')),
6719 # -l/--local is already there, commitopts cannot be used
6720 # -l/--local is already there, commitopts cannot be used
6720 ('e', 'edit', None, _('invoke editor on commit messages')),
6721 ('e', 'edit', None, _('invoke editor on commit messages')),
6721 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6722 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6722 ] + commitopts2,
6723 ] + commitopts2,
6723 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6724 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6724 def tag(ui, repo, name1, *names, **opts):
6725 def tag(ui, repo, name1, *names, **opts):
6725 """add one or more tags for the current or given revision
6726 """add one or more tags for the current or given revision
6726
6727
6727 Name a particular revision using <name>.
6728 Name a particular revision using <name>.
6728
6729
6729 Tags are used to name particular revisions of the repository and are
6730 Tags are used to name particular revisions of the repository and are
6730 very useful to compare different revisions, to go back to significant
6731 very useful to compare different revisions, to go back to significant
6731 earlier versions or to mark branch points as releases, etc. Changing
6732 earlier versions or to mark branch points as releases, etc. Changing
6732 an existing tag is normally disallowed; use -f/--force to override.
6733 an existing tag is normally disallowed; use -f/--force to override.
6733
6734
6734 If no revision is given, the parent of the working directory is
6735 If no revision is given, the parent of the working directory is
6735 used.
6736 used.
6736
6737
6737 To facilitate version control, distribution, and merging of tags,
6738 To facilitate version control, distribution, and merging of tags,
6738 they are stored as a file named ".hgtags" which is managed similarly
6739 they are stored as a file named ".hgtags" which is managed similarly
6739 to other project files and can be hand-edited if necessary. This
6740 to other project files and can be hand-edited if necessary. This
6740 also means that tagging creates a new commit. The file
6741 also means that tagging creates a new commit. The file
6741 ".hg/localtags" is used for local tags (not shared among
6742 ".hg/localtags" is used for local tags (not shared among
6742 repositories).
6743 repositories).
6743
6744
6744 Tag commits are usually made at the head of a branch. If the parent
6745 Tag commits are usually made at the head of a branch. If the parent
6745 of the working directory is not a branch head, :hg:`tag` aborts; use
6746 of the working directory is not a branch head, :hg:`tag` aborts; use
6746 -f/--force to force the tag commit to be based on a non-head
6747 -f/--force to force the tag commit to be based on a non-head
6747 changeset.
6748 changeset.
6748
6749
6749 See :hg:`help dates` for a list of formats valid for -d/--date.
6750 See :hg:`help dates` for a list of formats valid for -d/--date.
6750
6751
6751 Since tag names have priority over branch names during revision
6752 Since tag names have priority over branch names during revision
6752 lookup, using an existing branch name as a tag name is discouraged.
6753 lookup, using an existing branch name as a tag name is discouraged.
6753
6754
6754 Returns 0 on success.
6755 Returns 0 on success.
6755 """
6756 """
6756 wlock = lock = None
6757 wlock = lock = None
6757 try:
6758 try:
6758 wlock = repo.wlock()
6759 wlock = repo.wlock()
6759 lock = repo.lock()
6760 lock = repo.lock()
6760 rev_ = "."
6761 rev_ = "."
6761 names = [t.strip() for t in (name1,) + names]
6762 names = [t.strip() for t in (name1,) + names]
6762 if len(names) != len(set(names)):
6763 if len(names) != len(set(names)):
6763 raise error.Abort(_('tag names must be unique'))
6764 raise error.Abort(_('tag names must be unique'))
6764 for n in names:
6765 for n in names:
6765 scmutil.checknewlabel(repo, n, 'tag')
6766 scmutil.checknewlabel(repo, n, 'tag')
6766 if not n:
6767 if not n:
6767 raise error.Abort(_('tag names cannot consist entirely of '
6768 raise error.Abort(_('tag names cannot consist entirely of '
6768 'whitespace'))
6769 'whitespace'))
6769 if opts.get('rev') and opts.get('remove'):
6770 if opts.get('rev') and opts.get('remove'):
6770 raise error.Abort(_("--rev and --remove are incompatible"))
6771 raise error.Abort(_("--rev and --remove are incompatible"))
6771 if opts.get('rev'):
6772 if opts.get('rev'):
6772 rev_ = opts['rev']
6773 rev_ = opts['rev']
6773 message = opts.get('message')
6774 message = opts.get('message')
6774 if opts.get('remove'):
6775 if opts.get('remove'):
6775 if opts.get('local'):
6776 if opts.get('local'):
6776 expectedtype = 'local'
6777 expectedtype = 'local'
6777 else:
6778 else:
6778 expectedtype = 'global'
6779 expectedtype = 'global'
6779
6780
6780 for n in names:
6781 for n in names:
6781 if not repo.tagtype(n):
6782 if not repo.tagtype(n):
6782 raise error.Abort(_("tag '%s' does not exist") % n)
6783 raise error.Abort(_("tag '%s' does not exist") % n)
6783 if repo.tagtype(n) != expectedtype:
6784 if repo.tagtype(n) != expectedtype:
6784 if expectedtype == 'global':
6785 if expectedtype == 'global':
6785 raise error.Abort(_("tag '%s' is not a global tag") % n)
6786 raise error.Abort(_("tag '%s' is not a global tag") % n)
6786 else:
6787 else:
6787 raise error.Abort(_("tag '%s' is not a local tag") % n)
6788 raise error.Abort(_("tag '%s' is not a local tag") % n)
6788 rev_ = 'null'
6789 rev_ = 'null'
6789 if not message:
6790 if not message:
6790 # we don't translate commit messages
6791 # we don't translate commit messages
6791 message = 'Removed tag %s' % ', '.join(names)
6792 message = 'Removed tag %s' % ', '.join(names)
6792 elif not opts.get('force'):
6793 elif not opts.get('force'):
6793 for n in names:
6794 for n in names:
6794 if n in repo.tags():
6795 if n in repo.tags():
6795 raise error.Abort(_("tag '%s' already exists "
6796 raise error.Abort(_("tag '%s' already exists "
6796 "(use -f to force)") % n)
6797 "(use -f to force)") % n)
6797 if not opts.get('local'):
6798 if not opts.get('local'):
6798 p1, p2 = repo.dirstate.parents()
6799 p1, p2 = repo.dirstate.parents()
6799 if p2 != nullid:
6800 if p2 != nullid:
6800 raise error.Abort(_('uncommitted merge'))
6801 raise error.Abort(_('uncommitted merge'))
6801 bheads = repo.branchheads()
6802 bheads = repo.branchheads()
6802 if not opts.get('force') and bheads and p1 not in bheads:
6803 if not opts.get('force') and bheads and p1 not in bheads:
6803 raise error.Abort(_('working directory is not at a branch head '
6804 raise error.Abort(_('working directory is not at a branch head '
6804 '(use -f to force)'))
6805 '(use -f to force)'))
6805 r = scmutil.revsingle(repo, rev_).node()
6806 r = scmutil.revsingle(repo, rev_).node()
6806
6807
6807 if not message:
6808 if not message:
6808 # we don't translate commit messages
6809 # we don't translate commit messages
6809 message = ('Added tag %s for changeset %s' %
6810 message = ('Added tag %s for changeset %s' %
6810 (', '.join(names), short(r)))
6811 (', '.join(names), short(r)))
6811
6812
6812 date = opts.get('date')
6813 date = opts.get('date')
6813 if date:
6814 if date:
6814 date = util.parsedate(date)
6815 date = util.parsedate(date)
6815
6816
6816 if opts.get('remove'):
6817 if opts.get('remove'):
6817 editform = 'tag.remove'
6818 editform = 'tag.remove'
6818 else:
6819 else:
6819 editform = 'tag.add'
6820 editform = 'tag.add'
6820 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6821 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6821
6822
6822 # don't allow tagging the null rev
6823 # don't allow tagging the null rev
6823 if (not opts.get('remove') and
6824 if (not opts.get('remove') and
6824 scmutil.revsingle(repo, rev_).rev() == nullrev):
6825 scmutil.revsingle(repo, rev_).rev() == nullrev):
6825 raise error.Abort(_("cannot tag null revision"))
6826 raise error.Abort(_("cannot tag null revision"))
6826
6827
6827 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6828 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6828 editor=editor)
6829 editor=editor)
6829 finally:
6830 finally:
6830 release(lock, wlock)
6831 release(lock, wlock)
6831
6832
6832 @command('tags', formatteropts, '')
6833 @command('tags', formatteropts, '')
6833 def tags(ui, repo, **opts):
6834 def tags(ui, repo, **opts):
6834 """list repository tags
6835 """list repository tags
6835
6836
6836 This lists both regular and local tags. When the -v/--verbose
6837 This lists both regular and local tags. When the -v/--verbose
6837 switch is used, a third column "local" is printed for local tags.
6838 switch is used, a third column "local" is printed for local tags.
6838 When the -q/--quiet switch is used, only the tag name is printed.
6839 When the -q/--quiet switch is used, only the tag name is printed.
6839
6840
6840 Returns 0 on success.
6841 Returns 0 on success.
6841 """
6842 """
6842
6843
6843 fm = ui.formatter('tags', opts)
6844 fm = ui.formatter('tags', opts)
6844 hexfunc = fm.hexfunc
6845 hexfunc = fm.hexfunc
6845 tagtype = ""
6846 tagtype = ""
6846
6847
6847 for t, n in reversed(repo.tagslist()):
6848 for t, n in reversed(repo.tagslist()):
6848 hn = hexfunc(n)
6849 hn = hexfunc(n)
6849 label = 'tags.normal'
6850 label = 'tags.normal'
6850 tagtype = ''
6851 tagtype = ''
6851 if repo.tagtype(t) == 'local':
6852 if repo.tagtype(t) == 'local':
6852 label = 'tags.local'
6853 label = 'tags.local'
6853 tagtype = 'local'
6854 tagtype = 'local'
6854
6855
6855 fm.startitem()
6856 fm.startitem()
6856 fm.write('tag', '%s', t, label=label)
6857 fm.write('tag', '%s', t, label=label)
6857 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6858 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6858 fm.condwrite(not ui.quiet, 'rev node', fmt,
6859 fm.condwrite(not ui.quiet, 'rev node', fmt,
6859 repo.changelog.rev(n), hn, label=label)
6860 repo.changelog.rev(n), hn, label=label)
6860 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6861 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6861 tagtype, label=label)
6862 tagtype, label=label)
6862 fm.plain('\n')
6863 fm.plain('\n')
6863 fm.end()
6864 fm.end()
6864
6865
6865 @command('tip',
6866 @command('tip',
6866 [('p', 'patch', None, _('show patch')),
6867 [('p', 'patch', None, _('show patch')),
6867 ('g', 'git', None, _('use git extended diff format')),
6868 ('g', 'git', None, _('use git extended diff format')),
6868 ] + templateopts,
6869 ] + templateopts,
6869 _('[-p] [-g]'))
6870 _('[-p] [-g]'))
6870 def tip(ui, repo, **opts):
6871 def tip(ui, repo, **opts):
6871 """show the tip revision (DEPRECATED)
6872 """show the tip revision (DEPRECATED)
6872
6873
6873 The tip revision (usually just called the tip) is the changeset
6874 The tip revision (usually just called the tip) is the changeset
6874 most recently added to the repository (and therefore the most
6875 most recently added to the repository (and therefore the most
6875 recently changed head).
6876 recently changed head).
6876
6877
6877 If you have just made a commit, that commit will be the tip. If
6878 If you have just made a commit, that commit will be the tip. If
6878 you have just pulled changes from another repository, the tip of
6879 you have just pulled changes from another repository, the tip of
6879 that repository becomes the current tip. The "tip" tag is special
6880 that repository becomes the current tip. The "tip" tag is special
6880 and cannot be renamed or assigned to a different changeset.
6881 and cannot be renamed or assigned to a different changeset.
6881
6882
6882 This command is deprecated, please use :hg:`heads` instead.
6883 This command is deprecated, please use :hg:`heads` instead.
6883
6884
6884 Returns 0 on success.
6885 Returns 0 on success.
6885 """
6886 """
6886 displayer = cmdutil.show_changeset(ui, repo, opts)
6887 displayer = cmdutil.show_changeset(ui, repo, opts)
6887 displayer.show(repo['tip'])
6888 displayer.show(repo['tip'])
6888 displayer.close()
6889 displayer.close()
6889
6890
6890 @command('unbundle',
6891 @command('unbundle',
6891 [('u', 'update', None,
6892 [('u', 'update', None,
6892 _('update to new branch head if changesets were unbundled'))],
6893 _('update to new branch head if changesets were unbundled'))],
6893 _('[-u] FILE...'))
6894 _('[-u] FILE...'))
6894 def unbundle(ui, repo, fname1, *fnames, **opts):
6895 def unbundle(ui, repo, fname1, *fnames, **opts):
6895 """apply one or more changegroup files
6896 """apply one or more changegroup files
6896
6897
6897 Apply one or more compressed changegroup files generated by the
6898 Apply one or more compressed changegroup files generated by the
6898 bundle command.
6899 bundle command.
6899
6900
6900 Returns 0 on success, 1 if an update has unresolved files.
6901 Returns 0 on success, 1 if an update has unresolved files.
6901 """
6902 """
6902 fnames = (fname1,) + fnames
6903 fnames = (fname1,) + fnames
6903
6904
6904 with repo.lock():
6905 with repo.lock():
6905 for fname in fnames:
6906 for fname in fnames:
6906 f = hg.openpath(ui, fname)
6907 f = hg.openpath(ui, fname)
6907 gen = exchange.readbundle(ui, f, fname)
6908 gen = exchange.readbundle(ui, f, fname)
6908 if isinstance(gen, bundle2.unbundle20):
6909 if isinstance(gen, bundle2.unbundle20):
6909 tr = repo.transaction('unbundle')
6910 tr = repo.transaction('unbundle')
6910 try:
6911 try:
6911 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
6912 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
6912 url='bundle:' + fname)
6913 url='bundle:' + fname)
6913 tr.close()
6914 tr.close()
6914 except error.BundleUnknownFeatureError as exc:
6915 except error.BundleUnknownFeatureError as exc:
6915 raise error.Abort(_('%s: unknown bundle feature, %s')
6916 raise error.Abort(_('%s: unknown bundle feature, %s')
6916 % (fname, exc),
6917 % (fname, exc),
6917 hint=_("see https://mercurial-scm.org/"
6918 hint=_("see https://mercurial-scm.org/"
6918 "wiki/BundleFeature for more "
6919 "wiki/BundleFeature for more "
6919 "information"))
6920 "information"))
6920 finally:
6921 finally:
6921 if tr:
6922 if tr:
6922 tr.release()
6923 tr.release()
6923 changes = [r.get('return', 0)
6924 changes = [r.get('return', 0)
6924 for r in op.records['changegroup']]
6925 for r in op.records['changegroup']]
6925 modheads = changegroup.combineresults(changes)
6926 modheads = changegroup.combineresults(changes)
6926 elif isinstance(gen, streamclone.streamcloneapplier):
6927 elif isinstance(gen, streamclone.streamcloneapplier):
6927 raise error.Abort(
6928 raise error.Abort(
6928 _('packed bundles cannot be applied with '
6929 _('packed bundles cannot be applied with '
6929 '"hg unbundle"'),
6930 '"hg unbundle"'),
6930 hint=_('use "hg debugapplystreamclonebundle"'))
6931 hint=_('use "hg debugapplystreamclonebundle"'))
6931 else:
6932 else:
6932 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
6933 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
6933
6934
6934 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
6935 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
6935
6936
6936 @command('^update|up|checkout|co',
6937 @command('^update|up|checkout|co',
6937 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6938 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6938 ('c', 'check', None, _('require clean working directory')),
6939 ('c', 'check', None, _('require clean working directory')),
6939 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6940 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6940 ('r', 'rev', '', _('revision'), _('REV'))
6941 ('r', 'rev', '', _('revision'), _('REV'))
6941 ] + mergetoolopts,
6942 ] + mergetoolopts,
6942 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6943 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6943 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6944 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6944 tool=None):
6945 tool=None):
6945 """update working directory (or switch revisions)
6946 """update working directory (or switch revisions)
6946
6947
6947 Update the repository's working directory to the specified
6948 Update the repository's working directory to the specified
6948 changeset. If no changeset is specified, update to the tip of the
6949 changeset. If no changeset is specified, update to the tip of the
6949 current named branch and move the active bookmark (see :hg:`help
6950 current named branch and move the active bookmark (see :hg:`help
6950 bookmarks`).
6951 bookmarks`).
6951
6952
6952 Update sets the working directory's parent revision to the specified
6953 Update sets the working directory's parent revision to the specified
6953 changeset (see :hg:`help parents`).
6954 changeset (see :hg:`help parents`).
6954
6955
6955 If the changeset is not a descendant or ancestor of the working
6956 If the changeset is not a descendant or ancestor of the working
6956 directory's parent, the update is aborted. With the -c/--check
6957 directory's parent, the update is aborted. With the -c/--check
6957 option, the working directory is checked for uncommitted changes; if
6958 option, the working directory is checked for uncommitted changes; if
6958 none are found, the working directory is updated to the specified
6959 none are found, the working directory is updated to the specified
6959 changeset.
6960 changeset.
6960
6961
6961 .. container:: verbose
6962 .. container:: verbose
6962
6963
6963 The following rules apply when the working directory contains
6964 The following rules apply when the working directory contains
6964 uncommitted changes:
6965 uncommitted changes:
6965
6966
6966 1. If neither -c/--check nor -C/--clean is specified, and if
6967 1. If neither -c/--check nor -C/--clean is specified, and if
6967 the requested changeset is an ancestor or descendant of
6968 the requested changeset is an ancestor or descendant of
6968 the working directory's parent, the uncommitted changes
6969 the working directory's parent, the uncommitted changes
6969 are merged into the requested changeset and the merged
6970 are merged into the requested changeset and the merged
6970 result is left uncommitted. If the requested changeset is
6971 result is left uncommitted. If the requested changeset is
6971 not an ancestor or descendant (that is, it is on another
6972 not an ancestor or descendant (that is, it is on another
6972 branch), the update is aborted and the uncommitted changes
6973 branch), the update is aborted and the uncommitted changes
6973 are preserved.
6974 are preserved.
6974
6975
6975 2. With the -c/--check option, the update is aborted and the
6976 2. With the -c/--check option, the update is aborted and the
6976 uncommitted changes are preserved.
6977 uncommitted changes are preserved.
6977
6978
6978 3. With the -C/--clean option, uncommitted changes are discarded and
6979 3. With the -C/--clean option, uncommitted changes are discarded and
6979 the working directory is updated to the requested changeset.
6980 the working directory is updated to the requested changeset.
6980
6981
6981 To cancel an uncommitted merge (and lose your changes), use
6982 To cancel an uncommitted merge (and lose your changes), use
6982 :hg:`update --clean .`.
6983 :hg:`update --clean .`.
6983
6984
6984 Use null as the changeset to remove the working directory (like
6985 Use null as the changeset to remove the working directory (like
6985 :hg:`clone -U`).
6986 :hg:`clone -U`).
6986
6987
6987 If you want to revert just one file to an older revision, use
6988 If you want to revert just one file to an older revision, use
6988 :hg:`revert [-r REV] NAME`.
6989 :hg:`revert [-r REV] NAME`.
6989
6990
6990 See :hg:`help dates` for a list of formats valid for -d/--date.
6991 See :hg:`help dates` for a list of formats valid for -d/--date.
6991
6992
6992 Returns 0 on success, 1 if there are unresolved files.
6993 Returns 0 on success, 1 if there are unresolved files.
6993 """
6994 """
6994 if rev and node:
6995 if rev and node:
6995 raise error.Abort(_("please specify just one revision"))
6996 raise error.Abort(_("please specify just one revision"))
6996
6997
6997 if rev is None or rev == '':
6998 if rev is None or rev == '':
6998 rev = node
6999 rev = node
6999
7000
7000 if date and rev is not None:
7001 if date and rev is not None:
7001 raise error.Abort(_("you can't specify a revision and a date"))
7002 raise error.Abort(_("you can't specify a revision and a date"))
7002
7003
7003 if check and clean:
7004 if check and clean:
7004 raise error.Abort(_("cannot specify both -c/--check and -C/--clean"))
7005 raise error.Abort(_("cannot specify both -c/--check and -C/--clean"))
7005
7006
7006 with repo.wlock():
7007 with repo.wlock():
7007 cmdutil.clearunfinished(repo)
7008 cmdutil.clearunfinished(repo)
7008
7009
7009 if date:
7010 if date:
7010 rev = cmdutil.finddate(ui, repo, date)
7011 rev = cmdutil.finddate(ui, repo, date)
7011
7012
7012 # if we defined a bookmark, we have to remember the original name
7013 # if we defined a bookmark, we have to remember the original name
7013 brev = rev
7014 brev = rev
7014 rev = scmutil.revsingle(repo, rev, rev).rev()
7015 rev = scmutil.revsingle(repo, rev, rev).rev()
7015
7016
7016 if check:
7017 if check:
7017 cmdutil.bailifchanged(repo, merge=False)
7018 cmdutil.bailifchanged(repo, merge=False)
7018
7019
7019 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
7020 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
7020
7021
7021 return hg.updatetotally(ui, repo, rev, brev, clean=clean, check=check)
7022 return hg.updatetotally(ui, repo, rev, brev, clean=clean, check=check)
7022
7023
7023 @command('verify', [])
7024 @command('verify', [])
7024 def verify(ui, repo):
7025 def verify(ui, repo):
7025 """verify the integrity of the repository
7026 """verify the integrity of the repository
7026
7027
7027 Verify the integrity of the current repository.
7028 Verify the integrity of the current repository.
7028
7029
7029 This will perform an extensive check of the repository's
7030 This will perform an extensive check of the repository's
7030 integrity, validating the hashes and checksums of each entry in
7031 integrity, validating the hashes and checksums of each entry in
7031 the changelog, manifest, and tracked files, as well as the
7032 the changelog, manifest, and tracked files, as well as the
7032 integrity of their crosslinks and indices.
7033 integrity of their crosslinks and indices.
7033
7034
7034 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7035 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7035 for more information about recovery from corruption of the
7036 for more information about recovery from corruption of the
7036 repository.
7037 repository.
7037
7038
7038 Returns 0 on success, 1 if errors are encountered.
7039 Returns 0 on success, 1 if errors are encountered.
7039 """
7040 """
7040 return hg.verify(repo)
7041 return hg.verify(repo)
7041
7042
7042 @command('version', [] + formatteropts, norepo=True)
7043 @command('version', [] + formatteropts, norepo=True)
7043 def version_(ui, **opts):
7044 def version_(ui, **opts):
7044 """output version and copyright information"""
7045 """output version and copyright information"""
7045 fm = ui.formatter("version", opts)
7046 fm = ui.formatter("version", opts)
7046 fm.startitem()
7047 fm.startitem()
7047 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
7048 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
7048 util.version())
7049 util.version())
7049 license = _(
7050 license = _(
7050 "(see https://mercurial-scm.org for more information)\n"
7051 "(see https://mercurial-scm.org for more information)\n"
7051 "\nCopyright (C) 2005-2016 Matt Mackall and others\n"
7052 "\nCopyright (C) 2005-2016 Matt Mackall and others\n"
7052 "This is free software; see the source for copying conditions. "
7053 "This is free software; see the source for copying conditions. "
7053 "There is NO\nwarranty; "
7054 "There is NO\nwarranty; "
7054 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7055 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7055 )
7056 )
7056 if not ui.quiet:
7057 if not ui.quiet:
7057 fm.plain(license)
7058 fm.plain(license)
7058
7059
7059 if ui.verbose:
7060 if ui.verbose:
7060 fm.plain(_("\nEnabled extensions:\n\n"))
7061 fm.plain(_("\nEnabled extensions:\n\n"))
7061 # format names and versions into columns
7062 # format names and versions into columns
7062 names = []
7063 names = []
7063 vers = []
7064 vers = []
7064 isinternals = []
7065 isinternals = []
7065 for name, module in extensions.extensions():
7066 for name, module in extensions.extensions():
7066 names.append(name)
7067 names.append(name)
7067 vers.append(extensions.moduleversion(module) or None)
7068 vers.append(extensions.moduleversion(module) or None)
7068 isinternals.append(extensions.ismoduleinternal(module))
7069 isinternals.append(extensions.ismoduleinternal(module))
7069 fn = fm.nested("extensions")
7070 fn = fm.nested("extensions")
7070 if names:
7071 if names:
7071 namefmt = " %%-%ds " % max(len(n) for n in names)
7072 namefmt = " %%-%ds " % max(len(n) for n in names)
7072 places = [_("external"), _("internal")]
7073 places = [_("external"), _("internal")]
7073 for n, v, p in zip(names, vers, isinternals):
7074 for n, v, p in zip(names, vers, isinternals):
7074 fn.startitem()
7075 fn.startitem()
7075 fn.condwrite(ui.verbose, "name", namefmt, n)
7076 fn.condwrite(ui.verbose, "name", namefmt, n)
7076 if ui.verbose:
7077 if ui.verbose:
7077 fn.plain("%s " % places[p])
7078 fn.plain("%s " % places[p])
7078 fn.data(bundled=p)
7079 fn.data(bundled=p)
7079 fn.condwrite(ui.verbose and v, "ver", "%s", v)
7080 fn.condwrite(ui.verbose and v, "ver", "%s", v)
7080 if ui.verbose:
7081 if ui.verbose:
7081 fn.plain("\n")
7082 fn.plain("\n")
7082 fn.end()
7083 fn.end()
7083 fm.end()
7084 fm.end()
7084
7085
7085 def loadcmdtable(ui, name, cmdtable):
7086 def loadcmdtable(ui, name, cmdtable):
7086 """Load command functions from specified cmdtable
7087 """Load command functions from specified cmdtable
7087 """
7088 """
7088 overrides = [cmd for cmd in cmdtable if cmd in table]
7089 overrides = [cmd for cmd in cmdtable if cmd in table]
7089 if overrides:
7090 if overrides:
7090 ui.warn(_("extension '%s' overrides commands: %s\n")
7091 ui.warn(_("extension '%s' overrides commands: %s\n")
7091 % (name, " ".join(overrides)))
7092 % (name, " ".join(overrides)))
7092 table.update(cmdtable)
7093 table.update(cmdtable)
@@ -1,55 +1,55 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2
2
3 from __future__ import absolute_import
3 from __future__ import absolute_import
4
4
5 """
5 """
6 Small and dumb HTTP server for use in tests.
6 Small and dumb HTTP server for use in tests.
7 """
7 """
8
8
9 import optparse
9 import optparse
10 import signal
10 import signal
11 import sys
11 import sys
12
12
13 from mercurial import (
13 from mercurial import (
14 cmdutil,
14 server,
15 util,
15 util,
16 )
16 )
17
17
18 httpserver = util.httpserver
18 httpserver = util.httpserver
19 OptionParser = optparse.OptionParser
19 OptionParser = optparse.OptionParser
20
20
21 class simplehttpservice(object):
21 class simplehttpservice(object):
22 def __init__(self, host, port):
22 def __init__(self, host, port):
23 self.address = (host, port)
23 self.address = (host, port)
24 def init(self):
24 def init(self):
25 self.httpd = httpserver.httpserver(
25 self.httpd = httpserver.httpserver(
26 self.address, httpserver.simplehttprequesthandler)
26 self.address, httpserver.simplehttprequesthandler)
27 def run(self):
27 def run(self):
28 self.httpd.serve_forever()
28 self.httpd.serve_forever()
29
29
30 if __name__ == '__main__':
30 if __name__ == '__main__':
31 parser = OptionParser()
31 parser = OptionParser()
32 parser.add_option('-p', '--port', dest='port', type='int', default=8000,
32 parser.add_option('-p', '--port', dest='port', type='int', default=8000,
33 help='TCP port to listen on', metavar='PORT')
33 help='TCP port to listen on', metavar='PORT')
34 parser.add_option('-H', '--host', dest='host', default='localhost',
34 parser.add_option('-H', '--host', dest='host', default='localhost',
35 help='hostname or IP to listen on', metavar='HOST')
35 help='hostname or IP to listen on', metavar='HOST')
36 parser.add_option('--pid', dest='pid',
36 parser.add_option('--pid', dest='pid',
37 help='file name where the PID of the server is stored')
37 help='file name where the PID of the server is stored')
38 parser.add_option('-f', '--foreground', dest='foreground',
38 parser.add_option('-f', '--foreground', dest='foreground',
39 action='store_true',
39 action='store_true',
40 help='do not start the HTTP server in the background')
40 help='do not start the HTTP server in the background')
41 parser.add_option('--daemon-postexec', action='append')
41 parser.add_option('--daemon-postexec', action='append')
42
42
43 (options, args) = parser.parse_args()
43 (options, args) = parser.parse_args()
44
44
45 signal.signal(signal.SIGTERM, lambda x, y: sys.exit(0))
45 signal.signal(signal.SIGTERM, lambda x, y: sys.exit(0))
46
46
47 if options.foreground and options.pid:
47 if options.foreground and options.pid:
48 parser.error("options --pid and --foreground are mutually exclusive")
48 parser.error("options --pid and --foreground are mutually exclusive")
49
49
50 opts = {'pid_file': options.pid,
50 opts = {'pid_file': options.pid,
51 'daemon': not options.foreground,
51 'daemon': not options.foreground,
52 'daemon_postexec': options.daemon_postexec}
52 'daemon_postexec': options.daemon_postexec}
53 service = simplehttpservice(options.host, options.port)
53 service = simplehttpservice(options.host, options.port)
54 cmdutil.service(opts, initfn=service.init, runfn=service.run,
54 server.runservice(opts, initfn=service.init, runfn=service.run,
55 runargs=[sys.executable, __file__] + sys.argv[1:])
55 runargs=[sys.executable, __file__] + sys.argv[1:])
@@ -1,82 +1,82 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2
2
3 """dummy SMTP server for use in tests"""
3 """dummy SMTP server for use in tests"""
4
4
5 from __future__ import absolute_import
5 from __future__ import absolute_import
6
6
7 import asyncore
7 import asyncore
8 import optparse
8 import optparse
9 import smtpd
9 import smtpd
10 import ssl
10 import ssl
11 import sys
11 import sys
12
12
13 from mercurial import (
13 from mercurial import (
14 cmdutil,
14 server,
15 sslutil,
15 sslutil,
16 ui as uimod,
16 ui as uimod,
17 )
17 )
18
18
19 def log(msg):
19 def log(msg):
20 sys.stdout.write(msg)
20 sys.stdout.write(msg)
21 sys.stdout.flush()
21 sys.stdout.flush()
22
22
23 class dummysmtpserver(smtpd.SMTPServer):
23 class dummysmtpserver(smtpd.SMTPServer):
24 def __init__(self, localaddr):
24 def __init__(self, localaddr):
25 smtpd.SMTPServer.__init__(self, localaddr, remoteaddr=None)
25 smtpd.SMTPServer.__init__(self, localaddr, remoteaddr=None)
26
26
27 def process_message(self, peer, mailfrom, rcpttos, data):
27 def process_message(self, peer, mailfrom, rcpttos, data):
28 log('%s from=%s to=%s\n' % (peer[0], mailfrom, ', '.join(rcpttos)))
28 log('%s from=%s to=%s\n' % (peer[0], mailfrom, ', '.join(rcpttos)))
29
29
30 class dummysmtpsecureserver(dummysmtpserver):
30 class dummysmtpsecureserver(dummysmtpserver):
31 def __init__(self, localaddr, certfile):
31 def __init__(self, localaddr, certfile):
32 dummysmtpserver.__init__(self, localaddr)
32 dummysmtpserver.__init__(self, localaddr)
33 self._certfile = certfile
33 self._certfile = certfile
34
34
35 def handle_accept(self):
35 def handle_accept(self):
36 pair = self.accept()
36 pair = self.accept()
37 if not pair:
37 if not pair:
38 return
38 return
39 conn, addr = pair
39 conn, addr = pair
40 ui = uimod.ui()
40 ui = uimod.ui()
41 try:
41 try:
42 # wrap_socket() would block, but we don't care
42 # wrap_socket() would block, but we don't care
43 conn = sslutil.wrapserversocket(conn, ui, certfile=self._certfile)
43 conn = sslutil.wrapserversocket(conn, ui, certfile=self._certfile)
44 except ssl.SSLError:
44 except ssl.SSLError:
45 log('%s ssl error\n' % addr[0])
45 log('%s ssl error\n' % addr[0])
46 conn.close()
46 conn.close()
47 return
47 return
48 smtpd.SMTPChannel(self, conn, addr)
48 smtpd.SMTPChannel(self, conn, addr)
49
49
50 def run():
50 def run():
51 try:
51 try:
52 asyncore.loop()
52 asyncore.loop()
53 except KeyboardInterrupt:
53 except KeyboardInterrupt:
54 pass
54 pass
55
55
56 def main():
56 def main():
57 op = optparse.OptionParser()
57 op = optparse.OptionParser()
58 op.add_option('-d', '--daemon', action='store_true')
58 op.add_option('-d', '--daemon', action='store_true')
59 op.add_option('--daemon-postexec', action='append')
59 op.add_option('--daemon-postexec', action='append')
60 op.add_option('-p', '--port', type=int, default=8025)
60 op.add_option('-p', '--port', type=int, default=8025)
61 op.add_option('-a', '--address', default='localhost')
61 op.add_option('-a', '--address', default='localhost')
62 op.add_option('--pid-file', metavar='FILE')
62 op.add_option('--pid-file', metavar='FILE')
63 op.add_option('--tls', choices=['none', 'smtps'], default='none')
63 op.add_option('--tls', choices=['none', 'smtps'], default='none')
64 op.add_option('--certificate', metavar='FILE')
64 op.add_option('--certificate', metavar='FILE')
65
65
66 opts, args = op.parse_args()
66 opts, args = op.parse_args()
67 if opts.tls == 'smtps' and not opts.certificate:
67 if opts.tls == 'smtps' and not opts.certificate:
68 op.error('--certificate must be specified')
68 op.error('--certificate must be specified')
69
69
70 addr = (opts.address, opts.port)
70 addr = (opts.address, opts.port)
71 def init():
71 def init():
72 if opts.tls == 'none':
72 if opts.tls == 'none':
73 dummysmtpserver(addr)
73 dummysmtpserver(addr)
74 else:
74 else:
75 dummysmtpsecureserver(addr, opts.certificate)
75 dummysmtpsecureserver(addr, opts.certificate)
76 log('listening at %s:%d\n' % addr)
76 log('listening at %s:%d\n' % addr)
77
77
78 cmdutil.service(vars(opts), initfn=init, runfn=run,
78 server.runservice(vars(opts), initfn=init, runfn=run,
79 runargs=[sys.executable, __file__] + sys.argv[1:])
79 runargs=[sys.executable, __file__] + sys.argv[1:])
80
80
81 if __name__ == '__main__':
81 if __name__ == '__main__':
82 main()
82 main()
General Comments 0
You need to be logged in to leave comments. Login now