##// END OF EJS Templates
treemanifest: add --dir option to debug{revlog,data,index}...
Martin von Zweigbergk -
r25119:49c583ca default
parent child Browse files
Show More
@@ -1,3325 +1,3335 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 node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, errno, re, tempfile, cStringIO, shutil
10 import os, sys, errno, re, tempfile, cStringIO, shutil
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
12 import match as matchmod
12 import match as matchmod
13 import context, repair, graphmod, revset, phases, obsolete, pathutil
13 import context, repair, graphmod, revset, phases, obsolete, pathutil
14 import changelog
14 import changelog
15 import bookmarks
15 import bookmarks
16 import encoding
16 import encoding
17 import crecord as crecordmod
17 import crecord as crecordmod
18 import lock as lockmod
18 import lock as lockmod
19
19
20 def parsealiases(cmd):
20 def parsealiases(cmd):
21 return cmd.lstrip("^").split("|")
21 return cmd.lstrip("^").split("|")
22
22
23 def setupwrapcolorwrite(ui):
23 def setupwrapcolorwrite(ui):
24 # wrap ui.write so diff output can be labeled/colorized
24 # wrap ui.write so diff output can be labeled/colorized
25 def wrapwrite(orig, *args, **kw):
25 def wrapwrite(orig, *args, **kw):
26 label = kw.pop('label', '')
26 label = kw.pop('label', '')
27 for chunk, l in patch.difflabel(lambda: args):
27 for chunk, l in patch.difflabel(lambda: args):
28 orig(chunk, label=label + l)
28 orig(chunk, label=label + l)
29
29
30 oldwrite = ui.write
30 oldwrite = ui.write
31 def wrap(*args, **kwargs):
31 def wrap(*args, **kwargs):
32 return wrapwrite(oldwrite, *args, **kwargs)
32 return wrapwrite(oldwrite, *args, **kwargs)
33 setattr(ui, 'write', wrap)
33 setattr(ui, 'write', wrap)
34 return oldwrite
34 return oldwrite
35
35
36 def filterchunks(ui, originalhunks, usecurses, testfile):
36 def filterchunks(ui, originalhunks, usecurses, testfile):
37 if usecurses:
37 if usecurses:
38 if testfile:
38 if testfile:
39 recordfn = crecordmod.testdecorator(testfile,
39 recordfn = crecordmod.testdecorator(testfile,
40 crecordmod.testchunkselector)
40 crecordmod.testchunkselector)
41 else:
41 else:
42 recordfn = crecordmod.chunkselector
42 recordfn = crecordmod.chunkselector
43
43
44 return crecordmod.filterpatch(ui, originalhunks, recordfn)
44 return crecordmod.filterpatch(ui, originalhunks, recordfn)
45
45
46 else:
46 else:
47 return patch.filterpatch(ui, originalhunks)
47 return patch.filterpatch(ui, originalhunks)
48
48
49 def recordfilter(ui, originalhunks):
49 def recordfilter(ui, originalhunks):
50 usecurses = ui.configbool('experimental', 'crecord', False)
50 usecurses = ui.configbool('experimental', 'crecord', False)
51 testfile = ui.config('experimental', 'crecordtest', None)
51 testfile = ui.config('experimental', 'crecordtest', None)
52 oldwrite = setupwrapcolorwrite(ui)
52 oldwrite = setupwrapcolorwrite(ui)
53 try:
53 try:
54 newchunks = filterchunks(ui, originalhunks, usecurses, testfile)
54 newchunks = filterchunks(ui, originalhunks, usecurses, testfile)
55 finally:
55 finally:
56 ui.write = oldwrite
56 ui.write = oldwrite
57 return newchunks
57 return newchunks
58
58
59 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
59 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
60 filterfn, *pats, **opts):
60 filterfn, *pats, **opts):
61 import merge as mergemod
61 import merge as mergemod
62 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
62 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
63 ishunk = lambda x: isinstance(x, hunkclasses)
63 ishunk = lambda x: isinstance(x, hunkclasses)
64
64
65 if not ui.interactive():
65 if not ui.interactive():
66 raise util.Abort(_('running non-interactively, use %s instead') %
66 raise util.Abort(_('running non-interactively, use %s instead') %
67 cmdsuggest)
67 cmdsuggest)
68
68
69 # make sure username is set before going interactive
69 # make sure username is set before going interactive
70 if not opts.get('user'):
70 if not opts.get('user'):
71 ui.username() # raise exception, username not provided
71 ui.username() # raise exception, username not provided
72
72
73 def recordfunc(ui, repo, message, match, opts):
73 def recordfunc(ui, repo, message, match, opts):
74 """This is generic record driver.
74 """This is generic record driver.
75
75
76 Its job is to interactively filter local changes, and
76 Its job is to interactively filter local changes, and
77 accordingly prepare working directory into a state in which the
77 accordingly prepare working directory into a state in which the
78 job can be delegated to a non-interactive commit command such as
78 job can be delegated to a non-interactive commit command such as
79 'commit' or 'qrefresh'.
79 'commit' or 'qrefresh'.
80
80
81 After the actual job is done by non-interactive command, the
81 After the actual job is done by non-interactive command, the
82 working directory is restored to its original state.
82 working directory is restored to its original state.
83
83
84 In the end we'll record interesting changes, and everything else
84 In the end we'll record interesting changes, and everything else
85 will be left in place, so the user can continue working.
85 will be left in place, so the user can continue working.
86 """
86 """
87
87
88 checkunfinished(repo, commit=True)
88 checkunfinished(repo, commit=True)
89 merge = len(repo[None].parents()) > 1
89 merge = len(repo[None].parents()) > 1
90 if merge:
90 if merge:
91 raise util.Abort(_('cannot partially commit a merge '
91 raise util.Abort(_('cannot partially commit a merge '
92 '(use "hg commit" instead)'))
92 '(use "hg commit" instead)'))
93
93
94 status = repo.status(match=match)
94 status = repo.status(match=match)
95 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
95 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
96 diffopts.nodates = True
96 diffopts.nodates = True
97 diffopts.git = True
97 diffopts.git = True
98 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
98 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
99 originalchunks = patch.parsepatch(originaldiff)
99 originalchunks = patch.parsepatch(originaldiff)
100
100
101 # 1. filter patch, so we have intending-to apply subset of it
101 # 1. filter patch, so we have intending-to apply subset of it
102 try:
102 try:
103 chunks = filterfn(ui, originalchunks)
103 chunks = filterfn(ui, originalchunks)
104 except patch.PatchError, err:
104 except patch.PatchError, err:
105 raise util.Abort(_('error parsing patch: %s') % err)
105 raise util.Abort(_('error parsing patch: %s') % err)
106
106
107 # We need to keep a backup of files that have been newly added and
107 # We need to keep a backup of files that have been newly added and
108 # modified during the recording process because there is a previous
108 # modified during the recording process because there is a previous
109 # version without the edit in the workdir
109 # version without the edit in the workdir
110 newlyaddedandmodifiedfiles = set()
110 newlyaddedandmodifiedfiles = set()
111 for chunk in chunks:
111 for chunk in chunks:
112 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
112 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
113 originalchunks:
113 originalchunks:
114 newlyaddedandmodifiedfiles.add(chunk.header.filename())
114 newlyaddedandmodifiedfiles.add(chunk.header.filename())
115 contenders = set()
115 contenders = set()
116 for h in chunks:
116 for h in chunks:
117 try:
117 try:
118 contenders.update(set(h.files()))
118 contenders.update(set(h.files()))
119 except AttributeError:
119 except AttributeError:
120 pass
120 pass
121
121
122 changed = status.modified + status.added + status.removed
122 changed = status.modified + status.added + status.removed
123 newfiles = [f for f in changed if f in contenders]
123 newfiles = [f for f in changed if f in contenders]
124 if not newfiles:
124 if not newfiles:
125 ui.status(_('no changes to record\n'))
125 ui.status(_('no changes to record\n'))
126 return 0
126 return 0
127
127
128 modified = set(status.modified)
128 modified = set(status.modified)
129
129
130 # 2. backup changed files, so we can restore them in the end
130 # 2. backup changed files, so we can restore them in the end
131
131
132 if backupall:
132 if backupall:
133 tobackup = changed
133 tobackup = changed
134 else:
134 else:
135 tobackup = [f for f in newfiles if f in modified or f in \
135 tobackup = [f for f in newfiles if f in modified or f in \
136 newlyaddedandmodifiedfiles]
136 newlyaddedandmodifiedfiles]
137 backups = {}
137 backups = {}
138 if tobackup:
138 if tobackup:
139 backupdir = repo.join('record-backups')
139 backupdir = repo.join('record-backups')
140 try:
140 try:
141 os.mkdir(backupdir)
141 os.mkdir(backupdir)
142 except OSError, err:
142 except OSError, err:
143 if err.errno != errno.EEXIST:
143 if err.errno != errno.EEXIST:
144 raise
144 raise
145 try:
145 try:
146 # backup continues
146 # backup continues
147 for f in tobackup:
147 for f in tobackup:
148 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
148 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
149 dir=backupdir)
149 dir=backupdir)
150 os.close(fd)
150 os.close(fd)
151 ui.debug('backup %r as %r\n' % (f, tmpname))
151 ui.debug('backup %r as %r\n' % (f, tmpname))
152 util.copyfile(repo.wjoin(f), tmpname)
152 util.copyfile(repo.wjoin(f), tmpname)
153 shutil.copystat(repo.wjoin(f), tmpname)
153 shutil.copystat(repo.wjoin(f), tmpname)
154 backups[f] = tmpname
154 backups[f] = tmpname
155
155
156 fp = cStringIO.StringIO()
156 fp = cStringIO.StringIO()
157 for c in chunks:
157 for c in chunks:
158 fname = c.filename()
158 fname = c.filename()
159 if fname in backups:
159 if fname in backups:
160 c.write(fp)
160 c.write(fp)
161 dopatch = fp.tell()
161 dopatch = fp.tell()
162 fp.seek(0)
162 fp.seek(0)
163
163
164 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
164 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
165 # 3a. apply filtered patch to clean repo (clean)
165 # 3a. apply filtered patch to clean repo (clean)
166 if backups:
166 if backups:
167 # Equivalent to hg.revert
167 # Equivalent to hg.revert
168 choices = lambda key: key in backups
168 choices = lambda key: key in backups
169 mergemod.update(repo, repo.dirstate.p1(),
169 mergemod.update(repo, repo.dirstate.p1(),
170 False, True, choices)
170 False, True, choices)
171
171
172 # 3b. (apply)
172 # 3b. (apply)
173 if dopatch:
173 if dopatch:
174 try:
174 try:
175 ui.debug('applying patch\n')
175 ui.debug('applying patch\n')
176 ui.debug(fp.getvalue())
176 ui.debug(fp.getvalue())
177 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
177 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
178 except patch.PatchError, err:
178 except patch.PatchError, err:
179 raise util.Abort(str(err))
179 raise util.Abort(str(err))
180 del fp
180 del fp
181
181
182 # 4. We prepared working directory according to filtered
182 # 4. We prepared working directory according to filtered
183 # patch. Now is the time to delegate the job to
183 # patch. Now is the time to delegate the job to
184 # commit/qrefresh or the like!
184 # commit/qrefresh or the like!
185
185
186 # Make all of the pathnames absolute.
186 # Make all of the pathnames absolute.
187 newfiles = [repo.wjoin(nf) for nf in newfiles]
187 newfiles = [repo.wjoin(nf) for nf in newfiles]
188 return commitfunc(ui, repo, *newfiles, **opts)
188 return commitfunc(ui, repo, *newfiles, **opts)
189 finally:
189 finally:
190 # 5. finally restore backed-up files
190 # 5. finally restore backed-up files
191 try:
191 try:
192 for realname, tmpname in backups.iteritems():
192 for realname, tmpname in backups.iteritems():
193 ui.debug('restoring %r to %r\n' % (tmpname, realname))
193 ui.debug('restoring %r to %r\n' % (tmpname, realname))
194 util.copyfile(tmpname, repo.wjoin(realname))
194 util.copyfile(tmpname, repo.wjoin(realname))
195 # Our calls to copystat() here and above are a
195 # Our calls to copystat() here and above are a
196 # hack to trick any editors that have f open that
196 # hack to trick any editors that have f open that
197 # we haven't modified them.
197 # we haven't modified them.
198 #
198 #
199 # Also note that this racy as an editor could
199 # Also note that this racy as an editor could
200 # notice the file's mtime before we've finished
200 # notice the file's mtime before we've finished
201 # writing it.
201 # writing it.
202 shutil.copystat(tmpname, repo.wjoin(realname))
202 shutil.copystat(tmpname, repo.wjoin(realname))
203 os.unlink(tmpname)
203 os.unlink(tmpname)
204 if tobackup:
204 if tobackup:
205 os.rmdir(backupdir)
205 os.rmdir(backupdir)
206 except OSError:
206 except OSError:
207 pass
207 pass
208
208
209 return commit(ui, repo, recordfunc, pats, opts)
209 return commit(ui, repo, recordfunc, pats, opts)
210
210
211 def findpossible(cmd, table, strict=False):
211 def findpossible(cmd, table, strict=False):
212 """
212 """
213 Return cmd -> (aliases, command table entry)
213 Return cmd -> (aliases, command table entry)
214 for each matching command.
214 for each matching command.
215 Return debug commands (or their aliases) only if no normal command matches.
215 Return debug commands (or their aliases) only if no normal command matches.
216 """
216 """
217 choice = {}
217 choice = {}
218 debugchoice = {}
218 debugchoice = {}
219
219
220 if cmd in table:
220 if cmd in table:
221 # short-circuit exact matches, "log" alias beats "^log|history"
221 # short-circuit exact matches, "log" alias beats "^log|history"
222 keys = [cmd]
222 keys = [cmd]
223 else:
223 else:
224 keys = table.keys()
224 keys = table.keys()
225
225
226 allcmds = []
226 allcmds = []
227 for e in keys:
227 for e in keys:
228 aliases = parsealiases(e)
228 aliases = parsealiases(e)
229 allcmds.extend(aliases)
229 allcmds.extend(aliases)
230 found = None
230 found = None
231 if cmd in aliases:
231 if cmd in aliases:
232 found = cmd
232 found = cmd
233 elif not strict:
233 elif not strict:
234 for a in aliases:
234 for a in aliases:
235 if a.startswith(cmd):
235 if a.startswith(cmd):
236 found = a
236 found = a
237 break
237 break
238 if found is not None:
238 if found is not None:
239 if aliases[0].startswith("debug") or found.startswith("debug"):
239 if aliases[0].startswith("debug") or found.startswith("debug"):
240 debugchoice[found] = (aliases, table[e])
240 debugchoice[found] = (aliases, table[e])
241 else:
241 else:
242 choice[found] = (aliases, table[e])
242 choice[found] = (aliases, table[e])
243
243
244 if not choice and debugchoice:
244 if not choice and debugchoice:
245 choice = debugchoice
245 choice = debugchoice
246
246
247 return choice, allcmds
247 return choice, allcmds
248
248
249 def findcmd(cmd, table, strict=True):
249 def findcmd(cmd, table, strict=True):
250 """Return (aliases, command table entry) for command string."""
250 """Return (aliases, command table entry) for command string."""
251 choice, allcmds = findpossible(cmd, table, strict)
251 choice, allcmds = findpossible(cmd, table, strict)
252
252
253 if cmd in choice:
253 if cmd in choice:
254 return choice[cmd]
254 return choice[cmd]
255
255
256 if len(choice) > 1:
256 if len(choice) > 1:
257 clist = choice.keys()
257 clist = choice.keys()
258 clist.sort()
258 clist.sort()
259 raise error.AmbiguousCommand(cmd, clist)
259 raise error.AmbiguousCommand(cmd, clist)
260
260
261 if choice:
261 if choice:
262 return choice.values()[0]
262 return choice.values()[0]
263
263
264 raise error.UnknownCommand(cmd, allcmds)
264 raise error.UnknownCommand(cmd, allcmds)
265
265
266 def findrepo(p):
266 def findrepo(p):
267 while not os.path.isdir(os.path.join(p, ".hg")):
267 while not os.path.isdir(os.path.join(p, ".hg")):
268 oldp, p = p, os.path.dirname(p)
268 oldp, p = p, os.path.dirname(p)
269 if p == oldp:
269 if p == oldp:
270 return None
270 return None
271
271
272 return p
272 return p
273
273
274 def bailifchanged(repo, merge=True):
274 def bailifchanged(repo, merge=True):
275 if merge and repo.dirstate.p2() != nullid:
275 if merge and repo.dirstate.p2() != nullid:
276 raise util.Abort(_('outstanding uncommitted merge'))
276 raise util.Abort(_('outstanding uncommitted merge'))
277 modified, added, removed, deleted = repo.status()[:4]
277 modified, added, removed, deleted = repo.status()[:4]
278 if modified or added or removed or deleted:
278 if modified or added or removed or deleted:
279 raise util.Abort(_('uncommitted changes'))
279 raise util.Abort(_('uncommitted changes'))
280 ctx = repo[None]
280 ctx = repo[None]
281 for s in sorted(ctx.substate):
281 for s in sorted(ctx.substate):
282 ctx.sub(s).bailifchanged()
282 ctx.sub(s).bailifchanged()
283
283
284 def logmessage(ui, opts):
284 def logmessage(ui, opts):
285 """ get the log message according to -m and -l option """
285 """ get the log message according to -m and -l option """
286 message = opts.get('message')
286 message = opts.get('message')
287 logfile = opts.get('logfile')
287 logfile = opts.get('logfile')
288
288
289 if message and logfile:
289 if message and logfile:
290 raise util.Abort(_('options --message and --logfile are mutually '
290 raise util.Abort(_('options --message and --logfile are mutually '
291 'exclusive'))
291 'exclusive'))
292 if not message and logfile:
292 if not message and logfile:
293 try:
293 try:
294 if logfile == '-':
294 if logfile == '-':
295 message = ui.fin.read()
295 message = ui.fin.read()
296 else:
296 else:
297 message = '\n'.join(util.readfile(logfile).splitlines())
297 message = '\n'.join(util.readfile(logfile).splitlines())
298 except IOError, inst:
298 except IOError, inst:
299 raise util.Abort(_("can't read commit message '%s': %s") %
299 raise util.Abort(_("can't read commit message '%s': %s") %
300 (logfile, inst.strerror))
300 (logfile, inst.strerror))
301 return message
301 return message
302
302
303 def mergeeditform(ctxorbool, baseformname):
303 def mergeeditform(ctxorbool, baseformname):
304 """return appropriate editform name (referencing a committemplate)
304 """return appropriate editform name (referencing a committemplate)
305
305
306 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
306 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
307 merging is committed.
307 merging is committed.
308
308
309 This returns baseformname with '.merge' appended if it is a merge,
309 This returns baseformname with '.merge' appended if it is a merge,
310 otherwise '.normal' is appended.
310 otherwise '.normal' is appended.
311 """
311 """
312 if isinstance(ctxorbool, bool):
312 if isinstance(ctxorbool, bool):
313 if ctxorbool:
313 if ctxorbool:
314 return baseformname + ".merge"
314 return baseformname + ".merge"
315 elif 1 < len(ctxorbool.parents()):
315 elif 1 < len(ctxorbool.parents()):
316 return baseformname + ".merge"
316 return baseformname + ".merge"
317
317
318 return baseformname + ".normal"
318 return baseformname + ".normal"
319
319
320 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
320 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
321 editform='', **opts):
321 editform='', **opts):
322 """get appropriate commit message editor according to '--edit' option
322 """get appropriate commit message editor according to '--edit' option
323
323
324 'finishdesc' is a function to be called with edited commit message
324 'finishdesc' is a function to be called with edited commit message
325 (= 'description' of the new changeset) just after editing, but
325 (= 'description' of the new changeset) just after editing, but
326 before checking empty-ness. It should return actual text to be
326 before checking empty-ness. It should return actual text to be
327 stored into history. This allows to change description before
327 stored into history. This allows to change description before
328 storing.
328 storing.
329
329
330 'extramsg' is a extra message to be shown in the editor instead of
330 'extramsg' is a extra message to be shown in the editor instead of
331 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
331 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
332 is automatically added.
332 is automatically added.
333
333
334 'editform' is a dot-separated list of names, to distinguish
334 'editform' is a dot-separated list of names, to distinguish
335 the purpose of commit text editing.
335 the purpose of commit text editing.
336
336
337 'getcommiteditor' returns 'commitforceeditor' regardless of
337 'getcommiteditor' returns 'commitforceeditor' regardless of
338 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
338 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
339 they are specific for usage in MQ.
339 they are specific for usage in MQ.
340 """
340 """
341 if edit or finishdesc or extramsg:
341 if edit or finishdesc or extramsg:
342 return lambda r, c, s: commitforceeditor(r, c, s,
342 return lambda r, c, s: commitforceeditor(r, c, s,
343 finishdesc=finishdesc,
343 finishdesc=finishdesc,
344 extramsg=extramsg,
344 extramsg=extramsg,
345 editform=editform)
345 editform=editform)
346 elif editform:
346 elif editform:
347 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
347 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
348 else:
348 else:
349 return commiteditor
349 return commiteditor
350
350
351 def loglimit(opts):
351 def loglimit(opts):
352 """get the log limit according to option -l/--limit"""
352 """get the log limit according to option -l/--limit"""
353 limit = opts.get('limit')
353 limit = opts.get('limit')
354 if limit:
354 if limit:
355 try:
355 try:
356 limit = int(limit)
356 limit = int(limit)
357 except ValueError:
357 except ValueError:
358 raise util.Abort(_('limit must be a positive integer'))
358 raise util.Abort(_('limit must be a positive integer'))
359 if limit <= 0:
359 if limit <= 0:
360 raise util.Abort(_('limit must be positive'))
360 raise util.Abort(_('limit must be positive'))
361 else:
361 else:
362 limit = None
362 limit = None
363 return limit
363 return limit
364
364
365 def makefilename(repo, pat, node, desc=None,
365 def makefilename(repo, pat, node, desc=None,
366 total=None, seqno=None, revwidth=None, pathname=None):
366 total=None, seqno=None, revwidth=None, pathname=None):
367 node_expander = {
367 node_expander = {
368 'H': lambda: hex(node),
368 'H': lambda: hex(node),
369 'R': lambda: str(repo.changelog.rev(node)),
369 'R': lambda: str(repo.changelog.rev(node)),
370 'h': lambda: short(node),
370 'h': lambda: short(node),
371 'm': lambda: re.sub('[^\w]', '_', str(desc))
371 'm': lambda: re.sub('[^\w]', '_', str(desc))
372 }
372 }
373 expander = {
373 expander = {
374 '%': lambda: '%',
374 '%': lambda: '%',
375 'b': lambda: os.path.basename(repo.root),
375 'b': lambda: os.path.basename(repo.root),
376 }
376 }
377
377
378 try:
378 try:
379 if node:
379 if node:
380 expander.update(node_expander)
380 expander.update(node_expander)
381 if node:
381 if node:
382 expander['r'] = (lambda:
382 expander['r'] = (lambda:
383 str(repo.changelog.rev(node)).zfill(revwidth or 0))
383 str(repo.changelog.rev(node)).zfill(revwidth or 0))
384 if total is not None:
384 if total is not None:
385 expander['N'] = lambda: str(total)
385 expander['N'] = lambda: str(total)
386 if seqno is not None:
386 if seqno is not None:
387 expander['n'] = lambda: str(seqno)
387 expander['n'] = lambda: str(seqno)
388 if total is not None and seqno is not None:
388 if total is not None and seqno is not None:
389 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
389 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
390 if pathname is not None:
390 if pathname is not None:
391 expander['s'] = lambda: os.path.basename(pathname)
391 expander['s'] = lambda: os.path.basename(pathname)
392 expander['d'] = lambda: os.path.dirname(pathname) or '.'
392 expander['d'] = lambda: os.path.dirname(pathname) or '.'
393 expander['p'] = lambda: pathname
393 expander['p'] = lambda: pathname
394
394
395 newname = []
395 newname = []
396 patlen = len(pat)
396 patlen = len(pat)
397 i = 0
397 i = 0
398 while i < patlen:
398 while i < patlen:
399 c = pat[i]
399 c = pat[i]
400 if c == '%':
400 if c == '%':
401 i += 1
401 i += 1
402 c = pat[i]
402 c = pat[i]
403 c = expander[c]()
403 c = expander[c]()
404 newname.append(c)
404 newname.append(c)
405 i += 1
405 i += 1
406 return ''.join(newname)
406 return ''.join(newname)
407 except KeyError, inst:
407 except KeyError, inst:
408 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
408 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
409 inst.args[0])
409 inst.args[0])
410
410
411 def makefileobj(repo, pat, node=None, desc=None, total=None,
411 def makefileobj(repo, pat, node=None, desc=None, total=None,
412 seqno=None, revwidth=None, mode='wb', modemap=None,
412 seqno=None, revwidth=None, mode='wb', modemap=None,
413 pathname=None):
413 pathname=None):
414
414
415 writable = mode not in ('r', 'rb')
415 writable = mode not in ('r', 'rb')
416
416
417 if not pat or pat == '-':
417 if not pat or pat == '-':
418 if writable:
418 if writable:
419 fp = repo.ui.fout
419 fp = repo.ui.fout
420 else:
420 else:
421 fp = repo.ui.fin
421 fp = repo.ui.fin
422 if util.safehasattr(fp, 'fileno'):
422 if util.safehasattr(fp, 'fileno'):
423 return os.fdopen(os.dup(fp.fileno()), mode)
423 return os.fdopen(os.dup(fp.fileno()), mode)
424 else:
424 else:
425 # if this fp can't be duped properly, return
425 # if this fp can't be duped properly, return
426 # a dummy object that can be closed
426 # a dummy object that can be closed
427 class wrappedfileobj(object):
427 class wrappedfileobj(object):
428 noop = lambda x: None
428 noop = lambda x: None
429 def __init__(self, f):
429 def __init__(self, f):
430 self.f = f
430 self.f = f
431 def __getattr__(self, attr):
431 def __getattr__(self, attr):
432 if attr == 'close':
432 if attr == 'close':
433 return self.noop
433 return self.noop
434 else:
434 else:
435 return getattr(self.f, attr)
435 return getattr(self.f, attr)
436
436
437 return wrappedfileobj(fp)
437 return wrappedfileobj(fp)
438 if util.safehasattr(pat, 'write') and writable:
438 if util.safehasattr(pat, 'write') and writable:
439 return pat
439 return pat
440 if util.safehasattr(pat, 'read') and 'r' in mode:
440 if util.safehasattr(pat, 'read') and 'r' in mode:
441 return pat
441 return pat
442 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
442 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
443 if modemap is not None:
443 if modemap is not None:
444 mode = modemap.get(fn, mode)
444 mode = modemap.get(fn, mode)
445 if mode == 'wb':
445 if mode == 'wb':
446 modemap[fn] = 'ab'
446 modemap[fn] = 'ab'
447 return open(fn, mode)
447 return open(fn, mode)
448
448
449 def openrevlog(repo, cmd, file_, opts):
449 def openrevlog(repo, cmd, file_, opts):
450 """opens the changelog, manifest, a filelog or a given revlog"""
450 """opens the changelog, manifest, a filelog or a given revlog"""
451 cl = opts['changelog']
451 cl = opts['changelog']
452 mf = opts['manifest']
452 mf = opts['manifest']
453 dir = opts['dir']
453 msg = None
454 msg = None
454 if cl and mf:
455 if cl and mf:
455 msg = _('cannot specify --changelog and --manifest at the same time')
456 msg = _('cannot specify --changelog and --manifest at the same time')
457 elif cl and dir:
458 msg = _('cannot specify --changelog and --dir at the same time')
456 elif cl or mf:
459 elif cl or mf:
457 if file_:
460 if file_:
458 msg = _('cannot specify filename with --changelog or --manifest')
461 msg = _('cannot specify filename with --changelog or --manifest')
459 elif not repo:
462 elif not repo:
460 msg = _('cannot specify --changelog or --manifest '
463 msg = _('cannot specify --changelog or --manifest or --dir '
461 'without a repository')
464 'without a repository')
462 if msg:
465 if msg:
463 raise util.Abort(msg)
466 raise util.Abort(msg)
464
467
465 r = None
468 r = None
466 if repo:
469 if repo:
467 if cl:
470 if cl:
468 r = repo.unfiltered().changelog
471 r = repo.unfiltered().changelog
472 elif dir:
473 if 'treemanifest' not in repo.requirements:
474 raise util.Abort(_("--dir can only be used on repos with "
475 "treemanifest enabled"))
476 dirlog = repo.dirlog(file_)
477 if len(dirlog):
478 r = dirlog
469 elif mf:
479 elif mf:
470 r = repo.manifest
480 r = repo.manifest
471 elif file_:
481 elif file_:
472 filelog = repo.file(file_)
482 filelog = repo.file(file_)
473 if len(filelog):
483 if len(filelog):
474 r = filelog
484 r = filelog
475 if not r:
485 if not r:
476 if not file_:
486 if not file_:
477 raise error.CommandError(cmd, _('invalid arguments'))
487 raise error.CommandError(cmd, _('invalid arguments'))
478 if not os.path.isfile(file_):
488 if not os.path.isfile(file_):
479 raise util.Abort(_("revlog '%s' not found") % file_)
489 raise util.Abort(_("revlog '%s' not found") % file_)
480 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
490 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
481 file_[:-2] + ".i")
491 file_[:-2] + ".i")
482 return r
492 return r
483
493
484 def copy(ui, repo, pats, opts, rename=False):
494 def copy(ui, repo, pats, opts, rename=False):
485 # called with the repo lock held
495 # called with the repo lock held
486 #
496 #
487 # hgsep => pathname that uses "/" to separate directories
497 # hgsep => pathname that uses "/" to separate directories
488 # ossep => pathname that uses os.sep to separate directories
498 # ossep => pathname that uses os.sep to separate directories
489 cwd = repo.getcwd()
499 cwd = repo.getcwd()
490 targets = {}
500 targets = {}
491 after = opts.get("after")
501 after = opts.get("after")
492 dryrun = opts.get("dry_run")
502 dryrun = opts.get("dry_run")
493 wctx = repo[None]
503 wctx = repo[None]
494
504
495 def walkpat(pat):
505 def walkpat(pat):
496 srcs = []
506 srcs = []
497 if after:
507 if after:
498 badstates = '?'
508 badstates = '?'
499 else:
509 else:
500 badstates = '?r'
510 badstates = '?r'
501 m = scmutil.match(repo[None], [pat], opts, globbed=True)
511 m = scmutil.match(repo[None], [pat], opts, globbed=True)
502 for abs in repo.walk(m):
512 for abs in repo.walk(m):
503 state = repo.dirstate[abs]
513 state = repo.dirstate[abs]
504 rel = m.rel(abs)
514 rel = m.rel(abs)
505 exact = m.exact(abs)
515 exact = m.exact(abs)
506 if state in badstates:
516 if state in badstates:
507 if exact and state == '?':
517 if exact and state == '?':
508 ui.warn(_('%s: not copying - file is not managed\n') % rel)
518 ui.warn(_('%s: not copying - file is not managed\n') % rel)
509 if exact and state == 'r':
519 if exact and state == 'r':
510 ui.warn(_('%s: not copying - file has been marked for'
520 ui.warn(_('%s: not copying - file has been marked for'
511 ' remove\n') % rel)
521 ' remove\n') % rel)
512 continue
522 continue
513 # abs: hgsep
523 # abs: hgsep
514 # rel: ossep
524 # rel: ossep
515 srcs.append((abs, rel, exact))
525 srcs.append((abs, rel, exact))
516 return srcs
526 return srcs
517
527
518 # abssrc: hgsep
528 # abssrc: hgsep
519 # relsrc: ossep
529 # relsrc: ossep
520 # otarget: ossep
530 # otarget: ossep
521 def copyfile(abssrc, relsrc, otarget, exact):
531 def copyfile(abssrc, relsrc, otarget, exact):
522 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
532 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
523 if '/' in abstarget:
533 if '/' in abstarget:
524 # We cannot normalize abstarget itself, this would prevent
534 # We cannot normalize abstarget itself, this would prevent
525 # case only renames, like a => A.
535 # case only renames, like a => A.
526 abspath, absname = abstarget.rsplit('/', 1)
536 abspath, absname = abstarget.rsplit('/', 1)
527 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
537 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
528 reltarget = repo.pathto(abstarget, cwd)
538 reltarget = repo.pathto(abstarget, cwd)
529 target = repo.wjoin(abstarget)
539 target = repo.wjoin(abstarget)
530 src = repo.wjoin(abssrc)
540 src = repo.wjoin(abssrc)
531 state = repo.dirstate[abstarget]
541 state = repo.dirstate[abstarget]
532
542
533 scmutil.checkportable(ui, abstarget)
543 scmutil.checkportable(ui, abstarget)
534
544
535 # check for collisions
545 # check for collisions
536 prevsrc = targets.get(abstarget)
546 prevsrc = targets.get(abstarget)
537 if prevsrc is not None:
547 if prevsrc is not None:
538 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
548 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
539 (reltarget, repo.pathto(abssrc, cwd),
549 (reltarget, repo.pathto(abssrc, cwd),
540 repo.pathto(prevsrc, cwd)))
550 repo.pathto(prevsrc, cwd)))
541 return
551 return
542
552
543 # check for overwrites
553 # check for overwrites
544 exists = os.path.lexists(target)
554 exists = os.path.lexists(target)
545 samefile = False
555 samefile = False
546 if exists and abssrc != abstarget:
556 if exists and abssrc != abstarget:
547 if (repo.dirstate.normalize(abssrc) ==
557 if (repo.dirstate.normalize(abssrc) ==
548 repo.dirstate.normalize(abstarget)):
558 repo.dirstate.normalize(abstarget)):
549 if not rename:
559 if not rename:
550 ui.warn(_("%s: can't copy - same file\n") % reltarget)
560 ui.warn(_("%s: can't copy - same file\n") % reltarget)
551 return
561 return
552 exists = False
562 exists = False
553 samefile = True
563 samefile = True
554
564
555 if not after and exists or after and state in 'mn':
565 if not after and exists or after and state in 'mn':
556 if not opts['force']:
566 if not opts['force']:
557 ui.warn(_('%s: not overwriting - file exists\n') %
567 ui.warn(_('%s: not overwriting - file exists\n') %
558 reltarget)
568 reltarget)
559 return
569 return
560
570
561 if after:
571 if after:
562 if not exists:
572 if not exists:
563 if rename:
573 if rename:
564 ui.warn(_('%s: not recording move - %s does not exist\n') %
574 ui.warn(_('%s: not recording move - %s does not exist\n') %
565 (relsrc, reltarget))
575 (relsrc, reltarget))
566 else:
576 else:
567 ui.warn(_('%s: not recording copy - %s does not exist\n') %
577 ui.warn(_('%s: not recording copy - %s does not exist\n') %
568 (relsrc, reltarget))
578 (relsrc, reltarget))
569 return
579 return
570 elif not dryrun:
580 elif not dryrun:
571 try:
581 try:
572 if exists:
582 if exists:
573 os.unlink(target)
583 os.unlink(target)
574 targetdir = os.path.dirname(target) or '.'
584 targetdir = os.path.dirname(target) or '.'
575 if not os.path.isdir(targetdir):
585 if not os.path.isdir(targetdir):
576 os.makedirs(targetdir)
586 os.makedirs(targetdir)
577 if samefile:
587 if samefile:
578 tmp = target + "~hgrename"
588 tmp = target + "~hgrename"
579 os.rename(src, tmp)
589 os.rename(src, tmp)
580 os.rename(tmp, target)
590 os.rename(tmp, target)
581 else:
591 else:
582 util.copyfile(src, target)
592 util.copyfile(src, target)
583 srcexists = True
593 srcexists = True
584 except IOError, inst:
594 except IOError, inst:
585 if inst.errno == errno.ENOENT:
595 if inst.errno == errno.ENOENT:
586 ui.warn(_('%s: deleted in working directory\n') % relsrc)
596 ui.warn(_('%s: deleted in working directory\n') % relsrc)
587 srcexists = False
597 srcexists = False
588 else:
598 else:
589 ui.warn(_('%s: cannot copy - %s\n') %
599 ui.warn(_('%s: cannot copy - %s\n') %
590 (relsrc, inst.strerror))
600 (relsrc, inst.strerror))
591 return True # report a failure
601 return True # report a failure
592
602
593 if ui.verbose or not exact:
603 if ui.verbose or not exact:
594 if rename:
604 if rename:
595 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
605 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
596 else:
606 else:
597 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
607 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
598
608
599 targets[abstarget] = abssrc
609 targets[abstarget] = abssrc
600
610
601 # fix up dirstate
611 # fix up dirstate
602 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
612 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
603 dryrun=dryrun, cwd=cwd)
613 dryrun=dryrun, cwd=cwd)
604 if rename and not dryrun:
614 if rename and not dryrun:
605 if not after and srcexists and not samefile:
615 if not after and srcexists and not samefile:
606 util.unlinkpath(repo.wjoin(abssrc))
616 util.unlinkpath(repo.wjoin(abssrc))
607 wctx.forget([abssrc])
617 wctx.forget([abssrc])
608
618
609 # pat: ossep
619 # pat: ossep
610 # dest ossep
620 # dest ossep
611 # srcs: list of (hgsep, hgsep, ossep, bool)
621 # srcs: list of (hgsep, hgsep, ossep, bool)
612 # return: function that takes hgsep and returns ossep
622 # return: function that takes hgsep and returns ossep
613 def targetpathfn(pat, dest, srcs):
623 def targetpathfn(pat, dest, srcs):
614 if os.path.isdir(pat):
624 if os.path.isdir(pat):
615 abspfx = pathutil.canonpath(repo.root, cwd, pat)
625 abspfx = pathutil.canonpath(repo.root, cwd, pat)
616 abspfx = util.localpath(abspfx)
626 abspfx = util.localpath(abspfx)
617 if destdirexists:
627 if destdirexists:
618 striplen = len(os.path.split(abspfx)[0])
628 striplen = len(os.path.split(abspfx)[0])
619 else:
629 else:
620 striplen = len(abspfx)
630 striplen = len(abspfx)
621 if striplen:
631 if striplen:
622 striplen += len(os.sep)
632 striplen += len(os.sep)
623 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
633 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
624 elif destdirexists:
634 elif destdirexists:
625 res = lambda p: os.path.join(dest,
635 res = lambda p: os.path.join(dest,
626 os.path.basename(util.localpath(p)))
636 os.path.basename(util.localpath(p)))
627 else:
637 else:
628 res = lambda p: dest
638 res = lambda p: dest
629 return res
639 return res
630
640
631 # pat: ossep
641 # pat: ossep
632 # dest ossep
642 # dest ossep
633 # srcs: list of (hgsep, hgsep, ossep, bool)
643 # srcs: list of (hgsep, hgsep, ossep, bool)
634 # return: function that takes hgsep and returns ossep
644 # return: function that takes hgsep and returns ossep
635 def targetpathafterfn(pat, dest, srcs):
645 def targetpathafterfn(pat, dest, srcs):
636 if matchmod.patkind(pat):
646 if matchmod.patkind(pat):
637 # a mercurial pattern
647 # a mercurial pattern
638 res = lambda p: os.path.join(dest,
648 res = lambda p: os.path.join(dest,
639 os.path.basename(util.localpath(p)))
649 os.path.basename(util.localpath(p)))
640 else:
650 else:
641 abspfx = pathutil.canonpath(repo.root, cwd, pat)
651 abspfx = pathutil.canonpath(repo.root, cwd, pat)
642 if len(abspfx) < len(srcs[0][0]):
652 if len(abspfx) < len(srcs[0][0]):
643 # A directory. Either the target path contains the last
653 # A directory. Either the target path contains the last
644 # component of the source path or it does not.
654 # component of the source path or it does not.
645 def evalpath(striplen):
655 def evalpath(striplen):
646 score = 0
656 score = 0
647 for s in srcs:
657 for s in srcs:
648 t = os.path.join(dest, util.localpath(s[0])[striplen:])
658 t = os.path.join(dest, util.localpath(s[0])[striplen:])
649 if os.path.lexists(t):
659 if os.path.lexists(t):
650 score += 1
660 score += 1
651 return score
661 return score
652
662
653 abspfx = util.localpath(abspfx)
663 abspfx = util.localpath(abspfx)
654 striplen = len(abspfx)
664 striplen = len(abspfx)
655 if striplen:
665 if striplen:
656 striplen += len(os.sep)
666 striplen += len(os.sep)
657 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
667 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
658 score = evalpath(striplen)
668 score = evalpath(striplen)
659 striplen1 = len(os.path.split(abspfx)[0])
669 striplen1 = len(os.path.split(abspfx)[0])
660 if striplen1:
670 if striplen1:
661 striplen1 += len(os.sep)
671 striplen1 += len(os.sep)
662 if evalpath(striplen1) > score:
672 if evalpath(striplen1) > score:
663 striplen = striplen1
673 striplen = striplen1
664 res = lambda p: os.path.join(dest,
674 res = lambda p: os.path.join(dest,
665 util.localpath(p)[striplen:])
675 util.localpath(p)[striplen:])
666 else:
676 else:
667 # a file
677 # a file
668 if destdirexists:
678 if destdirexists:
669 res = lambda p: os.path.join(dest,
679 res = lambda p: os.path.join(dest,
670 os.path.basename(util.localpath(p)))
680 os.path.basename(util.localpath(p)))
671 else:
681 else:
672 res = lambda p: dest
682 res = lambda p: dest
673 return res
683 return res
674
684
675 pats = scmutil.expandpats(pats)
685 pats = scmutil.expandpats(pats)
676 if not pats:
686 if not pats:
677 raise util.Abort(_('no source or destination specified'))
687 raise util.Abort(_('no source or destination specified'))
678 if len(pats) == 1:
688 if len(pats) == 1:
679 raise util.Abort(_('no destination specified'))
689 raise util.Abort(_('no destination specified'))
680 dest = pats.pop()
690 dest = pats.pop()
681 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
691 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
682 if not destdirexists:
692 if not destdirexists:
683 if len(pats) > 1 or matchmod.patkind(pats[0]):
693 if len(pats) > 1 or matchmod.patkind(pats[0]):
684 raise util.Abort(_('with multiple sources, destination must be an '
694 raise util.Abort(_('with multiple sources, destination must be an '
685 'existing directory'))
695 'existing directory'))
686 if util.endswithsep(dest):
696 if util.endswithsep(dest):
687 raise util.Abort(_('destination %s is not a directory') % dest)
697 raise util.Abort(_('destination %s is not a directory') % dest)
688
698
689 tfn = targetpathfn
699 tfn = targetpathfn
690 if after:
700 if after:
691 tfn = targetpathafterfn
701 tfn = targetpathafterfn
692 copylist = []
702 copylist = []
693 for pat in pats:
703 for pat in pats:
694 srcs = walkpat(pat)
704 srcs = walkpat(pat)
695 if not srcs:
705 if not srcs:
696 continue
706 continue
697 copylist.append((tfn(pat, dest, srcs), srcs))
707 copylist.append((tfn(pat, dest, srcs), srcs))
698 if not copylist:
708 if not copylist:
699 raise util.Abort(_('no files to copy'))
709 raise util.Abort(_('no files to copy'))
700
710
701 errors = 0
711 errors = 0
702 for targetpath, srcs in copylist:
712 for targetpath, srcs in copylist:
703 for abssrc, relsrc, exact in srcs:
713 for abssrc, relsrc, exact in srcs:
704 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
714 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
705 errors += 1
715 errors += 1
706
716
707 if errors:
717 if errors:
708 ui.warn(_('(consider using --after)\n'))
718 ui.warn(_('(consider using --after)\n'))
709
719
710 return errors != 0
720 return errors != 0
711
721
712 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
722 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
713 runargs=None, appendpid=False):
723 runargs=None, appendpid=False):
714 '''Run a command as a service.'''
724 '''Run a command as a service.'''
715
725
716 def writepid(pid):
726 def writepid(pid):
717 if opts['pid_file']:
727 if opts['pid_file']:
718 if appendpid:
728 if appendpid:
719 mode = 'a'
729 mode = 'a'
720 else:
730 else:
721 mode = 'w'
731 mode = 'w'
722 fp = open(opts['pid_file'], mode)
732 fp = open(opts['pid_file'], mode)
723 fp.write(str(pid) + '\n')
733 fp.write(str(pid) + '\n')
724 fp.close()
734 fp.close()
725
735
726 if opts['daemon'] and not opts['daemon_pipefds']:
736 if opts['daemon'] and not opts['daemon_pipefds']:
727 # Signal child process startup with file removal
737 # Signal child process startup with file removal
728 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
738 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
729 os.close(lockfd)
739 os.close(lockfd)
730 try:
740 try:
731 if not runargs:
741 if not runargs:
732 runargs = util.hgcmd() + sys.argv[1:]
742 runargs = util.hgcmd() + sys.argv[1:]
733 runargs.append('--daemon-pipefds=%s' % lockpath)
743 runargs.append('--daemon-pipefds=%s' % lockpath)
734 # Don't pass --cwd to the child process, because we've already
744 # Don't pass --cwd to the child process, because we've already
735 # changed directory.
745 # changed directory.
736 for i in xrange(1, len(runargs)):
746 for i in xrange(1, len(runargs)):
737 if runargs[i].startswith('--cwd='):
747 if runargs[i].startswith('--cwd='):
738 del runargs[i]
748 del runargs[i]
739 break
749 break
740 elif runargs[i].startswith('--cwd'):
750 elif runargs[i].startswith('--cwd'):
741 del runargs[i:i + 2]
751 del runargs[i:i + 2]
742 break
752 break
743 def condfn():
753 def condfn():
744 return not os.path.exists(lockpath)
754 return not os.path.exists(lockpath)
745 pid = util.rundetached(runargs, condfn)
755 pid = util.rundetached(runargs, condfn)
746 if pid < 0:
756 if pid < 0:
747 raise util.Abort(_('child process failed to start'))
757 raise util.Abort(_('child process failed to start'))
748 writepid(pid)
758 writepid(pid)
749 finally:
759 finally:
750 try:
760 try:
751 os.unlink(lockpath)
761 os.unlink(lockpath)
752 except OSError, e:
762 except OSError, e:
753 if e.errno != errno.ENOENT:
763 if e.errno != errno.ENOENT:
754 raise
764 raise
755 if parentfn:
765 if parentfn:
756 return parentfn(pid)
766 return parentfn(pid)
757 else:
767 else:
758 return
768 return
759
769
760 if initfn:
770 if initfn:
761 initfn()
771 initfn()
762
772
763 if not opts['daemon']:
773 if not opts['daemon']:
764 writepid(os.getpid())
774 writepid(os.getpid())
765
775
766 if opts['daemon_pipefds']:
776 if opts['daemon_pipefds']:
767 lockpath = opts['daemon_pipefds']
777 lockpath = opts['daemon_pipefds']
768 try:
778 try:
769 os.setsid()
779 os.setsid()
770 except AttributeError:
780 except AttributeError:
771 pass
781 pass
772 os.unlink(lockpath)
782 os.unlink(lockpath)
773 util.hidewindow()
783 util.hidewindow()
774 sys.stdout.flush()
784 sys.stdout.flush()
775 sys.stderr.flush()
785 sys.stderr.flush()
776
786
777 nullfd = os.open(os.devnull, os.O_RDWR)
787 nullfd = os.open(os.devnull, os.O_RDWR)
778 logfilefd = nullfd
788 logfilefd = nullfd
779 if logfile:
789 if logfile:
780 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
790 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
781 os.dup2(nullfd, 0)
791 os.dup2(nullfd, 0)
782 os.dup2(logfilefd, 1)
792 os.dup2(logfilefd, 1)
783 os.dup2(logfilefd, 2)
793 os.dup2(logfilefd, 2)
784 if nullfd not in (0, 1, 2):
794 if nullfd not in (0, 1, 2):
785 os.close(nullfd)
795 os.close(nullfd)
786 if logfile and logfilefd not in (0, 1, 2):
796 if logfile and logfilefd not in (0, 1, 2):
787 os.close(logfilefd)
797 os.close(logfilefd)
788
798
789 if runfn:
799 if runfn:
790 return runfn()
800 return runfn()
791
801
792 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
802 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
793 """Utility function used by commands.import to import a single patch
803 """Utility function used by commands.import to import a single patch
794
804
795 This function is explicitly defined here to help the evolve extension to
805 This function is explicitly defined here to help the evolve extension to
796 wrap this part of the import logic.
806 wrap this part of the import logic.
797
807
798 The API is currently a bit ugly because it a simple code translation from
808 The API is currently a bit ugly because it a simple code translation from
799 the import command. Feel free to make it better.
809 the import command. Feel free to make it better.
800
810
801 :hunk: a patch (as a binary string)
811 :hunk: a patch (as a binary string)
802 :parents: nodes that will be parent of the created commit
812 :parents: nodes that will be parent of the created commit
803 :opts: the full dict of option passed to the import command
813 :opts: the full dict of option passed to the import command
804 :msgs: list to save commit message to.
814 :msgs: list to save commit message to.
805 (used in case we need to save it when failing)
815 (used in case we need to save it when failing)
806 :updatefunc: a function that update a repo to a given node
816 :updatefunc: a function that update a repo to a given node
807 updatefunc(<repo>, <node>)
817 updatefunc(<repo>, <node>)
808 """
818 """
809 tmpname, message, user, date, branch, nodeid, p1, p2 = \
819 tmpname, message, user, date, branch, nodeid, p1, p2 = \
810 patch.extract(ui, hunk)
820 patch.extract(ui, hunk)
811
821
812 update = not opts.get('bypass')
822 update = not opts.get('bypass')
813 strip = opts["strip"]
823 strip = opts["strip"]
814 prefix = opts["prefix"]
824 prefix = opts["prefix"]
815 sim = float(opts.get('similarity') or 0)
825 sim = float(opts.get('similarity') or 0)
816 if not tmpname:
826 if not tmpname:
817 return (None, None, False)
827 return (None, None, False)
818 msg = _('applied to working directory')
828 msg = _('applied to working directory')
819
829
820 rejects = False
830 rejects = False
821 dsguard = None
831 dsguard = None
822
832
823 try:
833 try:
824 cmdline_message = logmessage(ui, opts)
834 cmdline_message = logmessage(ui, opts)
825 if cmdline_message:
835 if cmdline_message:
826 # pickup the cmdline msg
836 # pickup the cmdline msg
827 message = cmdline_message
837 message = cmdline_message
828 elif message:
838 elif message:
829 # pickup the patch msg
839 # pickup the patch msg
830 message = message.strip()
840 message = message.strip()
831 else:
841 else:
832 # launch the editor
842 # launch the editor
833 message = None
843 message = None
834 ui.debug('message:\n%s\n' % message)
844 ui.debug('message:\n%s\n' % message)
835
845
836 if len(parents) == 1:
846 if len(parents) == 1:
837 parents.append(repo[nullid])
847 parents.append(repo[nullid])
838 if opts.get('exact'):
848 if opts.get('exact'):
839 if not nodeid or not p1:
849 if not nodeid or not p1:
840 raise util.Abort(_('not a Mercurial patch'))
850 raise util.Abort(_('not a Mercurial patch'))
841 p1 = repo[p1]
851 p1 = repo[p1]
842 p2 = repo[p2 or nullid]
852 p2 = repo[p2 or nullid]
843 elif p2:
853 elif p2:
844 try:
854 try:
845 p1 = repo[p1]
855 p1 = repo[p1]
846 p2 = repo[p2]
856 p2 = repo[p2]
847 # Without any options, consider p2 only if the
857 # Without any options, consider p2 only if the
848 # patch is being applied on top of the recorded
858 # patch is being applied on top of the recorded
849 # first parent.
859 # first parent.
850 if p1 != parents[0]:
860 if p1 != parents[0]:
851 p1 = parents[0]
861 p1 = parents[0]
852 p2 = repo[nullid]
862 p2 = repo[nullid]
853 except error.RepoError:
863 except error.RepoError:
854 p1, p2 = parents
864 p1, p2 = parents
855 if p2.node() == nullid:
865 if p2.node() == nullid:
856 ui.warn(_("warning: import the patch as a normal revision\n"
866 ui.warn(_("warning: import the patch as a normal revision\n"
857 "(use --exact to import the patch as a merge)\n"))
867 "(use --exact to import the patch as a merge)\n"))
858 else:
868 else:
859 p1, p2 = parents
869 p1, p2 = parents
860
870
861 n = None
871 n = None
862 if update:
872 if update:
863 dsguard = dirstateguard(repo, 'tryimportone')
873 dsguard = dirstateguard(repo, 'tryimportone')
864 if p1 != parents[0]:
874 if p1 != parents[0]:
865 updatefunc(repo, p1.node())
875 updatefunc(repo, p1.node())
866 if p2 != parents[1]:
876 if p2 != parents[1]:
867 repo.setparents(p1.node(), p2.node())
877 repo.setparents(p1.node(), p2.node())
868
878
869 if opts.get('exact') or opts.get('import_branch'):
879 if opts.get('exact') or opts.get('import_branch'):
870 repo.dirstate.setbranch(branch or 'default')
880 repo.dirstate.setbranch(branch or 'default')
871
881
872 partial = opts.get('partial', False)
882 partial = opts.get('partial', False)
873 files = set()
883 files = set()
874 try:
884 try:
875 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
885 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
876 files=files, eolmode=None, similarity=sim / 100.0)
886 files=files, eolmode=None, similarity=sim / 100.0)
877 except patch.PatchError, e:
887 except patch.PatchError, e:
878 if not partial:
888 if not partial:
879 raise util.Abort(str(e))
889 raise util.Abort(str(e))
880 if partial:
890 if partial:
881 rejects = True
891 rejects = True
882
892
883 files = list(files)
893 files = list(files)
884 if opts.get('no_commit'):
894 if opts.get('no_commit'):
885 if message:
895 if message:
886 msgs.append(message)
896 msgs.append(message)
887 else:
897 else:
888 if opts.get('exact') or p2:
898 if opts.get('exact') or p2:
889 # If you got here, you either use --force and know what
899 # If you got here, you either use --force and know what
890 # you are doing or used --exact or a merge patch while
900 # you are doing or used --exact or a merge patch while
891 # being updated to its first parent.
901 # being updated to its first parent.
892 m = None
902 m = None
893 else:
903 else:
894 m = scmutil.matchfiles(repo, files or [])
904 m = scmutil.matchfiles(repo, files or [])
895 editform = mergeeditform(repo[None], 'import.normal')
905 editform = mergeeditform(repo[None], 'import.normal')
896 if opts.get('exact'):
906 if opts.get('exact'):
897 editor = None
907 editor = None
898 else:
908 else:
899 editor = getcommiteditor(editform=editform, **opts)
909 editor = getcommiteditor(editform=editform, **opts)
900 allowemptyback = repo.ui.backupconfig('ui', 'allowemptycommit')
910 allowemptyback = repo.ui.backupconfig('ui', 'allowemptycommit')
901 try:
911 try:
902 if partial:
912 if partial:
903 repo.ui.setconfig('ui', 'allowemptycommit', True)
913 repo.ui.setconfig('ui', 'allowemptycommit', True)
904 n = repo.commit(message, opts.get('user') or user,
914 n = repo.commit(message, opts.get('user') or user,
905 opts.get('date') or date, match=m,
915 opts.get('date') or date, match=m,
906 editor=editor)
916 editor=editor)
907 finally:
917 finally:
908 repo.ui.restoreconfig(allowemptyback)
918 repo.ui.restoreconfig(allowemptyback)
909 dsguard.close()
919 dsguard.close()
910 else:
920 else:
911 if opts.get('exact') or opts.get('import_branch'):
921 if opts.get('exact') or opts.get('import_branch'):
912 branch = branch or 'default'
922 branch = branch or 'default'
913 else:
923 else:
914 branch = p1.branch()
924 branch = p1.branch()
915 store = patch.filestore()
925 store = patch.filestore()
916 try:
926 try:
917 files = set()
927 files = set()
918 try:
928 try:
919 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
929 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
920 files, eolmode=None)
930 files, eolmode=None)
921 except patch.PatchError, e:
931 except patch.PatchError, e:
922 raise util.Abort(str(e))
932 raise util.Abort(str(e))
923 if opts.get('exact'):
933 if opts.get('exact'):
924 editor = None
934 editor = None
925 else:
935 else:
926 editor = getcommiteditor(editform='import.bypass')
936 editor = getcommiteditor(editform='import.bypass')
927 memctx = context.makememctx(repo, (p1.node(), p2.node()),
937 memctx = context.makememctx(repo, (p1.node(), p2.node()),
928 message,
938 message,
929 opts.get('user') or user,
939 opts.get('user') or user,
930 opts.get('date') or date,
940 opts.get('date') or date,
931 branch, files, store,
941 branch, files, store,
932 editor=editor)
942 editor=editor)
933 n = memctx.commit()
943 n = memctx.commit()
934 finally:
944 finally:
935 store.close()
945 store.close()
936 if opts.get('exact') and opts.get('no_commit'):
946 if opts.get('exact') and opts.get('no_commit'):
937 # --exact with --no-commit is still useful in that it does merge
947 # --exact with --no-commit is still useful in that it does merge
938 # and branch bits
948 # and branch bits
939 ui.warn(_("warning: can't check exact import with --no-commit\n"))
949 ui.warn(_("warning: can't check exact import with --no-commit\n"))
940 elif opts.get('exact') and hex(n) != nodeid:
950 elif opts.get('exact') and hex(n) != nodeid:
941 raise util.Abort(_('patch is damaged or loses information'))
951 raise util.Abort(_('patch is damaged or loses information'))
942 if n:
952 if n:
943 # i18n: refers to a short changeset id
953 # i18n: refers to a short changeset id
944 msg = _('created %s') % short(n)
954 msg = _('created %s') % short(n)
945 return (msg, n, rejects)
955 return (msg, n, rejects)
946 finally:
956 finally:
947 lockmod.release(dsguard)
957 lockmod.release(dsguard)
948 os.unlink(tmpname)
958 os.unlink(tmpname)
949
959
950 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
960 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
951 opts=None):
961 opts=None):
952 '''export changesets as hg patches.'''
962 '''export changesets as hg patches.'''
953
963
954 total = len(revs)
964 total = len(revs)
955 revwidth = max([len(str(rev)) for rev in revs])
965 revwidth = max([len(str(rev)) for rev in revs])
956 filemode = {}
966 filemode = {}
957
967
958 def single(rev, seqno, fp):
968 def single(rev, seqno, fp):
959 ctx = repo[rev]
969 ctx = repo[rev]
960 node = ctx.node()
970 node = ctx.node()
961 parents = [p.node() for p in ctx.parents() if p]
971 parents = [p.node() for p in ctx.parents() if p]
962 branch = ctx.branch()
972 branch = ctx.branch()
963 if switch_parent:
973 if switch_parent:
964 parents.reverse()
974 parents.reverse()
965
975
966 if parents:
976 if parents:
967 prev = parents[0]
977 prev = parents[0]
968 else:
978 else:
969 prev = nullid
979 prev = nullid
970
980
971 shouldclose = False
981 shouldclose = False
972 if not fp and len(template) > 0:
982 if not fp and len(template) > 0:
973 desc_lines = ctx.description().rstrip().split('\n')
983 desc_lines = ctx.description().rstrip().split('\n')
974 desc = desc_lines[0] #Commit always has a first line.
984 desc = desc_lines[0] #Commit always has a first line.
975 fp = makefileobj(repo, template, node, desc=desc, total=total,
985 fp = makefileobj(repo, template, node, desc=desc, total=total,
976 seqno=seqno, revwidth=revwidth, mode='wb',
986 seqno=seqno, revwidth=revwidth, mode='wb',
977 modemap=filemode)
987 modemap=filemode)
978 if fp != template:
988 if fp != template:
979 shouldclose = True
989 shouldclose = True
980 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
990 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
981 repo.ui.note("%s\n" % fp.name)
991 repo.ui.note("%s\n" % fp.name)
982
992
983 if not fp:
993 if not fp:
984 write = repo.ui.write
994 write = repo.ui.write
985 else:
995 else:
986 def write(s, **kw):
996 def write(s, **kw):
987 fp.write(s)
997 fp.write(s)
988
998
989 write("# HG changeset patch\n")
999 write("# HG changeset patch\n")
990 write("# User %s\n" % ctx.user())
1000 write("# User %s\n" % ctx.user())
991 write("# Date %d %d\n" % ctx.date())
1001 write("# Date %d %d\n" % ctx.date())
992 write("# %s\n" % util.datestr(ctx.date()))
1002 write("# %s\n" % util.datestr(ctx.date()))
993 if branch and branch != 'default':
1003 if branch and branch != 'default':
994 write("# Branch %s\n" % branch)
1004 write("# Branch %s\n" % branch)
995 write("# Node ID %s\n" % hex(node))
1005 write("# Node ID %s\n" % hex(node))
996 write("# Parent %s\n" % hex(prev))
1006 write("# Parent %s\n" % hex(prev))
997 if len(parents) > 1:
1007 if len(parents) > 1:
998 write("# Parent %s\n" % hex(parents[1]))
1008 write("# Parent %s\n" % hex(parents[1]))
999 write(ctx.description().rstrip())
1009 write(ctx.description().rstrip())
1000 write("\n\n")
1010 write("\n\n")
1001
1011
1002 for chunk, label in patch.diffui(repo, prev, node, opts=opts):
1012 for chunk, label in patch.diffui(repo, prev, node, opts=opts):
1003 write(chunk, label=label)
1013 write(chunk, label=label)
1004
1014
1005 if shouldclose:
1015 if shouldclose:
1006 fp.close()
1016 fp.close()
1007
1017
1008 for seqno, rev in enumerate(revs):
1018 for seqno, rev in enumerate(revs):
1009 single(rev, seqno + 1, fp)
1019 single(rev, seqno + 1, fp)
1010
1020
1011 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1021 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1012 changes=None, stat=False, fp=None, prefix='',
1022 changes=None, stat=False, fp=None, prefix='',
1013 root='', listsubrepos=False):
1023 root='', listsubrepos=False):
1014 '''show diff or diffstat.'''
1024 '''show diff or diffstat.'''
1015 if fp is None:
1025 if fp is None:
1016 write = ui.write
1026 write = ui.write
1017 else:
1027 else:
1018 def write(s, **kw):
1028 def write(s, **kw):
1019 fp.write(s)
1029 fp.write(s)
1020
1030
1021 if root:
1031 if root:
1022 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1032 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1023 else:
1033 else:
1024 relroot = ''
1034 relroot = ''
1025 if relroot != '':
1035 if relroot != '':
1026 # XXX relative roots currently don't work if the root is within a
1036 # XXX relative roots currently don't work if the root is within a
1027 # subrepo
1037 # subrepo
1028 uirelroot = match.uipath(relroot)
1038 uirelroot = match.uipath(relroot)
1029 relroot += '/'
1039 relroot += '/'
1030 for matchroot in match.files():
1040 for matchroot in match.files():
1031 if not matchroot.startswith(relroot):
1041 if not matchroot.startswith(relroot):
1032 ui.warn(_('warning: %s not inside relative root %s\n') % (
1042 ui.warn(_('warning: %s not inside relative root %s\n') % (
1033 match.uipath(matchroot), uirelroot))
1043 match.uipath(matchroot), uirelroot))
1034
1044
1035 if stat:
1045 if stat:
1036 diffopts = diffopts.copy(context=0)
1046 diffopts = diffopts.copy(context=0)
1037 width = 80
1047 width = 80
1038 if not ui.plain():
1048 if not ui.plain():
1039 width = ui.termwidth()
1049 width = ui.termwidth()
1040 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1050 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1041 prefix=prefix, relroot=relroot)
1051 prefix=prefix, relroot=relroot)
1042 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1052 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1043 width=width,
1053 width=width,
1044 git=diffopts.git):
1054 git=diffopts.git):
1045 write(chunk, label=label)
1055 write(chunk, label=label)
1046 else:
1056 else:
1047 for chunk, label in patch.diffui(repo, node1, node2, match,
1057 for chunk, label in patch.diffui(repo, node1, node2, match,
1048 changes, diffopts, prefix=prefix,
1058 changes, diffopts, prefix=prefix,
1049 relroot=relroot):
1059 relroot=relroot):
1050 write(chunk, label=label)
1060 write(chunk, label=label)
1051
1061
1052 if listsubrepos:
1062 if listsubrepos:
1053 ctx1 = repo[node1]
1063 ctx1 = repo[node1]
1054 ctx2 = repo[node2]
1064 ctx2 = repo[node2]
1055 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1065 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1056 tempnode2 = node2
1066 tempnode2 = node2
1057 try:
1067 try:
1058 if node2 is not None:
1068 if node2 is not None:
1059 tempnode2 = ctx2.substate[subpath][1]
1069 tempnode2 = ctx2.substate[subpath][1]
1060 except KeyError:
1070 except KeyError:
1061 # A subrepo that existed in node1 was deleted between node1 and
1071 # A subrepo that existed in node1 was deleted between node1 and
1062 # node2 (inclusive). Thus, ctx2's substate won't contain that
1072 # node2 (inclusive). Thus, ctx2's substate won't contain that
1063 # subpath. The best we can do is to ignore it.
1073 # subpath. The best we can do is to ignore it.
1064 tempnode2 = None
1074 tempnode2 = None
1065 submatch = matchmod.narrowmatcher(subpath, match)
1075 submatch = matchmod.narrowmatcher(subpath, match)
1066 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1076 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1067 stat=stat, fp=fp, prefix=prefix)
1077 stat=stat, fp=fp, prefix=prefix)
1068
1078
1069 class changeset_printer(object):
1079 class changeset_printer(object):
1070 '''show changeset information when templating not requested.'''
1080 '''show changeset information when templating not requested.'''
1071
1081
1072 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1082 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1073 self.ui = ui
1083 self.ui = ui
1074 self.repo = repo
1084 self.repo = repo
1075 self.buffered = buffered
1085 self.buffered = buffered
1076 self.matchfn = matchfn
1086 self.matchfn = matchfn
1077 self.diffopts = diffopts
1087 self.diffopts = diffopts
1078 self.header = {}
1088 self.header = {}
1079 self.hunk = {}
1089 self.hunk = {}
1080 self.lastheader = None
1090 self.lastheader = None
1081 self.footer = None
1091 self.footer = None
1082
1092
1083 def flush(self, rev):
1093 def flush(self, rev):
1084 if rev in self.header:
1094 if rev in self.header:
1085 h = self.header[rev]
1095 h = self.header[rev]
1086 if h != self.lastheader:
1096 if h != self.lastheader:
1087 self.lastheader = h
1097 self.lastheader = h
1088 self.ui.write(h)
1098 self.ui.write(h)
1089 del self.header[rev]
1099 del self.header[rev]
1090 if rev in self.hunk:
1100 if rev in self.hunk:
1091 self.ui.write(self.hunk[rev])
1101 self.ui.write(self.hunk[rev])
1092 del self.hunk[rev]
1102 del self.hunk[rev]
1093 return 1
1103 return 1
1094 return 0
1104 return 0
1095
1105
1096 def close(self):
1106 def close(self):
1097 if self.footer:
1107 if self.footer:
1098 self.ui.write(self.footer)
1108 self.ui.write(self.footer)
1099
1109
1100 def show(self, ctx, copies=None, matchfn=None, **props):
1110 def show(self, ctx, copies=None, matchfn=None, **props):
1101 if self.buffered:
1111 if self.buffered:
1102 self.ui.pushbuffer()
1112 self.ui.pushbuffer()
1103 self._show(ctx, copies, matchfn, props)
1113 self._show(ctx, copies, matchfn, props)
1104 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
1114 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
1105 else:
1115 else:
1106 self._show(ctx, copies, matchfn, props)
1116 self._show(ctx, copies, matchfn, props)
1107
1117
1108 def _show(self, ctx, copies, matchfn, props):
1118 def _show(self, ctx, copies, matchfn, props):
1109 '''show a single changeset or file revision'''
1119 '''show a single changeset or file revision'''
1110 changenode = ctx.node()
1120 changenode = ctx.node()
1111 rev = ctx.rev()
1121 rev = ctx.rev()
1112 if self.ui.debugflag:
1122 if self.ui.debugflag:
1113 hexfunc = hex
1123 hexfunc = hex
1114 else:
1124 else:
1115 hexfunc = short
1125 hexfunc = short
1116 if rev is None:
1126 if rev is None:
1117 pctx = ctx.p1()
1127 pctx = ctx.p1()
1118 revnode = (pctx.rev(), hexfunc(pctx.node()) + '+')
1128 revnode = (pctx.rev(), hexfunc(pctx.node()) + '+')
1119 else:
1129 else:
1120 revnode = (rev, hexfunc(changenode))
1130 revnode = (rev, hexfunc(changenode))
1121
1131
1122 if self.ui.quiet:
1132 if self.ui.quiet:
1123 self.ui.write("%d:%s\n" % revnode, label='log.node')
1133 self.ui.write("%d:%s\n" % revnode, label='log.node')
1124 return
1134 return
1125
1135
1126 date = util.datestr(ctx.date())
1136 date = util.datestr(ctx.date())
1127
1137
1128 # i18n: column positioning for "hg log"
1138 # i18n: column positioning for "hg log"
1129 self.ui.write(_("changeset: %d:%s\n") % revnode,
1139 self.ui.write(_("changeset: %d:%s\n") % revnode,
1130 label='log.changeset changeset.%s' % ctx.phasestr())
1140 label='log.changeset changeset.%s' % ctx.phasestr())
1131
1141
1132 # branches are shown first before any other names due to backwards
1142 # branches are shown first before any other names due to backwards
1133 # compatibility
1143 # compatibility
1134 branch = ctx.branch()
1144 branch = ctx.branch()
1135 # don't show the default branch name
1145 # don't show the default branch name
1136 if branch != 'default':
1146 if branch != 'default':
1137 # i18n: column positioning for "hg log"
1147 # i18n: column positioning for "hg log"
1138 self.ui.write(_("branch: %s\n") % branch,
1148 self.ui.write(_("branch: %s\n") % branch,
1139 label='log.branch')
1149 label='log.branch')
1140
1150
1141 for name, ns in self.repo.names.iteritems():
1151 for name, ns in self.repo.names.iteritems():
1142 # branches has special logic already handled above, so here we just
1152 # branches has special logic already handled above, so here we just
1143 # skip it
1153 # skip it
1144 if name == 'branches':
1154 if name == 'branches':
1145 continue
1155 continue
1146 # we will use the templatename as the color name since those two
1156 # we will use the templatename as the color name since those two
1147 # should be the same
1157 # should be the same
1148 for name in ns.names(self.repo, changenode):
1158 for name in ns.names(self.repo, changenode):
1149 self.ui.write(ns.logfmt % name,
1159 self.ui.write(ns.logfmt % name,
1150 label='log.%s' % ns.colorname)
1160 label='log.%s' % ns.colorname)
1151 if self.ui.debugflag:
1161 if self.ui.debugflag:
1152 # i18n: column positioning for "hg log"
1162 # i18n: column positioning for "hg log"
1153 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1163 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1154 label='log.phase')
1164 label='log.phase')
1155 for pctx in self._meaningful_parentrevs(ctx):
1165 for pctx in self._meaningful_parentrevs(ctx):
1156 label = 'log.parent changeset.%s' % pctx.phasestr()
1166 label = 'log.parent changeset.%s' % pctx.phasestr()
1157 # i18n: column positioning for "hg log"
1167 # i18n: column positioning for "hg log"
1158 self.ui.write(_("parent: %d:%s\n")
1168 self.ui.write(_("parent: %d:%s\n")
1159 % (pctx.rev(), hexfunc(pctx.node())),
1169 % (pctx.rev(), hexfunc(pctx.node())),
1160 label=label)
1170 label=label)
1161
1171
1162 if self.ui.debugflag and rev is not None:
1172 if self.ui.debugflag and rev is not None:
1163 mnode = ctx.manifestnode()
1173 mnode = ctx.manifestnode()
1164 # i18n: column positioning for "hg log"
1174 # i18n: column positioning for "hg log"
1165 self.ui.write(_("manifest: %d:%s\n") %
1175 self.ui.write(_("manifest: %d:%s\n") %
1166 (self.repo.manifest.rev(mnode), hex(mnode)),
1176 (self.repo.manifest.rev(mnode), hex(mnode)),
1167 label='ui.debug log.manifest')
1177 label='ui.debug log.manifest')
1168 # i18n: column positioning for "hg log"
1178 # i18n: column positioning for "hg log"
1169 self.ui.write(_("user: %s\n") % ctx.user(),
1179 self.ui.write(_("user: %s\n") % ctx.user(),
1170 label='log.user')
1180 label='log.user')
1171 # i18n: column positioning for "hg log"
1181 # i18n: column positioning for "hg log"
1172 self.ui.write(_("date: %s\n") % date,
1182 self.ui.write(_("date: %s\n") % date,
1173 label='log.date')
1183 label='log.date')
1174
1184
1175 if self.ui.debugflag:
1185 if self.ui.debugflag:
1176 files = ctx.p1().status(ctx)[:3]
1186 files = ctx.p1().status(ctx)[:3]
1177 for key, value in zip([# i18n: column positioning for "hg log"
1187 for key, value in zip([# i18n: column positioning for "hg log"
1178 _("files:"),
1188 _("files:"),
1179 # i18n: column positioning for "hg log"
1189 # i18n: column positioning for "hg log"
1180 _("files+:"),
1190 _("files+:"),
1181 # i18n: column positioning for "hg log"
1191 # i18n: column positioning for "hg log"
1182 _("files-:")], files):
1192 _("files-:")], files):
1183 if value:
1193 if value:
1184 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1194 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1185 label='ui.debug log.files')
1195 label='ui.debug log.files')
1186 elif ctx.files() and self.ui.verbose:
1196 elif ctx.files() and self.ui.verbose:
1187 # i18n: column positioning for "hg log"
1197 # i18n: column positioning for "hg log"
1188 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1198 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1189 label='ui.note log.files')
1199 label='ui.note log.files')
1190 if copies and self.ui.verbose:
1200 if copies and self.ui.verbose:
1191 copies = ['%s (%s)' % c for c in copies]
1201 copies = ['%s (%s)' % c for c in copies]
1192 # i18n: column positioning for "hg log"
1202 # i18n: column positioning for "hg log"
1193 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1203 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1194 label='ui.note log.copies')
1204 label='ui.note log.copies')
1195
1205
1196 extra = ctx.extra()
1206 extra = ctx.extra()
1197 if extra and self.ui.debugflag:
1207 if extra and self.ui.debugflag:
1198 for key, value in sorted(extra.items()):
1208 for key, value in sorted(extra.items()):
1199 # i18n: column positioning for "hg log"
1209 # i18n: column positioning for "hg log"
1200 self.ui.write(_("extra: %s=%s\n")
1210 self.ui.write(_("extra: %s=%s\n")
1201 % (key, value.encode('string_escape')),
1211 % (key, value.encode('string_escape')),
1202 label='ui.debug log.extra')
1212 label='ui.debug log.extra')
1203
1213
1204 description = ctx.description().strip()
1214 description = ctx.description().strip()
1205 if description:
1215 if description:
1206 if self.ui.verbose:
1216 if self.ui.verbose:
1207 self.ui.write(_("description:\n"),
1217 self.ui.write(_("description:\n"),
1208 label='ui.note log.description')
1218 label='ui.note log.description')
1209 self.ui.write(description,
1219 self.ui.write(description,
1210 label='ui.note log.description')
1220 label='ui.note log.description')
1211 self.ui.write("\n\n")
1221 self.ui.write("\n\n")
1212 else:
1222 else:
1213 # i18n: column positioning for "hg log"
1223 # i18n: column positioning for "hg log"
1214 self.ui.write(_("summary: %s\n") %
1224 self.ui.write(_("summary: %s\n") %
1215 description.splitlines()[0],
1225 description.splitlines()[0],
1216 label='log.summary')
1226 label='log.summary')
1217 self.ui.write("\n")
1227 self.ui.write("\n")
1218
1228
1219 self.showpatch(changenode, matchfn)
1229 self.showpatch(changenode, matchfn)
1220
1230
1221 def showpatch(self, node, matchfn):
1231 def showpatch(self, node, matchfn):
1222 if not matchfn:
1232 if not matchfn:
1223 matchfn = self.matchfn
1233 matchfn = self.matchfn
1224 if matchfn:
1234 if matchfn:
1225 stat = self.diffopts.get('stat')
1235 stat = self.diffopts.get('stat')
1226 diff = self.diffopts.get('patch')
1236 diff = self.diffopts.get('patch')
1227 diffopts = patch.diffallopts(self.ui, self.diffopts)
1237 diffopts = patch.diffallopts(self.ui, self.diffopts)
1228 prev = self.repo.changelog.parents(node)[0]
1238 prev = self.repo.changelog.parents(node)[0]
1229 if stat:
1239 if stat:
1230 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1240 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1231 match=matchfn, stat=True)
1241 match=matchfn, stat=True)
1232 if diff:
1242 if diff:
1233 if stat:
1243 if stat:
1234 self.ui.write("\n")
1244 self.ui.write("\n")
1235 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1245 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1236 match=matchfn, stat=False)
1246 match=matchfn, stat=False)
1237 self.ui.write("\n")
1247 self.ui.write("\n")
1238
1248
1239 def _meaningful_parentrevs(self, ctx):
1249 def _meaningful_parentrevs(self, ctx):
1240 """Return list of meaningful (or all if debug) parentrevs for rev.
1250 """Return list of meaningful (or all if debug) parentrevs for rev.
1241
1251
1242 For merges (two non-nullrev revisions) both parents are meaningful.
1252 For merges (two non-nullrev revisions) both parents are meaningful.
1243 Otherwise the first parent revision is considered meaningful if it
1253 Otherwise the first parent revision is considered meaningful if it
1244 is not the preceding revision.
1254 is not the preceding revision.
1245 """
1255 """
1246 parents = ctx.parents()
1256 parents = ctx.parents()
1247 if len(parents) > 1:
1257 if len(parents) > 1:
1248 return parents
1258 return parents
1249 if self.ui.debugflag:
1259 if self.ui.debugflag:
1250 return [parents[0], self.repo['null']]
1260 return [parents[0], self.repo['null']]
1251 if parents[0].rev() >= scmutil.intrev(self.repo, ctx.rev()) - 1:
1261 if parents[0].rev() >= scmutil.intrev(self.repo, ctx.rev()) - 1:
1252 return []
1262 return []
1253 return parents
1263 return parents
1254
1264
1255 class jsonchangeset(changeset_printer):
1265 class jsonchangeset(changeset_printer):
1256 '''format changeset information.'''
1266 '''format changeset information.'''
1257
1267
1258 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1268 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1259 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1269 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1260 self.cache = {}
1270 self.cache = {}
1261 self._first = True
1271 self._first = True
1262
1272
1263 def close(self):
1273 def close(self):
1264 if not self._first:
1274 if not self._first:
1265 self.ui.write("\n]\n")
1275 self.ui.write("\n]\n")
1266 else:
1276 else:
1267 self.ui.write("[]\n")
1277 self.ui.write("[]\n")
1268
1278
1269 def _show(self, ctx, copies, matchfn, props):
1279 def _show(self, ctx, copies, matchfn, props):
1270 '''show a single changeset or file revision'''
1280 '''show a single changeset or file revision'''
1271 rev = ctx.rev()
1281 rev = ctx.rev()
1272 if rev is None:
1282 if rev is None:
1273 jrev = jnode = 'null'
1283 jrev = jnode = 'null'
1274 else:
1284 else:
1275 jrev = str(rev)
1285 jrev = str(rev)
1276 jnode = '"%s"' % hex(ctx.node())
1286 jnode = '"%s"' % hex(ctx.node())
1277 j = encoding.jsonescape
1287 j = encoding.jsonescape
1278
1288
1279 if self._first:
1289 if self._first:
1280 self.ui.write("[\n {")
1290 self.ui.write("[\n {")
1281 self._first = False
1291 self._first = False
1282 else:
1292 else:
1283 self.ui.write(",\n {")
1293 self.ui.write(",\n {")
1284
1294
1285 if self.ui.quiet:
1295 if self.ui.quiet:
1286 self.ui.write('\n "rev": %s' % jrev)
1296 self.ui.write('\n "rev": %s' % jrev)
1287 self.ui.write(',\n "node": %s' % jnode)
1297 self.ui.write(',\n "node": %s' % jnode)
1288 self.ui.write('\n }')
1298 self.ui.write('\n }')
1289 return
1299 return
1290
1300
1291 self.ui.write('\n "rev": %s' % jrev)
1301 self.ui.write('\n "rev": %s' % jrev)
1292 self.ui.write(',\n "node": %s' % jnode)
1302 self.ui.write(',\n "node": %s' % jnode)
1293 self.ui.write(',\n "branch": "%s"' % j(ctx.branch()))
1303 self.ui.write(',\n "branch": "%s"' % j(ctx.branch()))
1294 self.ui.write(',\n "phase": "%s"' % ctx.phasestr())
1304 self.ui.write(',\n "phase": "%s"' % ctx.phasestr())
1295 self.ui.write(',\n "user": "%s"' % j(ctx.user()))
1305 self.ui.write(',\n "user": "%s"' % j(ctx.user()))
1296 self.ui.write(',\n "date": [%d, %d]' % ctx.date())
1306 self.ui.write(',\n "date": [%d, %d]' % ctx.date())
1297 self.ui.write(',\n "desc": "%s"' % j(ctx.description()))
1307 self.ui.write(',\n "desc": "%s"' % j(ctx.description()))
1298
1308
1299 self.ui.write(',\n "bookmarks": [%s]' %
1309 self.ui.write(',\n "bookmarks": [%s]' %
1300 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1310 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1301 self.ui.write(',\n "tags": [%s]' %
1311 self.ui.write(',\n "tags": [%s]' %
1302 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1312 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1303 self.ui.write(',\n "parents": [%s]' %
1313 self.ui.write(',\n "parents": [%s]' %
1304 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1314 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1305
1315
1306 if self.ui.debugflag:
1316 if self.ui.debugflag:
1307 if rev is None:
1317 if rev is None:
1308 jmanifestnode = 'null'
1318 jmanifestnode = 'null'
1309 else:
1319 else:
1310 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1320 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1311 self.ui.write(',\n "manifest": %s' % jmanifestnode)
1321 self.ui.write(',\n "manifest": %s' % jmanifestnode)
1312
1322
1313 self.ui.write(',\n "extra": {%s}' %
1323 self.ui.write(',\n "extra": {%s}' %
1314 ", ".join('"%s": "%s"' % (j(k), j(v))
1324 ", ".join('"%s": "%s"' % (j(k), j(v))
1315 for k, v in ctx.extra().items()))
1325 for k, v in ctx.extra().items()))
1316
1326
1317 files = ctx.p1().status(ctx)
1327 files = ctx.p1().status(ctx)
1318 self.ui.write(',\n "modified": [%s]' %
1328 self.ui.write(',\n "modified": [%s]' %
1319 ", ".join('"%s"' % j(f) for f in files[0]))
1329 ", ".join('"%s"' % j(f) for f in files[0]))
1320 self.ui.write(',\n "added": [%s]' %
1330 self.ui.write(',\n "added": [%s]' %
1321 ", ".join('"%s"' % j(f) for f in files[1]))
1331 ", ".join('"%s"' % j(f) for f in files[1]))
1322 self.ui.write(',\n "removed": [%s]' %
1332 self.ui.write(',\n "removed": [%s]' %
1323 ", ".join('"%s"' % j(f) for f in files[2]))
1333 ", ".join('"%s"' % j(f) for f in files[2]))
1324
1334
1325 elif self.ui.verbose:
1335 elif self.ui.verbose:
1326 self.ui.write(',\n "files": [%s]' %
1336 self.ui.write(',\n "files": [%s]' %
1327 ", ".join('"%s"' % j(f) for f in ctx.files()))
1337 ", ".join('"%s"' % j(f) for f in ctx.files()))
1328
1338
1329 if copies:
1339 if copies:
1330 self.ui.write(',\n "copies": {%s}' %
1340 self.ui.write(',\n "copies": {%s}' %
1331 ", ".join('"%s": "%s"' % (j(k), j(v))
1341 ", ".join('"%s": "%s"' % (j(k), j(v))
1332 for k, v in copies))
1342 for k, v in copies))
1333
1343
1334 matchfn = self.matchfn
1344 matchfn = self.matchfn
1335 if matchfn:
1345 if matchfn:
1336 stat = self.diffopts.get('stat')
1346 stat = self.diffopts.get('stat')
1337 diff = self.diffopts.get('patch')
1347 diff = self.diffopts.get('patch')
1338 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1348 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1339 node, prev = ctx.node(), ctx.p1().node()
1349 node, prev = ctx.node(), ctx.p1().node()
1340 if stat:
1350 if stat:
1341 self.ui.pushbuffer()
1351 self.ui.pushbuffer()
1342 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1352 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1343 match=matchfn, stat=True)
1353 match=matchfn, stat=True)
1344 self.ui.write(',\n "diffstat": "%s"' % j(self.ui.popbuffer()))
1354 self.ui.write(',\n "diffstat": "%s"' % j(self.ui.popbuffer()))
1345 if diff:
1355 if diff:
1346 self.ui.pushbuffer()
1356 self.ui.pushbuffer()
1347 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1357 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1348 match=matchfn, stat=False)
1358 match=matchfn, stat=False)
1349 self.ui.write(',\n "diff": "%s"' % j(self.ui.popbuffer()))
1359 self.ui.write(',\n "diff": "%s"' % j(self.ui.popbuffer()))
1350
1360
1351 self.ui.write("\n }")
1361 self.ui.write("\n }")
1352
1362
1353 class changeset_templater(changeset_printer):
1363 class changeset_templater(changeset_printer):
1354 '''format changeset information.'''
1364 '''format changeset information.'''
1355
1365
1356 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1366 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1357 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1367 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1358 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1368 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1359 defaulttempl = {
1369 defaulttempl = {
1360 'parent': '{rev}:{node|formatnode} ',
1370 'parent': '{rev}:{node|formatnode} ',
1361 'manifest': '{rev}:{node|formatnode}',
1371 'manifest': '{rev}:{node|formatnode}',
1362 'file_copy': '{name} ({source})',
1372 'file_copy': '{name} ({source})',
1363 'extra': '{key}={value|stringescape}'
1373 'extra': '{key}={value|stringescape}'
1364 }
1374 }
1365 # filecopy is preserved for compatibility reasons
1375 # filecopy is preserved for compatibility reasons
1366 defaulttempl['filecopy'] = defaulttempl['file_copy']
1376 defaulttempl['filecopy'] = defaulttempl['file_copy']
1367 self.t = templater.templater(mapfile, {'formatnode': formatnode},
1377 self.t = templater.templater(mapfile, {'formatnode': formatnode},
1368 cache=defaulttempl)
1378 cache=defaulttempl)
1369 if tmpl:
1379 if tmpl:
1370 self.t.cache['changeset'] = tmpl
1380 self.t.cache['changeset'] = tmpl
1371
1381
1372 self.cache = {}
1382 self.cache = {}
1373
1383
1374 def _show(self, ctx, copies, matchfn, props):
1384 def _show(self, ctx, copies, matchfn, props):
1375 '''show a single changeset or file revision'''
1385 '''show a single changeset or file revision'''
1376
1386
1377 showlist = templatekw.showlist
1387 showlist = templatekw.showlist
1378
1388
1379 # showparents() behaviour depends on ui trace level which
1389 # showparents() behaviour depends on ui trace level which
1380 # causes unexpected behaviours at templating level and makes
1390 # causes unexpected behaviours at templating level and makes
1381 # it harder to extract it in a standalone function. Its
1391 # it harder to extract it in a standalone function. Its
1382 # behaviour cannot be changed so leave it here for now.
1392 # behaviour cannot be changed so leave it here for now.
1383 def showparents(**args):
1393 def showparents(**args):
1384 ctx = args['ctx']
1394 ctx = args['ctx']
1385 parents = [[('rev', p.rev()),
1395 parents = [[('rev', p.rev()),
1386 ('node', p.hex()),
1396 ('node', p.hex()),
1387 ('phase', p.phasestr())]
1397 ('phase', p.phasestr())]
1388 for p in self._meaningful_parentrevs(ctx)]
1398 for p in self._meaningful_parentrevs(ctx)]
1389 return showlist('parent', parents, **args)
1399 return showlist('parent', parents, **args)
1390
1400
1391 props = props.copy()
1401 props = props.copy()
1392 props.update(templatekw.keywords)
1402 props.update(templatekw.keywords)
1393 props['parents'] = showparents
1403 props['parents'] = showparents
1394 props['templ'] = self.t
1404 props['templ'] = self.t
1395 props['ctx'] = ctx
1405 props['ctx'] = ctx
1396 props['repo'] = self.repo
1406 props['repo'] = self.repo
1397 props['revcache'] = {'copies': copies}
1407 props['revcache'] = {'copies': copies}
1398 props['cache'] = self.cache
1408 props['cache'] = self.cache
1399
1409
1400 # find correct templates for current mode
1410 # find correct templates for current mode
1401
1411
1402 tmplmodes = [
1412 tmplmodes = [
1403 (True, None),
1413 (True, None),
1404 (self.ui.verbose, 'verbose'),
1414 (self.ui.verbose, 'verbose'),
1405 (self.ui.quiet, 'quiet'),
1415 (self.ui.quiet, 'quiet'),
1406 (self.ui.debugflag, 'debug'),
1416 (self.ui.debugflag, 'debug'),
1407 ]
1417 ]
1408
1418
1409 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
1419 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
1410 for mode, postfix in tmplmodes:
1420 for mode, postfix in tmplmodes:
1411 for type in types:
1421 for type in types:
1412 cur = postfix and ('%s_%s' % (type, postfix)) or type
1422 cur = postfix and ('%s_%s' % (type, postfix)) or type
1413 if mode and cur in self.t:
1423 if mode and cur in self.t:
1414 types[type] = cur
1424 types[type] = cur
1415
1425
1416 try:
1426 try:
1417
1427
1418 # write header
1428 # write header
1419 if types['header']:
1429 if types['header']:
1420 h = templater.stringify(self.t(types['header'], **props))
1430 h = templater.stringify(self.t(types['header'], **props))
1421 if self.buffered:
1431 if self.buffered:
1422 self.header[ctx.rev()] = h
1432 self.header[ctx.rev()] = h
1423 else:
1433 else:
1424 if self.lastheader != h:
1434 if self.lastheader != h:
1425 self.lastheader = h
1435 self.lastheader = h
1426 self.ui.write(h)
1436 self.ui.write(h)
1427
1437
1428 # write changeset metadata, then patch if requested
1438 # write changeset metadata, then patch if requested
1429 key = types['changeset']
1439 key = types['changeset']
1430 self.ui.write(templater.stringify(self.t(key, **props)))
1440 self.ui.write(templater.stringify(self.t(key, **props)))
1431 self.showpatch(ctx.node(), matchfn)
1441 self.showpatch(ctx.node(), matchfn)
1432
1442
1433 if types['footer']:
1443 if types['footer']:
1434 if not self.footer:
1444 if not self.footer:
1435 self.footer = templater.stringify(self.t(types['footer'],
1445 self.footer = templater.stringify(self.t(types['footer'],
1436 **props))
1446 **props))
1437
1447
1438 except KeyError, inst:
1448 except KeyError, inst:
1439 msg = _("%s: no key named '%s'")
1449 msg = _("%s: no key named '%s'")
1440 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
1450 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
1441 except SyntaxError, inst:
1451 except SyntaxError, inst:
1442 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
1452 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
1443
1453
1444 def gettemplate(ui, tmpl, style):
1454 def gettemplate(ui, tmpl, style):
1445 """
1455 """
1446 Find the template matching the given template spec or style.
1456 Find the template matching the given template spec or style.
1447 """
1457 """
1448
1458
1449 # ui settings
1459 # ui settings
1450 if not tmpl and not style: # template are stronger than style
1460 if not tmpl and not style: # template are stronger than style
1451 tmpl = ui.config('ui', 'logtemplate')
1461 tmpl = ui.config('ui', 'logtemplate')
1452 if tmpl:
1462 if tmpl:
1453 try:
1463 try:
1454 tmpl = templater.unquotestring(tmpl)
1464 tmpl = templater.unquotestring(tmpl)
1455 except SyntaxError:
1465 except SyntaxError:
1456 pass
1466 pass
1457 return tmpl, None
1467 return tmpl, None
1458 else:
1468 else:
1459 style = util.expandpath(ui.config('ui', 'style', ''))
1469 style = util.expandpath(ui.config('ui', 'style', ''))
1460
1470
1461 if not tmpl and style:
1471 if not tmpl and style:
1462 mapfile = style
1472 mapfile = style
1463 if not os.path.split(mapfile)[0]:
1473 if not os.path.split(mapfile)[0]:
1464 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1474 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1465 or templater.templatepath(mapfile))
1475 or templater.templatepath(mapfile))
1466 if mapname:
1476 if mapname:
1467 mapfile = mapname
1477 mapfile = mapname
1468 return None, mapfile
1478 return None, mapfile
1469
1479
1470 if not tmpl:
1480 if not tmpl:
1471 return None, None
1481 return None, None
1472
1482
1473 # looks like a literal template?
1483 # looks like a literal template?
1474 if '{' in tmpl:
1484 if '{' in tmpl:
1475 return tmpl, None
1485 return tmpl, None
1476
1486
1477 # perhaps a stock style?
1487 # perhaps a stock style?
1478 if not os.path.split(tmpl)[0]:
1488 if not os.path.split(tmpl)[0]:
1479 mapname = (templater.templatepath('map-cmdline.' + tmpl)
1489 mapname = (templater.templatepath('map-cmdline.' + tmpl)
1480 or templater.templatepath(tmpl))
1490 or templater.templatepath(tmpl))
1481 if mapname and os.path.isfile(mapname):
1491 if mapname and os.path.isfile(mapname):
1482 return None, mapname
1492 return None, mapname
1483
1493
1484 # perhaps it's a reference to [templates]
1494 # perhaps it's a reference to [templates]
1485 t = ui.config('templates', tmpl)
1495 t = ui.config('templates', tmpl)
1486 if t:
1496 if t:
1487 try:
1497 try:
1488 tmpl = templater.unquotestring(t)
1498 tmpl = templater.unquotestring(t)
1489 except SyntaxError:
1499 except SyntaxError:
1490 tmpl = t
1500 tmpl = t
1491 return tmpl, None
1501 return tmpl, None
1492
1502
1493 if tmpl == 'list':
1503 if tmpl == 'list':
1494 ui.write(_("available styles: %s\n") % templater.stylelist())
1504 ui.write(_("available styles: %s\n") % templater.stylelist())
1495 raise util.Abort(_("specify a template"))
1505 raise util.Abort(_("specify a template"))
1496
1506
1497 # perhaps it's a path to a map or a template
1507 # perhaps it's a path to a map or a template
1498 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
1508 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
1499 # is it a mapfile for a style?
1509 # is it a mapfile for a style?
1500 if os.path.basename(tmpl).startswith("map-"):
1510 if os.path.basename(tmpl).startswith("map-"):
1501 return None, os.path.realpath(tmpl)
1511 return None, os.path.realpath(tmpl)
1502 tmpl = open(tmpl).read()
1512 tmpl = open(tmpl).read()
1503 return tmpl, None
1513 return tmpl, None
1504
1514
1505 # constant string?
1515 # constant string?
1506 return tmpl, None
1516 return tmpl, None
1507
1517
1508 def show_changeset(ui, repo, opts, buffered=False):
1518 def show_changeset(ui, repo, opts, buffered=False):
1509 """show one changeset using template or regular display.
1519 """show one changeset using template or regular display.
1510
1520
1511 Display format will be the first non-empty hit of:
1521 Display format will be the first non-empty hit of:
1512 1. option 'template'
1522 1. option 'template'
1513 2. option 'style'
1523 2. option 'style'
1514 3. [ui] setting 'logtemplate'
1524 3. [ui] setting 'logtemplate'
1515 4. [ui] setting 'style'
1525 4. [ui] setting 'style'
1516 If all of these values are either the unset or the empty string,
1526 If all of these values are either the unset or the empty string,
1517 regular display via changeset_printer() is done.
1527 regular display via changeset_printer() is done.
1518 """
1528 """
1519 # options
1529 # options
1520 matchfn = None
1530 matchfn = None
1521 if opts.get('patch') or opts.get('stat'):
1531 if opts.get('patch') or opts.get('stat'):
1522 matchfn = scmutil.matchall(repo)
1532 matchfn = scmutil.matchall(repo)
1523
1533
1524 if opts.get('template') == 'json':
1534 if opts.get('template') == 'json':
1525 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1535 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1526
1536
1527 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1537 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1528
1538
1529 if not tmpl and not mapfile:
1539 if not tmpl and not mapfile:
1530 return changeset_printer(ui, repo, matchfn, opts, buffered)
1540 return changeset_printer(ui, repo, matchfn, opts, buffered)
1531
1541
1532 try:
1542 try:
1533 t = changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile,
1543 t = changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile,
1534 buffered)
1544 buffered)
1535 except SyntaxError, inst:
1545 except SyntaxError, inst:
1536 raise util.Abort(inst.args[0])
1546 raise util.Abort(inst.args[0])
1537 return t
1547 return t
1538
1548
1539 def showmarker(ui, marker):
1549 def showmarker(ui, marker):
1540 """utility function to display obsolescence marker in a readable way
1550 """utility function to display obsolescence marker in a readable way
1541
1551
1542 To be used by debug function."""
1552 To be used by debug function."""
1543 ui.write(hex(marker.precnode()))
1553 ui.write(hex(marker.precnode()))
1544 for repl in marker.succnodes():
1554 for repl in marker.succnodes():
1545 ui.write(' ')
1555 ui.write(' ')
1546 ui.write(hex(repl))
1556 ui.write(hex(repl))
1547 ui.write(' %X ' % marker.flags())
1557 ui.write(' %X ' % marker.flags())
1548 parents = marker.parentnodes()
1558 parents = marker.parentnodes()
1549 if parents is not None:
1559 if parents is not None:
1550 ui.write('{%s} ' % ', '.join(hex(p) for p in parents))
1560 ui.write('{%s} ' % ', '.join(hex(p) for p in parents))
1551 ui.write('(%s) ' % util.datestr(marker.date()))
1561 ui.write('(%s) ' % util.datestr(marker.date()))
1552 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1562 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1553 sorted(marker.metadata().items())
1563 sorted(marker.metadata().items())
1554 if t[0] != 'date')))
1564 if t[0] != 'date')))
1555 ui.write('\n')
1565 ui.write('\n')
1556
1566
1557 def finddate(ui, repo, date):
1567 def finddate(ui, repo, date):
1558 """Find the tipmost changeset that matches the given date spec"""
1568 """Find the tipmost changeset that matches the given date spec"""
1559
1569
1560 df = util.matchdate(date)
1570 df = util.matchdate(date)
1561 m = scmutil.matchall(repo)
1571 m = scmutil.matchall(repo)
1562 results = {}
1572 results = {}
1563
1573
1564 def prep(ctx, fns):
1574 def prep(ctx, fns):
1565 d = ctx.date()
1575 d = ctx.date()
1566 if df(d[0]):
1576 if df(d[0]):
1567 results[ctx.rev()] = d
1577 results[ctx.rev()] = d
1568
1578
1569 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1579 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1570 rev = ctx.rev()
1580 rev = ctx.rev()
1571 if rev in results:
1581 if rev in results:
1572 ui.status(_("found revision %s from %s\n") %
1582 ui.status(_("found revision %s from %s\n") %
1573 (rev, util.datestr(results[rev])))
1583 (rev, util.datestr(results[rev])))
1574 return str(rev)
1584 return str(rev)
1575
1585
1576 raise util.Abort(_("revision matching date not found"))
1586 raise util.Abort(_("revision matching date not found"))
1577
1587
1578 def increasingwindows(windowsize=8, sizelimit=512):
1588 def increasingwindows(windowsize=8, sizelimit=512):
1579 while True:
1589 while True:
1580 yield windowsize
1590 yield windowsize
1581 if windowsize < sizelimit:
1591 if windowsize < sizelimit:
1582 windowsize *= 2
1592 windowsize *= 2
1583
1593
1584 class FileWalkError(Exception):
1594 class FileWalkError(Exception):
1585 pass
1595 pass
1586
1596
1587 def walkfilerevs(repo, match, follow, revs, fncache):
1597 def walkfilerevs(repo, match, follow, revs, fncache):
1588 '''Walks the file history for the matched files.
1598 '''Walks the file history for the matched files.
1589
1599
1590 Returns the changeset revs that are involved in the file history.
1600 Returns the changeset revs that are involved in the file history.
1591
1601
1592 Throws FileWalkError if the file history can't be walked using
1602 Throws FileWalkError if the file history can't be walked using
1593 filelogs alone.
1603 filelogs alone.
1594 '''
1604 '''
1595 wanted = set()
1605 wanted = set()
1596 copies = []
1606 copies = []
1597 minrev, maxrev = min(revs), max(revs)
1607 minrev, maxrev = min(revs), max(revs)
1598 def filerevgen(filelog, last):
1608 def filerevgen(filelog, last):
1599 """
1609 """
1600 Only files, no patterns. Check the history of each file.
1610 Only files, no patterns. Check the history of each file.
1601
1611
1602 Examines filelog entries within minrev, maxrev linkrev range
1612 Examines filelog entries within minrev, maxrev linkrev range
1603 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1613 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1604 tuples in backwards order
1614 tuples in backwards order
1605 """
1615 """
1606 cl_count = len(repo)
1616 cl_count = len(repo)
1607 revs = []
1617 revs = []
1608 for j in xrange(0, last + 1):
1618 for j in xrange(0, last + 1):
1609 linkrev = filelog.linkrev(j)
1619 linkrev = filelog.linkrev(j)
1610 if linkrev < minrev:
1620 if linkrev < minrev:
1611 continue
1621 continue
1612 # only yield rev for which we have the changelog, it can
1622 # only yield rev for which we have the changelog, it can
1613 # happen while doing "hg log" during a pull or commit
1623 # happen while doing "hg log" during a pull or commit
1614 if linkrev >= cl_count:
1624 if linkrev >= cl_count:
1615 break
1625 break
1616
1626
1617 parentlinkrevs = []
1627 parentlinkrevs = []
1618 for p in filelog.parentrevs(j):
1628 for p in filelog.parentrevs(j):
1619 if p != nullrev:
1629 if p != nullrev:
1620 parentlinkrevs.append(filelog.linkrev(p))
1630 parentlinkrevs.append(filelog.linkrev(p))
1621 n = filelog.node(j)
1631 n = filelog.node(j)
1622 revs.append((linkrev, parentlinkrevs,
1632 revs.append((linkrev, parentlinkrevs,
1623 follow and filelog.renamed(n)))
1633 follow and filelog.renamed(n)))
1624
1634
1625 return reversed(revs)
1635 return reversed(revs)
1626 def iterfiles():
1636 def iterfiles():
1627 pctx = repo['.']
1637 pctx = repo['.']
1628 for filename in match.files():
1638 for filename in match.files():
1629 if follow:
1639 if follow:
1630 if filename not in pctx:
1640 if filename not in pctx:
1631 raise util.Abort(_('cannot follow file not in parent '
1641 raise util.Abort(_('cannot follow file not in parent '
1632 'revision: "%s"') % filename)
1642 'revision: "%s"') % filename)
1633 yield filename, pctx[filename].filenode()
1643 yield filename, pctx[filename].filenode()
1634 else:
1644 else:
1635 yield filename, None
1645 yield filename, None
1636 for filename_node in copies:
1646 for filename_node in copies:
1637 yield filename_node
1647 yield filename_node
1638
1648
1639 for file_, node in iterfiles():
1649 for file_, node in iterfiles():
1640 filelog = repo.file(file_)
1650 filelog = repo.file(file_)
1641 if not len(filelog):
1651 if not len(filelog):
1642 if node is None:
1652 if node is None:
1643 # A zero count may be a directory or deleted file, so
1653 # A zero count may be a directory or deleted file, so
1644 # try to find matching entries on the slow path.
1654 # try to find matching entries on the slow path.
1645 if follow:
1655 if follow:
1646 raise util.Abort(
1656 raise util.Abort(
1647 _('cannot follow nonexistent file: "%s"') % file_)
1657 _('cannot follow nonexistent file: "%s"') % file_)
1648 raise FileWalkError("Cannot walk via filelog")
1658 raise FileWalkError("Cannot walk via filelog")
1649 else:
1659 else:
1650 continue
1660 continue
1651
1661
1652 if node is None:
1662 if node is None:
1653 last = len(filelog) - 1
1663 last = len(filelog) - 1
1654 else:
1664 else:
1655 last = filelog.rev(node)
1665 last = filelog.rev(node)
1656
1666
1657 # keep track of all ancestors of the file
1667 # keep track of all ancestors of the file
1658 ancestors = set([filelog.linkrev(last)])
1668 ancestors = set([filelog.linkrev(last)])
1659
1669
1660 # iterate from latest to oldest revision
1670 # iterate from latest to oldest revision
1661 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1671 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1662 if not follow:
1672 if not follow:
1663 if rev > maxrev:
1673 if rev > maxrev:
1664 continue
1674 continue
1665 else:
1675 else:
1666 # Note that last might not be the first interesting
1676 # Note that last might not be the first interesting
1667 # rev to us:
1677 # rev to us:
1668 # if the file has been changed after maxrev, we'll
1678 # if the file has been changed after maxrev, we'll
1669 # have linkrev(last) > maxrev, and we still need
1679 # have linkrev(last) > maxrev, and we still need
1670 # to explore the file graph
1680 # to explore the file graph
1671 if rev not in ancestors:
1681 if rev not in ancestors:
1672 continue
1682 continue
1673 # XXX insert 1327 fix here
1683 # XXX insert 1327 fix here
1674 if flparentlinkrevs:
1684 if flparentlinkrevs:
1675 ancestors.update(flparentlinkrevs)
1685 ancestors.update(flparentlinkrevs)
1676
1686
1677 fncache.setdefault(rev, []).append(file_)
1687 fncache.setdefault(rev, []).append(file_)
1678 wanted.add(rev)
1688 wanted.add(rev)
1679 if copied:
1689 if copied:
1680 copies.append(copied)
1690 copies.append(copied)
1681
1691
1682 return wanted
1692 return wanted
1683
1693
1684 class _followfilter(object):
1694 class _followfilter(object):
1685 def __init__(self, repo, onlyfirst=False):
1695 def __init__(self, repo, onlyfirst=False):
1686 self.repo = repo
1696 self.repo = repo
1687 self.startrev = nullrev
1697 self.startrev = nullrev
1688 self.roots = set()
1698 self.roots = set()
1689 self.onlyfirst = onlyfirst
1699 self.onlyfirst = onlyfirst
1690
1700
1691 def match(self, rev):
1701 def match(self, rev):
1692 def realparents(rev):
1702 def realparents(rev):
1693 if self.onlyfirst:
1703 if self.onlyfirst:
1694 return self.repo.changelog.parentrevs(rev)[0:1]
1704 return self.repo.changelog.parentrevs(rev)[0:1]
1695 else:
1705 else:
1696 return filter(lambda x: x != nullrev,
1706 return filter(lambda x: x != nullrev,
1697 self.repo.changelog.parentrevs(rev))
1707 self.repo.changelog.parentrevs(rev))
1698
1708
1699 if self.startrev == nullrev:
1709 if self.startrev == nullrev:
1700 self.startrev = rev
1710 self.startrev = rev
1701 return True
1711 return True
1702
1712
1703 if rev > self.startrev:
1713 if rev > self.startrev:
1704 # forward: all descendants
1714 # forward: all descendants
1705 if not self.roots:
1715 if not self.roots:
1706 self.roots.add(self.startrev)
1716 self.roots.add(self.startrev)
1707 for parent in realparents(rev):
1717 for parent in realparents(rev):
1708 if parent in self.roots:
1718 if parent in self.roots:
1709 self.roots.add(rev)
1719 self.roots.add(rev)
1710 return True
1720 return True
1711 else:
1721 else:
1712 # backwards: all parents
1722 # backwards: all parents
1713 if not self.roots:
1723 if not self.roots:
1714 self.roots.update(realparents(self.startrev))
1724 self.roots.update(realparents(self.startrev))
1715 if rev in self.roots:
1725 if rev in self.roots:
1716 self.roots.remove(rev)
1726 self.roots.remove(rev)
1717 self.roots.update(realparents(rev))
1727 self.roots.update(realparents(rev))
1718 return True
1728 return True
1719
1729
1720 return False
1730 return False
1721
1731
1722 def walkchangerevs(repo, match, opts, prepare):
1732 def walkchangerevs(repo, match, opts, prepare):
1723 '''Iterate over files and the revs in which they changed.
1733 '''Iterate over files and the revs in which they changed.
1724
1734
1725 Callers most commonly need to iterate backwards over the history
1735 Callers most commonly need to iterate backwards over the history
1726 in which they are interested. Doing so has awful (quadratic-looking)
1736 in which they are interested. Doing so has awful (quadratic-looking)
1727 performance, so we use iterators in a "windowed" way.
1737 performance, so we use iterators in a "windowed" way.
1728
1738
1729 We walk a window of revisions in the desired order. Within the
1739 We walk a window of revisions in the desired order. Within the
1730 window, we first walk forwards to gather data, then in the desired
1740 window, we first walk forwards to gather data, then in the desired
1731 order (usually backwards) to display it.
1741 order (usually backwards) to display it.
1732
1742
1733 This function returns an iterator yielding contexts. Before
1743 This function returns an iterator yielding contexts. Before
1734 yielding each context, the iterator will first call the prepare
1744 yielding each context, the iterator will first call the prepare
1735 function on each context in the window in forward order.'''
1745 function on each context in the window in forward order.'''
1736
1746
1737 follow = opts.get('follow') or opts.get('follow_first')
1747 follow = opts.get('follow') or opts.get('follow_first')
1738 revs = _logrevs(repo, opts)
1748 revs = _logrevs(repo, opts)
1739 if not revs:
1749 if not revs:
1740 return []
1750 return []
1741 wanted = set()
1751 wanted = set()
1742 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1752 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1743 fncache = {}
1753 fncache = {}
1744 change = repo.changectx
1754 change = repo.changectx
1745
1755
1746 # First step is to fill wanted, the set of revisions that we want to yield.
1756 # First step is to fill wanted, the set of revisions that we want to yield.
1747 # When it does not induce extra cost, we also fill fncache for revisions in
1757 # When it does not induce extra cost, we also fill fncache for revisions in
1748 # wanted: a cache of filenames that were changed (ctx.files()) and that
1758 # wanted: a cache of filenames that were changed (ctx.files()) and that
1749 # match the file filtering conditions.
1759 # match the file filtering conditions.
1750
1760
1751 if match.always():
1761 if match.always():
1752 # No files, no patterns. Display all revs.
1762 # No files, no patterns. Display all revs.
1753 wanted = revs
1763 wanted = revs
1754
1764
1755 if not slowpath and match.files():
1765 if not slowpath and match.files():
1756 # We only have to read through the filelog to find wanted revisions
1766 # We only have to read through the filelog to find wanted revisions
1757
1767
1758 try:
1768 try:
1759 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1769 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1760 except FileWalkError:
1770 except FileWalkError:
1761 slowpath = True
1771 slowpath = True
1762
1772
1763 # We decided to fall back to the slowpath because at least one
1773 # We decided to fall back to the slowpath because at least one
1764 # of the paths was not a file. Check to see if at least one of them
1774 # of the paths was not a file. Check to see if at least one of them
1765 # existed in history, otherwise simply return
1775 # existed in history, otherwise simply return
1766 for path in match.files():
1776 for path in match.files():
1767 if path == '.' or path in repo.store:
1777 if path == '.' or path in repo.store:
1768 break
1778 break
1769 else:
1779 else:
1770 return []
1780 return []
1771
1781
1772 if slowpath:
1782 if slowpath:
1773 # We have to read the changelog to match filenames against
1783 # We have to read the changelog to match filenames against
1774 # changed files
1784 # changed files
1775
1785
1776 if follow:
1786 if follow:
1777 raise util.Abort(_('can only follow copies/renames for explicit '
1787 raise util.Abort(_('can only follow copies/renames for explicit '
1778 'filenames'))
1788 'filenames'))
1779
1789
1780 # The slow path checks files modified in every changeset.
1790 # The slow path checks files modified in every changeset.
1781 # This is really slow on large repos, so compute the set lazily.
1791 # This is really slow on large repos, so compute the set lazily.
1782 class lazywantedset(object):
1792 class lazywantedset(object):
1783 def __init__(self):
1793 def __init__(self):
1784 self.set = set()
1794 self.set = set()
1785 self.revs = set(revs)
1795 self.revs = set(revs)
1786
1796
1787 # No need to worry about locality here because it will be accessed
1797 # No need to worry about locality here because it will be accessed
1788 # in the same order as the increasing window below.
1798 # in the same order as the increasing window below.
1789 def __contains__(self, value):
1799 def __contains__(self, value):
1790 if value in self.set:
1800 if value in self.set:
1791 return True
1801 return True
1792 elif not value in self.revs:
1802 elif not value in self.revs:
1793 return False
1803 return False
1794 else:
1804 else:
1795 self.revs.discard(value)
1805 self.revs.discard(value)
1796 ctx = change(value)
1806 ctx = change(value)
1797 matches = filter(match, ctx.files())
1807 matches = filter(match, ctx.files())
1798 if matches:
1808 if matches:
1799 fncache[value] = matches
1809 fncache[value] = matches
1800 self.set.add(value)
1810 self.set.add(value)
1801 return True
1811 return True
1802 return False
1812 return False
1803
1813
1804 def discard(self, value):
1814 def discard(self, value):
1805 self.revs.discard(value)
1815 self.revs.discard(value)
1806 self.set.discard(value)
1816 self.set.discard(value)
1807
1817
1808 wanted = lazywantedset()
1818 wanted = lazywantedset()
1809
1819
1810 # it might be worthwhile to do this in the iterator if the rev range
1820 # it might be worthwhile to do this in the iterator if the rev range
1811 # is descending and the prune args are all within that range
1821 # is descending and the prune args are all within that range
1812 for rev in opts.get('prune', ()):
1822 for rev in opts.get('prune', ()):
1813 rev = repo[rev].rev()
1823 rev = repo[rev].rev()
1814 ff = _followfilter(repo)
1824 ff = _followfilter(repo)
1815 stop = min(revs[0], revs[-1])
1825 stop = min(revs[0], revs[-1])
1816 for x in xrange(rev, stop - 1, -1):
1826 for x in xrange(rev, stop - 1, -1):
1817 if ff.match(x):
1827 if ff.match(x):
1818 wanted = wanted - [x]
1828 wanted = wanted - [x]
1819
1829
1820 # Now that wanted is correctly initialized, we can iterate over the
1830 # Now that wanted is correctly initialized, we can iterate over the
1821 # revision range, yielding only revisions in wanted.
1831 # revision range, yielding only revisions in wanted.
1822 def iterate():
1832 def iterate():
1823 if follow and not match.files():
1833 if follow and not match.files():
1824 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1834 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1825 def want(rev):
1835 def want(rev):
1826 return ff.match(rev) and rev in wanted
1836 return ff.match(rev) and rev in wanted
1827 else:
1837 else:
1828 def want(rev):
1838 def want(rev):
1829 return rev in wanted
1839 return rev in wanted
1830
1840
1831 it = iter(revs)
1841 it = iter(revs)
1832 stopiteration = False
1842 stopiteration = False
1833 for windowsize in increasingwindows():
1843 for windowsize in increasingwindows():
1834 nrevs = []
1844 nrevs = []
1835 for i in xrange(windowsize):
1845 for i in xrange(windowsize):
1836 try:
1846 try:
1837 rev = it.next()
1847 rev = it.next()
1838 if want(rev):
1848 if want(rev):
1839 nrevs.append(rev)
1849 nrevs.append(rev)
1840 except (StopIteration):
1850 except (StopIteration):
1841 stopiteration = True
1851 stopiteration = True
1842 break
1852 break
1843 for rev in sorted(nrevs):
1853 for rev in sorted(nrevs):
1844 fns = fncache.get(rev)
1854 fns = fncache.get(rev)
1845 ctx = change(rev)
1855 ctx = change(rev)
1846 if not fns:
1856 if not fns:
1847 def fns_generator():
1857 def fns_generator():
1848 for f in ctx.files():
1858 for f in ctx.files():
1849 if match(f):
1859 if match(f):
1850 yield f
1860 yield f
1851 fns = fns_generator()
1861 fns = fns_generator()
1852 prepare(ctx, fns)
1862 prepare(ctx, fns)
1853 for rev in nrevs:
1863 for rev in nrevs:
1854 yield change(rev)
1864 yield change(rev)
1855
1865
1856 if stopiteration:
1866 if stopiteration:
1857 break
1867 break
1858
1868
1859 return iterate()
1869 return iterate()
1860
1870
1861 def _makefollowlogfilematcher(repo, files, followfirst):
1871 def _makefollowlogfilematcher(repo, files, followfirst):
1862 # When displaying a revision with --patch --follow FILE, we have
1872 # When displaying a revision with --patch --follow FILE, we have
1863 # to know which file of the revision must be diffed. With
1873 # to know which file of the revision must be diffed. With
1864 # --follow, we want the names of the ancestors of FILE in the
1874 # --follow, we want the names of the ancestors of FILE in the
1865 # revision, stored in "fcache". "fcache" is populated by
1875 # revision, stored in "fcache". "fcache" is populated by
1866 # reproducing the graph traversal already done by --follow revset
1876 # reproducing the graph traversal already done by --follow revset
1867 # and relating linkrevs to file names (which is not "correct" but
1877 # and relating linkrevs to file names (which is not "correct" but
1868 # good enough).
1878 # good enough).
1869 fcache = {}
1879 fcache = {}
1870 fcacheready = [False]
1880 fcacheready = [False]
1871 pctx = repo['.']
1881 pctx = repo['.']
1872
1882
1873 def populate():
1883 def populate():
1874 for fn in files:
1884 for fn in files:
1875 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1885 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1876 for c in i:
1886 for c in i:
1877 fcache.setdefault(c.linkrev(), set()).add(c.path())
1887 fcache.setdefault(c.linkrev(), set()).add(c.path())
1878
1888
1879 def filematcher(rev):
1889 def filematcher(rev):
1880 if not fcacheready[0]:
1890 if not fcacheready[0]:
1881 # Lazy initialization
1891 # Lazy initialization
1882 fcacheready[0] = True
1892 fcacheready[0] = True
1883 populate()
1893 populate()
1884 return scmutil.matchfiles(repo, fcache.get(rev, []))
1894 return scmutil.matchfiles(repo, fcache.get(rev, []))
1885
1895
1886 return filematcher
1896 return filematcher
1887
1897
1888 def _makenofollowlogfilematcher(repo, pats, opts):
1898 def _makenofollowlogfilematcher(repo, pats, opts):
1889 '''hook for extensions to override the filematcher for non-follow cases'''
1899 '''hook for extensions to override the filematcher for non-follow cases'''
1890 return None
1900 return None
1891
1901
1892 def _makelogrevset(repo, pats, opts, revs):
1902 def _makelogrevset(repo, pats, opts, revs):
1893 """Return (expr, filematcher) where expr is a revset string built
1903 """Return (expr, filematcher) where expr is a revset string built
1894 from log options and file patterns or None. If --stat or --patch
1904 from log options and file patterns or None. If --stat or --patch
1895 are not passed filematcher is None. Otherwise it is a callable
1905 are not passed filematcher is None. Otherwise it is a callable
1896 taking a revision number and returning a match objects filtering
1906 taking a revision number and returning a match objects filtering
1897 the files to be detailed when displaying the revision.
1907 the files to be detailed when displaying the revision.
1898 """
1908 """
1899 opt2revset = {
1909 opt2revset = {
1900 'no_merges': ('not merge()', None),
1910 'no_merges': ('not merge()', None),
1901 'only_merges': ('merge()', None),
1911 'only_merges': ('merge()', None),
1902 '_ancestors': ('ancestors(%(val)s)', None),
1912 '_ancestors': ('ancestors(%(val)s)', None),
1903 '_fancestors': ('_firstancestors(%(val)s)', None),
1913 '_fancestors': ('_firstancestors(%(val)s)', None),
1904 '_descendants': ('descendants(%(val)s)', None),
1914 '_descendants': ('descendants(%(val)s)', None),
1905 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1915 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1906 '_matchfiles': ('_matchfiles(%(val)s)', None),
1916 '_matchfiles': ('_matchfiles(%(val)s)', None),
1907 'date': ('date(%(val)r)', None),
1917 'date': ('date(%(val)r)', None),
1908 'branch': ('branch(%(val)r)', ' or '),
1918 'branch': ('branch(%(val)r)', ' or '),
1909 '_patslog': ('filelog(%(val)r)', ' or '),
1919 '_patslog': ('filelog(%(val)r)', ' or '),
1910 '_patsfollow': ('follow(%(val)r)', ' or '),
1920 '_patsfollow': ('follow(%(val)r)', ' or '),
1911 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1921 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1912 'keyword': ('keyword(%(val)r)', ' or '),
1922 'keyword': ('keyword(%(val)r)', ' or '),
1913 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1923 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1914 'user': ('user(%(val)r)', ' or '),
1924 'user': ('user(%(val)r)', ' or '),
1915 }
1925 }
1916
1926
1917 opts = dict(opts)
1927 opts = dict(opts)
1918 # follow or not follow?
1928 # follow or not follow?
1919 follow = opts.get('follow') or opts.get('follow_first')
1929 follow = opts.get('follow') or opts.get('follow_first')
1920 if opts.get('follow_first'):
1930 if opts.get('follow_first'):
1921 followfirst = 1
1931 followfirst = 1
1922 else:
1932 else:
1923 followfirst = 0
1933 followfirst = 0
1924 # --follow with FILE behaviour depends on revs...
1934 # --follow with FILE behaviour depends on revs...
1925 it = iter(revs)
1935 it = iter(revs)
1926 startrev = it.next()
1936 startrev = it.next()
1927 try:
1937 try:
1928 followdescendants = startrev < it.next()
1938 followdescendants = startrev < it.next()
1929 except (StopIteration):
1939 except (StopIteration):
1930 followdescendants = False
1940 followdescendants = False
1931
1941
1932 # branch and only_branch are really aliases and must be handled at
1942 # branch and only_branch are really aliases and must be handled at
1933 # the same time
1943 # the same time
1934 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1944 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1935 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1945 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1936 # pats/include/exclude are passed to match.match() directly in
1946 # pats/include/exclude are passed to match.match() directly in
1937 # _matchfiles() revset but walkchangerevs() builds its matcher with
1947 # _matchfiles() revset but walkchangerevs() builds its matcher with
1938 # scmutil.match(). The difference is input pats are globbed on
1948 # scmutil.match(). The difference is input pats are globbed on
1939 # platforms without shell expansion (windows).
1949 # platforms without shell expansion (windows).
1940 wctx = repo[None]
1950 wctx = repo[None]
1941 match, pats = scmutil.matchandpats(wctx, pats, opts)
1951 match, pats = scmutil.matchandpats(wctx, pats, opts)
1942 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1952 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1943 if not slowpath:
1953 if not slowpath:
1944 for f in match.files():
1954 for f in match.files():
1945 if follow and f not in wctx:
1955 if follow and f not in wctx:
1946 # If the file exists, it may be a directory, so let it
1956 # If the file exists, it may be a directory, so let it
1947 # take the slow path.
1957 # take the slow path.
1948 if os.path.exists(repo.wjoin(f)):
1958 if os.path.exists(repo.wjoin(f)):
1949 slowpath = True
1959 slowpath = True
1950 continue
1960 continue
1951 else:
1961 else:
1952 raise util.Abort(_('cannot follow file not in parent '
1962 raise util.Abort(_('cannot follow file not in parent '
1953 'revision: "%s"') % f)
1963 'revision: "%s"') % f)
1954 filelog = repo.file(f)
1964 filelog = repo.file(f)
1955 if not filelog:
1965 if not filelog:
1956 # A zero count may be a directory or deleted file, so
1966 # A zero count may be a directory or deleted file, so
1957 # try to find matching entries on the slow path.
1967 # try to find matching entries on the slow path.
1958 if follow:
1968 if follow:
1959 raise util.Abort(
1969 raise util.Abort(
1960 _('cannot follow nonexistent file: "%s"') % f)
1970 _('cannot follow nonexistent file: "%s"') % f)
1961 slowpath = True
1971 slowpath = True
1962
1972
1963 # We decided to fall back to the slowpath because at least one
1973 # We decided to fall back to the slowpath because at least one
1964 # of the paths was not a file. Check to see if at least one of them
1974 # of the paths was not a file. Check to see if at least one of them
1965 # existed in history - in that case, we'll continue down the
1975 # existed in history - in that case, we'll continue down the
1966 # slowpath; otherwise, we can turn off the slowpath
1976 # slowpath; otherwise, we can turn off the slowpath
1967 if slowpath:
1977 if slowpath:
1968 for path in match.files():
1978 for path in match.files():
1969 if path == '.' or path in repo.store:
1979 if path == '.' or path in repo.store:
1970 break
1980 break
1971 else:
1981 else:
1972 slowpath = False
1982 slowpath = False
1973
1983
1974 fpats = ('_patsfollow', '_patsfollowfirst')
1984 fpats = ('_patsfollow', '_patsfollowfirst')
1975 fnopats = (('_ancestors', '_fancestors'),
1985 fnopats = (('_ancestors', '_fancestors'),
1976 ('_descendants', '_fdescendants'))
1986 ('_descendants', '_fdescendants'))
1977 if slowpath:
1987 if slowpath:
1978 # See walkchangerevs() slow path.
1988 # See walkchangerevs() slow path.
1979 #
1989 #
1980 # pats/include/exclude cannot be represented as separate
1990 # pats/include/exclude cannot be represented as separate
1981 # revset expressions as their filtering logic applies at file
1991 # revset expressions as their filtering logic applies at file
1982 # level. For instance "-I a -X a" matches a revision touching
1992 # level. For instance "-I a -X a" matches a revision touching
1983 # "a" and "b" while "file(a) and not file(b)" does
1993 # "a" and "b" while "file(a) and not file(b)" does
1984 # not. Besides, filesets are evaluated against the working
1994 # not. Besides, filesets are evaluated against the working
1985 # directory.
1995 # directory.
1986 matchargs = ['r:', 'd:relpath']
1996 matchargs = ['r:', 'd:relpath']
1987 for p in pats:
1997 for p in pats:
1988 matchargs.append('p:' + p)
1998 matchargs.append('p:' + p)
1989 for p in opts.get('include', []):
1999 for p in opts.get('include', []):
1990 matchargs.append('i:' + p)
2000 matchargs.append('i:' + p)
1991 for p in opts.get('exclude', []):
2001 for p in opts.get('exclude', []):
1992 matchargs.append('x:' + p)
2002 matchargs.append('x:' + p)
1993 matchargs = ','.join(('%r' % p) for p in matchargs)
2003 matchargs = ','.join(('%r' % p) for p in matchargs)
1994 opts['_matchfiles'] = matchargs
2004 opts['_matchfiles'] = matchargs
1995 if follow:
2005 if follow:
1996 opts[fnopats[0][followfirst]] = '.'
2006 opts[fnopats[0][followfirst]] = '.'
1997 else:
2007 else:
1998 if follow:
2008 if follow:
1999 if pats:
2009 if pats:
2000 # follow() revset interprets its file argument as a
2010 # follow() revset interprets its file argument as a
2001 # manifest entry, so use match.files(), not pats.
2011 # manifest entry, so use match.files(), not pats.
2002 opts[fpats[followfirst]] = list(match.files())
2012 opts[fpats[followfirst]] = list(match.files())
2003 else:
2013 else:
2004 op = fnopats[followdescendants][followfirst]
2014 op = fnopats[followdescendants][followfirst]
2005 opts[op] = 'rev(%d)' % startrev
2015 opts[op] = 'rev(%d)' % startrev
2006 else:
2016 else:
2007 opts['_patslog'] = list(pats)
2017 opts['_patslog'] = list(pats)
2008
2018
2009 filematcher = None
2019 filematcher = None
2010 if opts.get('patch') or opts.get('stat'):
2020 if opts.get('patch') or opts.get('stat'):
2011 # When following files, track renames via a special matcher.
2021 # When following files, track renames via a special matcher.
2012 # If we're forced to take the slowpath it means we're following
2022 # If we're forced to take the slowpath it means we're following
2013 # at least one pattern/directory, so don't bother with rename tracking.
2023 # at least one pattern/directory, so don't bother with rename tracking.
2014 if follow and not match.always() and not slowpath:
2024 if follow and not match.always() and not slowpath:
2015 # _makefollowlogfilematcher expects its files argument to be
2025 # _makefollowlogfilematcher expects its files argument to be
2016 # relative to the repo root, so use match.files(), not pats.
2026 # relative to the repo root, so use match.files(), not pats.
2017 filematcher = _makefollowlogfilematcher(repo, match.files(),
2027 filematcher = _makefollowlogfilematcher(repo, match.files(),
2018 followfirst)
2028 followfirst)
2019 else:
2029 else:
2020 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2030 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2021 if filematcher is None:
2031 if filematcher is None:
2022 filematcher = lambda rev: match
2032 filematcher = lambda rev: match
2023
2033
2024 expr = []
2034 expr = []
2025 for op, val in sorted(opts.iteritems()):
2035 for op, val in sorted(opts.iteritems()):
2026 if not val:
2036 if not val:
2027 continue
2037 continue
2028 if op not in opt2revset:
2038 if op not in opt2revset:
2029 continue
2039 continue
2030 revop, andor = opt2revset[op]
2040 revop, andor = opt2revset[op]
2031 if '%(val)' not in revop:
2041 if '%(val)' not in revop:
2032 expr.append(revop)
2042 expr.append(revop)
2033 else:
2043 else:
2034 if not isinstance(val, list):
2044 if not isinstance(val, list):
2035 e = revop % {'val': val}
2045 e = revop % {'val': val}
2036 else:
2046 else:
2037 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2047 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2038 expr.append(e)
2048 expr.append(e)
2039
2049
2040 if expr:
2050 if expr:
2041 expr = '(' + ' and '.join(expr) + ')'
2051 expr = '(' + ' and '.join(expr) + ')'
2042 else:
2052 else:
2043 expr = None
2053 expr = None
2044 return expr, filematcher
2054 return expr, filematcher
2045
2055
2046 def _logrevs(repo, opts):
2056 def _logrevs(repo, opts):
2047 # Default --rev value depends on --follow but --follow behaviour
2057 # Default --rev value depends on --follow but --follow behaviour
2048 # depends on revisions resolved from --rev...
2058 # depends on revisions resolved from --rev...
2049 follow = opts.get('follow') or opts.get('follow_first')
2059 follow = opts.get('follow') or opts.get('follow_first')
2050 if opts.get('rev'):
2060 if opts.get('rev'):
2051 revs = scmutil.revrange(repo, opts['rev'])
2061 revs = scmutil.revrange(repo, opts['rev'])
2052 elif follow and repo.dirstate.p1() == nullid:
2062 elif follow and repo.dirstate.p1() == nullid:
2053 revs = revset.baseset()
2063 revs = revset.baseset()
2054 elif follow:
2064 elif follow:
2055 revs = repo.revs('reverse(:.)')
2065 revs = repo.revs('reverse(:.)')
2056 else:
2066 else:
2057 revs = revset.spanset(repo)
2067 revs = revset.spanset(repo)
2058 revs.reverse()
2068 revs.reverse()
2059 return revs
2069 return revs
2060
2070
2061 def getgraphlogrevs(repo, pats, opts):
2071 def getgraphlogrevs(repo, pats, opts):
2062 """Return (revs, expr, filematcher) where revs is an iterable of
2072 """Return (revs, expr, filematcher) where revs is an iterable of
2063 revision numbers, expr is a revset string built from log options
2073 revision numbers, expr is a revset string built from log options
2064 and file patterns or None, and used to filter 'revs'. If --stat or
2074 and file patterns or None, and used to filter 'revs'. If --stat or
2065 --patch are not passed filematcher is None. Otherwise it is a
2075 --patch are not passed filematcher is None. Otherwise it is a
2066 callable taking a revision number and returning a match objects
2076 callable taking a revision number and returning a match objects
2067 filtering the files to be detailed when displaying the revision.
2077 filtering the files to be detailed when displaying the revision.
2068 """
2078 """
2069 limit = loglimit(opts)
2079 limit = loglimit(opts)
2070 revs = _logrevs(repo, opts)
2080 revs = _logrevs(repo, opts)
2071 if not revs:
2081 if not revs:
2072 return revset.baseset(), None, None
2082 return revset.baseset(), None, None
2073 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2083 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2074 if opts.get('rev'):
2084 if opts.get('rev'):
2075 # User-specified revs might be unsorted, but don't sort before
2085 # User-specified revs might be unsorted, but don't sort before
2076 # _makelogrevset because it might depend on the order of revs
2086 # _makelogrevset because it might depend on the order of revs
2077 revs.sort(reverse=True)
2087 revs.sort(reverse=True)
2078 if expr:
2088 if expr:
2079 # Revset matchers often operate faster on revisions in changelog
2089 # Revset matchers often operate faster on revisions in changelog
2080 # order, because most filters deal with the changelog.
2090 # order, because most filters deal with the changelog.
2081 revs.reverse()
2091 revs.reverse()
2082 matcher = revset.match(repo.ui, expr)
2092 matcher = revset.match(repo.ui, expr)
2083 # Revset matches can reorder revisions. "A or B" typically returns
2093 # Revset matches can reorder revisions. "A or B" typically returns
2084 # returns the revision matching A then the revision matching B. Sort
2094 # returns the revision matching A then the revision matching B. Sort
2085 # again to fix that.
2095 # again to fix that.
2086 revs = matcher(repo, revs)
2096 revs = matcher(repo, revs)
2087 revs.sort(reverse=True)
2097 revs.sort(reverse=True)
2088 if limit is not None:
2098 if limit is not None:
2089 limitedrevs = []
2099 limitedrevs = []
2090 for idx, rev in enumerate(revs):
2100 for idx, rev in enumerate(revs):
2091 if idx >= limit:
2101 if idx >= limit:
2092 break
2102 break
2093 limitedrevs.append(rev)
2103 limitedrevs.append(rev)
2094 revs = revset.baseset(limitedrevs)
2104 revs = revset.baseset(limitedrevs)
2095
2105
2096 return revs, expr, filematcher
2106 return revs, expr, filematcher
2097
2107
2098 def getlogrevs(repo, pats, opts):
2108 def getlogrevs(repo, pats, opts):
2099 """Return (revs, expr, filematcher) where revs is an iterable of
2109 """Return (revs, expr, filematcher) where revs is an iterable of
2100 revision numbers, expr is a revset string built from log options
2110 revision numbers, expr is a revset string built from log options
2101 and file patterns or None, and used to filter 'revs'. If --stat or
2111 and file patterns or None, and used to filter 'revs'. If --stat or
2102 --patch are not passed filematcher is None. Otherwise it is a
2112 --patch are not passed filematcher is None. Otherwise it is a
2103 callable taking a revision number and returning a match objects
2113 callable taking a revision number and returning a match objects
2104 filtering the files to be detailed when displaying the revision.
2114 filtering the files to be detailed when displaying the revision.
2105 """
2115 """
2106 limit = loglimit(opts)
2116 limit = loglimit(opts)
2107 revs = _logrevs(repo, opts)
2117 revs = _logrevs(repo, opts)
2108 if not revs:
2118 if not revs:
2109 return revset.baseset([]), None, None
2119 return revset.baseset([]), None, None
2110 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2120 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2111 if expr:
2121 if expr:
2112 # Revset matchers often operate faster on revisions in changelog
2122 # Revset matchers often operate faster on revisions in changelog
2113 # order, because most filters deal with the changelog.
2123 # order, because most filters deal with the changelog.
2114 if not opts.get('rev'):
2124 if not opts.get('rev'):
2115 revs.reverse()
2125 revs.reverse()
2116 matcher = revset.match(repo.ui, expr)
2126 matcher = revset.match(repo.ui, expr)
2117 # Revset matches can reorder revisions. "A or B" typically returns
2127 # Revset matches can reorder revisions. "A or B" typically returns
2118 # returns the revision matching A then the revision matching B. Sort
2128 # returns the revision matching A then the revision matching B. Sort
2119 # again to fix that.
2129 # again to fix that.
2120 revs = matcher(repo, revs)
2130 revs = matcher(repo, revs)
2121 if not opts.get('rev'):
2131 if not opts.get('rev'):
2122 revs.sort(reverse=True)
2132 revs.sort(reverse=True)
2123 if limit is not None:
2133 if limit is not None:
2124 count = 0
2134 count = 0
2125 limitedrevs = []
2135 limitedrevs = []
2126 it = iter(revs)
2136 it = iter(revs)
2127 while count < limit:
2137 while count < limit:
2128 try:
2138 try:
2129 limitedrevs.append(it.next())
2139 limitedrevs.append(it.next())
2130 except (StopIteration):
2140 except (StopIteration):
2131 break
2141 break
2132 count += 1
2142 count += 1
2133 revs = revset.baseset(limitedrevs)
2143 revs = revset.baseset(limitedrevs)
2134
2144
2135 return revs, expr, filematcher
2145 return revs, expr, filematcher
2136
2146
2137 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
2147 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
2138 filematcher=None):
2148 filematcher=None):
2139 seen, state = [], graphmod.asciistate()
2149 seen, state = [], graphmod.asciistate()
2140 for rev, type, ctx, parents in dag:
2150 for rev, type, ctx, parents in dag:
2141 char = 'o'
2151 char = 'o'
2142 if ctx.node() in showparents:
2152 if ctx.node() in showparents:
2143 char = '@'
2153 char = '@'
2144 elif ctx.obsolete():
2154 elif ctx.obsolete():
2145 char = 'x'
2155 char = 'x'
2146 elif ctx.closesbranch():
2156 elif ctx.closesbranch():
2147 char = '_'
2157 char = '_'
2148 copies = None
2158 copies = None
2149 if getrenamed and ctx.rev():
2159 if getrenamed and ctx.rev():
2150 copies = []
2160 copies = []
2151 for fn in ctx.files():
2161 for fn in ctx.files():
2152 rename = getrenamed(fn, ctx.rev())
2162 rename = getrenamed(fn, ctx.rev())
2153 if rename:
2163 if rename:
2154 copies.append((fn, rename[0]))
2164 copies.append((fn, rename[0]))
2155 revmatchfn = None
2165 revmatchfn = None
2156 if filematcher is not None:
2166 if filematcher is not None:
2157 revmatchfn = filematcher(ctx.rev())
2167 revmatchfn = filematcher(ctx.rev())
2158 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2168 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2159 lines = displayer.hunk.pop(rev).split('\n')
2169 lines = displayer.hunk.pop(rev).split('\n')
2160 if not lines[-1]:
2170 if not lines[-1]:
2161 del lines[-1]
2171 del lines[-1]
2162 displayer.flush(rev)
2172 displayer.flush(rev)
2163 edges = edgefn(type, char, lines, seen, rev, parents)
2173 edges = edgefn(type, char, lines, seen, rev, parents)
2164 for type, char, lines, coldata in edges:
2174 for type, char, lines, coldata in edges:
2165 graphmod.ascii(ui, state, type, char, lines, coldata)
2175 graphmod.ascii(ui, state, type, char, lines, coldata)
2166 displayer.close()
2176 displayer.close()
2167
2177
2168 def graphlog(ui, repo, *pats, **opts):
2178 def graphlog(ui, repo, *pats, **opts):
2169 # Parameters are identical to log command ones
2179 # Parameters are identical to log command ones
2170 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2180 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2171 revdag = graphmod.dagwalker(repo, revs)
2181 revdag = graphmod.dagwalker(repo, revs)
2172
2182
2173 getrenamed = None
2183 getrenamed = None
2174 if opts.get('copies'):
2184 if opts.get('copies'):
2175 endrev = None
2185 endrev = None
2176 if opts.get('rev'):
2186 if opts.get('rev'):
2177 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2187 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2178 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2188 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2179 displayer = show_changeset(ui, repo, opts, buffered=True)
2189 displayer = show_changeset(ui, repo, opts, buffered=True)
2180 showparents = [ctx.node() for ctx in repo[None].parents()]
2190 showparents = [ctx.node() for ctx in repo[None].parents()]
2181 displaygraph(ui, revdag, displayer, showparents,
2191 displaygraph(ui, revdag, displayer, showparents,
2182 graphmod.asciiedges, getrenamed, filematcher)
2192 graphmod.asciiedges, getrenamed, filematcher)
2183
2193
2184 def checkunsupportedgraphflags(pats, opts):
2194 def checkunsupportedgraphflags(pats, opts):
2185 for op in ["newest_first"]:
2195 for op in ["newest_first"]:
2186 if op in opts and opts[op]:
2196 if op in opts and opts[op]:
2187 raise util.Abort(_("-G/--graph option is incompatible with --%s")
2197 raise util.Abort(_("-G/--graph option is incompatible with --%s")
2188 % op.replace("_", "-"))
2198 % op.replace("_", "-"))
2189
2199
2190 def graphrevs(repo, nodes, opts):
2200 def graphrevs(repo, nodes, opts):
2191 limit = loglimit(opts)
2201 limit = loglimit(opts)
2192 nodes.reverse()
2202 nodes.reverse()
2193 if limit is not None:
2203 if limit is not None:
2194 nodes = nodes[:limit]
2204 nodes = nodes[:limit]
2195 return graphmod.nodes(repo, nodes)
2205 return graphmod.nodes(repo, nodes)
2196
2206
2197 def add(ui, repo, match, prefix, explicitonly, **opts):
2207 def add(ui, repo, match, prefix, explicitonly, **opts):
2198 join = lambda f: os.path.join(prefix, f)
2208 join = lambda f: os.path.join(prefix, f)
2199 bad = []
2209 bad = []
2200 oldbad = match.bad
2210 oldbad = match.bad
2201 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
2211 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
2202 names = []
2212 names = []
2203 wctx = repo[None]
2213 wctx = repo[None]
2204 cca = None
2214 cca = None
2205 abort, warn = scmutil.checkportabilityalert(ui)
2215 abort, warn = scmutil.checkportabilityalert(ui)
2206 if abort or warn:
2216 if abort or warn:
2207 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2217 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2208 for f in wctx.walk(match):
2218 for f in wctx.walk(match):
2209 exact = match.exact(f)
2219 exact = match.exact(f)
2210 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2220 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2211 if cca:
2221 if cca:
2212 cca(f)
2222 cca(f)
2213 names.append(f)
2223 names.append(f)
2214 if ui.verbose or not exact:
2224 if ui.verbose or not exact:
2215 ui.status(_('adding %s\n') % match.rel(f))
2225 ui.status(_('adding %s\n') % match.rel(f))
2216
2226
2217 for subpath in sorted(wctx.substate):
2227 for subpath in sorted(wctx.substate):
2218 sub = wctx.sub(subpath)
2228 sub = wctx.sub(subpath)
2219 try:
2229 try:
2220 submatch = matchmod.narrowmatcher(subpath, match)
2230 submatch = matchmod.narrowmatcher(subpath, match)
2221 if opts.get('subrepos'):
2231 if opts.get('subrepos'):
2222 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2232 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2223 else:
2233 else:
2224 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2234 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2225 except error.LookupError:
2235 except error.LookupError:
2226 ui.status(_("skipping missing subrepository: %s\n")
2236 ui.status(_("skipping missing subrepository: %s\n")
2227 % join(subpath))
2237 % join(subpath))
2228
2238
2229 if not opts.get('dry_run'):
2239 if not opts.get('dry_run'):
2230 rejected = wctx.add(names, prefix)
2240 rejected = wctx.add(names, prefix)
2231 bad.extend(f for f in rejected if f in match.files())
2241 bad.extend(f for f in rejected if f in match.files())
2232 return bad
2242 return bad
2233
2243
2234 def forget(ui, repo, match, prefix, explicitonly):
2244 def forget(ui, repo, match, prefix, explicitonly):
2235 join = lambda f: os.path.join(prefix, f)
2245 join = lambda f: os.path.join(prefix, f)
2236 bad = []
2246 bad = []
2237 oldbad = match.bad
2247 oldbad = match.bad
2238 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
2248 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
2239 wctx = repo[None]
2249 wctx = repo[None]
2240 forgot = []
2250 forgot = []
2241 s = repo.status(match=match, clean=True)
2251 s = repo.status(match=match, clean=True)
2242 forget = sorted(s[0] + s[1] + s[3] + s[6])
2252 forget = sorted(s[0] + s[1] + s[3] + s[6])
2243 if explicitonly:
2253 if explicitonly:
2244 forget = [f for f in forget if match.exact(f)]
2254 forget = [f for f in forget if match.exact(f)]
2245
2255
2246 for subpath in sorted(wctx.substate):
2256 for subpath in sorted(wctx.substate):
2247 sub = wctx.sub(subpath)
2257 sub = wctx.sub(subpath)
2248 try:
2258 try:
2249 submatch = matchmod.narrowmatcher(subpath, match)
2259 submatch = matchmod.narrowmatcher(subpath, match)
2250 subbad, subforgot = sub.forget(submatch, prefix)
2260 subbad, subforgot = sub.forget(submatch, prefix)
2251 bad.extend([subpath + '/' + f for f in subbad])
2261 bad.extend([subpath + '/' + f for f in subbad])
2252 forgot.extend([subpath + '/' + f for f in subforgot])
2262 forgot.extend([subpath + '/' + f for f in subforgot])
2253 except error.LookupError:
2263 except error.LookupError:
2254 ui.status(_("skipping missing subrepository: %s\n")
2264 ui.status(_("skipping missing subrepository: %s\n")
2255 % join(subpath))
2265 % join(subpath))
2256
2266
2257 if not explicitonly:
2267 if not explicitonly:
2258 for f in match.files():
2268 for f in match.files():
2259 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2269 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2260 if f not in forgot:
2270 if f not in forgot:
2261 if repo.wvfs.exists(f):
2271 if repo.wvfs.exists(f):
2262 # Don't complain if the exact case match wasn't given.
2272 # Don't complain if the exact case match wasn't given.
2263 # But don't do this until after checking 'forgot', so
2273 # But don't do this until after checking 'forgot', so
2264 # that subrepo files aren't normalized, and this op is
2274 # that subrepo files aren't normalized, and this op is
2265 # purely from data cached by the status walk above.
2275 # purely from data cached by the status walk above.
2266 if repo.dirstate.normalize(f) in repo.dirstate:
2276 if repo.dirstate.normalize(f) in repo.dirstate:
2267 continue
2277 continue
2268 ui.warn(_('not removing %s: '
2278 ui.warn(_('not removing %s: '
2269 'file is already untracked\n')
2279 'file is already untracked\n')
2270 % match.rel(f))
2280 % match.rel(f))
2271 bad.append(f)
2281 bad.append(f)
2272
2282
2273 for f in forget:
2283 for f in forget:
2274 if ui.verbose or not match.exact(f):
2284 if ui.verbose or not match.exact(f):
2275 ui.status(_('removing %s\n') % match.rel(f))
2285 ui.status(_('removing %s\n') % match.rel(f))
2276
2286
2277 rejected = wctx.forget(forget, prefix)
2287 rejected = wctx.forget(forget, prefix)
2278 bad.extend(f for f in rejected if f in match.files())
2288 bad.extend(f for f in rejected if f in match.files())
2279 forgot.extend(f for f in forget if f not in rejected)
2289 forgot.extend(f for f in forget if f not in rejected)
2280 return bad, forgot
2290 return bad, forgot
2281
2291
2282 def files(ui, ctx, m, fm, fmt, subrepos):
2292 def files(ui, ctx, m, fm, fmt, subrepos):
2283 rev = ctx.rev()
2293 rev = ctx.rev()
2284 ret = 1
2294 ret = 1
2285 ds = ctx.repo().dirstate
2295 ds = ctx.repo().dirstate
2286
2296
2287 for f in ctx.matches(m):
2297 for f in ctx.matches(m):
2288 if rev is None and ds[f] == 'r':
2298 if rev is None and ds[f] == 'r':
2289 continue
2299 continue
2290 fm.startitem()
2300 fm.startitem()
2291 if ui.verbose:
2301 if ui.verbose:
2292 fc = ctx[f]
2302 fc = ctx[f]
2293 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2303 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2294 fm.data(abspath=f)
2304 fm.data(abspath=f)
2295 fm.write('path', fmt, m.rel(f))
2305 fm.write('path', fmt, m.rel(f))
2296 ret = 0
2306 ret = 0
2297
2307
2298 if subrepos:
2308 if subrepos:
2299 for subpath in sorted(ctx.substate):
2309 for subpath in sorted(ctx.substate):
2300 sub = ctx.sub(subpath)
2310 sub = ctx.sub(subpath)
2301 try:
2311 try:
2302 submatch = matchmod.narrowmatcher(subpath, m)
2312 submatch = matchmod.narrowmatcher(subpath, m)
2303 if sub.printfiles(ui, submatch, fm, fmt) == 0:
2313 if sub.printfiles(ui, submatch, fm, fmt) == 0:
2304 ret = 0
2314 ret = 0
2305 except error.LookupError:
2315 except error.LookupError:
2306 ui.status(_("skipping missing subrepository: %s\n")
2316 ui.status(_("skipping missing subrepository: %s\n")
2307 % m.abs(subpath))
2317 % m.abs(subpath))
2308
2318
2309 return ret
2319 return ret
2310
2320
2311 def remove(ui, repo, m, prefix, after, force, subrepos):
2321 def remove(ui, repo, m, prefix, after, force, subrepos):
2312 join = lambda f: os.path.join(prefix, f)
2322 join = lambda f: os.path.join(prefix, f)
2313 ret = 0
2323 ret = 0
2314 s = repo.status(match=m, clean=True)
2324 s = repo.status(match=m, clean=True)
2315 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2325 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2316
2326
2317 wctx = repo[None]
2327 wctx = repo[None]
2318
2328
2319 for subpath in sorted(wctx.substate):
2329 for subpath in sorted(wctx.substate):
2320 def matchessubrepo(matcher, subpath):
2330 def matchessubrepo(matcher, subpath):
2321 if matcher.exact(subpath):
2331 if matcher.exact(subpath):
2322 return True
2332 return True
2323 for f in matcher.files():
2333 for f in matcher.files():
2324 if f.startswith(subpath):
2334 if f.startswith(subpath):
2325 return True
2335 return True
2326 return False
2336 return False
2327
2337
2328 if subrepos or matchessubrepo(m, subpath):
2338 if subrepos or matchessubrepo(m, subpath):
2329 sub = wctx.sub(subpath)
2339 sub = wctx.sub(subpath)
2330 try:
2340 try:
2331 submatch = matchmod.narrowmatcher(subpath, m)
2341 submatch = matchmod.narrowmatcher(subpath, m)
2332 if sub.removefiles(submatch, prefix, after, force, subrepos):
2342 if sub.removefiles(submatch, prefix, after, force, subrepos):
2333 ret = 1
2343 ret = 1
2334 except error.LookupError:
2344 except error.LookupError:
2335 ui.status(_("skipping missing subrepository: %s\n")
2345 ui.status(_("skipping missing subrepository: %s\n")
2336 % join(subpath))
2346 % join(subpath))
2337
2347
2338 # warn about failure to delete explicit files/dirs
2348 # warn about failure to delete explicit files/dirs
2339 deleteddirs = util.dirs(deleted)
2349 deleteddirs = util.dirs(deleted)
2340 for f in m.files():
2350 for f in m.files():
2341 def insubrepo():
2351 def insubrepo():
2342 for subpath in wctx.substate:
2352 for subpath in wctx.substate:
2343 if f.startswith(subpath):
2353 if f.startswith(subpath):
2344 return True
2354 return True
2345 return False
2355 return False
2346
2356
2347 isdir = f in deleteddirs or wctx.hasdir(f)
2357 isdir = f in deleteddirs or wctx.hasdir(f)
2348 if f in repo.dirstate or isdir or f == '.' or insubrepo():
2358 if f in repo.dirstate or isdir or f == '.' or insubrepo():
2349 continue
2359 continue
2350
2360
2351 if repo.wvfs.exists(f):
2361 if repo.wvfs.exists(f):
2352 if repo.wvfs.isdir(f):
2362 if repo.wvfs.isdir(f):
2353 ui.warn(_('not removing %s: no tracked files\n')
2363 ui.warn(_('not removing %s: no tracked files\n')
2354 % m.rel(f))
2364 % m.rel(f))
2355 else:
2365 else:
2356 ui.warn(_('not removing %s: file is untracked\n')
2366 ui.warn(_('not removing %s: file is untracked\n')
2357 % m.rel(f))
2367 % m.rel(f))
2358 # missing files will generate a warning elsewhere
2368 # missing files will generate a warning elsewhere
2359 ret = 1
2369 ret = 1
2360
2370
2361 if force:
2371 if force:
2362 list = modified + deleted + clean + added
2372 list = modified + deleted + clean + added
2363 elif after:
2373 elif after:
2364 list = deleted
2374 list = deleted
2365 for f in modified + added + clean:
2375 for f in modified + added + clean:
2366 ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
2376 ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
2367 ret = 1
2377 ret = 1
2368 else:
2378 else:
2369 list = deleted + clean
2379 list = deleted + clean
2370 for f in modified:
2380 for f in modified:
2371 ui.warn(_('not removing %s: file is modified (use -f'
2381 ui.warn(_('not removing %s: file is modified (use -f'
2372 ' to force removal)\n') % m.rel(f))
2382 ' to force removal)\n') % m.rel(f))
2373 ret = 1
2383 ret = 1
2374 for f in added:
2384 for f in added:
2375 ui.warn(_('not removing %s: file has been marked for add'
2385 ui.warn(_('not removing %s: file has been marked for add'
2376 ' (use forget to undo)\n') % m.rel(f))
2386 ' (use forget to undo)\n') % m.rel(f))
2377 ret = 1
2387 ret = 1
2378
2388
2379 for f in sorted(list):
2389 for f in sorted(list):
2380 if ui.verbose or not m.exact(f):
2390 if ui.verbose or not m.exact(f):
2381 ui.status(_('removing %s\n') % m.rel(f))
2391 ui.status(_('removing %s\n') % m.rel(f))
2382
2392
2383 wlock = repo.wlock()
2393 wlock = repo.wlock()
2384 try:
2394 try:
2385 if not after:
2395 if not after:
2386 for f in list:
2396 for f in list:
2387 if f in added:
2397 if f in added:
2388 continue # we never unlink added files on remove
2398 continue # we never unlink added files on remove
2389 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
2399 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
2390 repo[None].forget(list)
2400 repo[None].forget(list)
2391 finally:
2401 finally:
2392 wlock.release()
2402 wlock.release()
2393
2403
2394 return ret
2404 return ret
2395
2405
2396 def cat(ui, repo, ctx, matcher, prefix, **opts):
2406 def cat(ui, repo, ctx, matcher, prefix, **opts):
2397 err = 1
2407 err = 1
2398
2408
2399 def write(path):
2409 def write(path):
2400 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2410 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2401 pathname=os.path.join(prefix, path))
2411 pathname=os.path.join(prefix, path))
2402 data = ctx[path].data()
2412 data = ctx[path].data()
2403 if opts.get('decode'):
2413 if opts.get('decode'):
2404 data = repo.wwritedata(path, data)
2414 data = repo.wwritedata(path, data)
2405 fp.write(data)
2415 fp.write(data)
2406 fp.close()
2416 fp.close()
2407
2417
2408 # Automation often uses hg cat on single files, so special case it
2418 # Automation often uses hg cat on single files, so special case it
2409 # for performance to avoid the cost of parsing the manifest.
2419 # for performance to avoid the cost of parsing the manifest.
2410 if len(matcher.files()) == 1 and not matcher.anypats():
2420 if len(matcher.files()) == 1 and not matcher.anypats():
2411 file = matcher.files()[0]
2421 file = matcher.files()[0]
2412 mf = repo.manifest
2422 mf = repo.manifest
2413 mfnode = ctx.manifestnode()
2423 mfnode = ctx.manifestnode()
2414 if mfnode and mf.find(mfnode, file)[0]:
2424 if mfnode and mf.find(mfnode, file)[0]:
2415 write(file)
2425 write(file)
2416 return 0
2426 return 0
2417
2427
2418 # Don't warn about "missing" files that are really in subrepos
2428 # Don't warn about "missing" files that are really in subrepos
2419 bad = matcher.bad
2429 bad = matcher.bad
2420
2430
2421 def badfn(path, msg):
2431 def badfn(path, msg):
2422 for subpath in ctx.substate:
2432 for subpath in ctx.substate:
2423 if path.startswith(subpath):
2433 if path.startswith(subpath):
2424 return
2434 return
2425 bad(path, msg)
2435 bad(path, msg)
2426
2436
2427 matcher.bad = badfn
2437 matcher.bad = badfn
2428
2438
2429 for abs in ctx.walk(matcher):
2439 for abs in ctx.walk(matcher):
2430 write(abs)
2440 write(abs)
2431 err = 0
2441 err = 0
2432
2442
2433 matcher.bad = bad
2443 matcher.bad = bad
2434
2444
2435 for subpath in sorted(ctx.substate):
2445 for subpath in sorted(ctx.substate):
2436 sub = ctx.sub(subpath)
2446 sub = ctx.sub(subpath)
2437 try:
2447 try:
2438 submatch = matchmod.narrowmatcher(subpath, matcher)
2448 submatch = matchmod.narrowmatcher(subpath, matcher)
2439
2449
2440 if not sub.cat(submatch, os.path.join(prefix, sub._path),
2450 if not sub.cat(submatch, os.path.join(prefix, sub._path),
2441 **opts):
2451 **opts):
2442 err = 0
2452 err = 0
2443 except error.RepoLookupError:
2453 except error.RepoLookupError:
2444 ui.status(_("skipping missing subrepository: %s\n")
2454 ui.status(_("skipping missing subrepository: %s\n")
2445 % os.path.join(prefix, subpath))
2455 % os.path.join(prefix, subpath))
2446
2456
2447 return err
2457 return err
2448
2458
2449 def commit(ui, repo, commitfunc, pats, opts):
2459 def commit(ui, repo, commitfunc, pats, opts):
2450 '''commit the specified files or all outstanding changes'''
2460 '''commit the specified files or all outstanding changes'''
2451 date = opts.get('date')
2461 date = opts.get('date')
2452 if date:
2462 if date:
2453 opts['date'] = util.parsedate(date)
2463 opts['date'] = util.parsedate(date)
2454 message = logmessage(ui, opts)
2464 message = logmessage(ui, opts)
2455 matcher = scmutil.match(repo[None], pats, opts)
2465 matcher = scmutil.match(repo[None], pats, opts)
2456
2466
2457 # extract addremove carefully -- this function can be called from a command
2467 # extract addremove carefully -- this function can be called from a command
2458 # that doesn't support addremove
2468 # that doesn't support addremove
2459 if opts.get('addremove'):
2469 if opts.get('addremove'):
2460 if scmutil.addremove(repo, matcher, "", opts) != 0:
2470 if scmutil.addremove(repo, matcher, "", opts) != 0:
2461 raise util.Abort(
2471 raise util.Abort(
2462 _("failed to mark all new/missing files as added/removed"))
2472 _("failed to mark all new/missing files as added/removed"))
2463
2473
2464 return commitfunc(ui, repo, message, matcher, opts)
2474 return commitfunc(ui, repo, message, matcher, opts)
2465
2475
2466 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2476 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2467 # amend will reuse the existing user if not specified, but the obsolete
2477 # amend will reuse the existing user if not specified, but the obsolete
2468 # marker creation requires that the current user's name is specified.
2478 # marker creation requires that the current user's name is specified.
2469 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2479 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2470 ui.username() # raise exception if username not set
2480 ui.username() # raise exception if username not set
2471
2481
2472 ui.note(_('amending changeset %s\n') % old)
2482 ui.note(_('amending changeset %s\n') % old)
2473 base = old.p1()
2483 base = old.p1()
2474
2484
2475 wlock = dsguard = lock = newid = None
2485 wlock = dsguard = lock = newid = None
2476 try:
2486 try:
2477 wlock = repo.wlock()
2487 wlock = repo.wlock()
2478 dsguard = dirstateguard(repo, 'amend')
2488 dsguard = dirstateguard(repo, 'amend')
2479 lock = repo.lock()
2489 lock = repo.lock()
2480 tr = repo.transaction('amend')
2490 tr = repo.transaction('amend')
2481 try:
2491 try:
2482 # See if we got a message from -m or -l, if not, open the editor
2492 # See if we got a message from -m or -l, if not, open the editor
2483 # with the message of the changeset to amend
2493 # with the message of the changeset to amend
2484 message = logmessage(ui, opts)
2494 message = logmessage(ui, opts)
2485 # ensure logfile does not conflict with later enforcement of the
2495 # ensure logfile does not conflict with later enforcement of the
2486 # message. potential logfile content has been processed by
2496 # message. potential logfile content has been processed by
2487 # `logmessage` anyway.
2497 # `logmessage` anyway.
2488 opts.pop('logfile')
2498 opts.pop('logfile')
2489 # First, do a regular commit to record all changes in the working
2499 # First, do a regular commit to record all changes in the working
2490 # directory (if there are any)
2500 # directory (if there are any)
2491 ui.callhooks = False
2501 ui.callhooks = False
2492 activebookmark = repo._activebookmark
2502 activebookmark = repo._activebookmark
2493 try:
2503 try:
2494 repo._activebookmark = None
2504 repo._activebookmark = None
2495 opts['message'] = 'temporary amend commit for %s' % old
2505 opts['message'] = 'temporary amend commit for %s' % old
2496 node = commit(ui, repo, commitfunc, pats, opts)
2506 node = commit(ui, repo, commitfunc, pats, opts)
2497 finally:
2507 finally:
2498 repo._activebookmark = activebookmark
2508 repo._activebookmark = activebookmark
2499 ui.callhooks = True
2509 ui.callhooks = True
2500 ctx = repo[node]
2510 ctx = repo[node]
2501
2511
2502 # Participating changesets:
2512 # Participating changesets:
2503 #
2513 #
2504 # node/ctx o - new (intermediate) commit that contains changes
2514 # node/ctx o - new (intermediate) commit that contains changes
2505 # | from working dir to go into amending commit
2515 # | from working dir to go into amending commit
2506 # | (or a workingctx if there were no changes)
2516 # | (or a workingctx if there were no changes)
2507 # |
2517 # |
2508 # old o - changeset to amend
2518 # old o - changeset to amend
2509 # |
2519 # |
2510 # base o - parent of amending changeset
2520 # base o - parent of amending changeset
2511
2521
2512 # Update extra dict from amended commit (e.g. to preserve graft
2522 # Update extra dict from amended commit (e.g. to preserve graft
2513 # source)
2523 # source)
2514 extra.update(old.extra())
2524 extra.update(old.extra())
2515
2525
2516 # Also update it from the intermediate commit or from the wctx
2526 # Also update it from the intermediate commit or from the wctx
2517 extra.update(ctx.extra())
2527 extra.update(ctx.extra())
2518
2528
2519 if len(old.parents()) > 1:
2529 if len(old.parents()) > 1:
2520 # ctx.files() isn't reliable for merges, so fall back to the
2530 # ctx.files() isn't reliable for merges, so fall back to the
2521 # slower repo.status() method
2531 # slower repo.status() method
2522 files = set([fn for st in repo.status(base, old)[:3]
2532 files = set([fn for st in repo.status(base, old)[:3]
2523 for fn in st])
2533 for fn in st])
2524 else:
2534 else:
2525 files = set(old.files())
2535 files = set(old.files())
2526
2536
2527 # Second, we use either the commit we just did, or if there were no
2537 # Second, we use either the commit we just did, or if there were no
2528 # changes the parent of the working directory as the version of the
2538 # changes the parent of the working directory as the version of the
2529 # files in the final amend commit
2539 # files in the final amend commit
2530 if node:
2540 if node:
2531 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2541 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2532
2542
2533 user = ctx.user()
2543 user = ctx.user()
2534 date = ctx.date()
2544 date = ctx.date()
2535 # Recompute copies (avoid recording a -> b -> a)
2545 # Recompute copies (avoid recording a -> b -> a)
2536 copied = copies.pathcopies(base, ctx)
2546 copied = copies.pathcopies(base, ctx)
2537 if old.p2:
2547 if old.p2:
2538 copied.update(copies.pathcopies(old.p2(), ctx))
2548 copied.update(copies.pathcopies(old.p2(), ctx))
2539
2549
2540 # Prune files which were reverted by the updates: if old
2550 # Prune files which were reverted by the updates: if old
2541 # introduced file X and our intermediate commit, node,
2551 # introduced file X and our intermediate commit, node,
2542 # renamed that file, then those two files are the same and
2552 # renamed that file, then those two files are the same and
2543 # we can discard X from our list of files. Likewise if X
2553 # we can discard X from our list of files. Likewise if X
2544 # was deleted, it's no longer relevant
2554 # was deleted, it's no longer relevant
2545 files.update(ctx.files())
2555 files.update(ctx.files())
2546
2556
2547 def samefile(f):
2557 def samefile(f):
2548 if f in ctx.manifest():
2558 if f in ctx.manifest():
2549 a = ctx.filectx(f)
2559 a = ctx.filectx(f)
2550 if f in base.manifest():
2560 if f in base.manifest():
2551 b = base.filectx(f)
2561 b = base.filectx(f)
2552 return (not a.cmp(b)
2562 return (not a.cmp(b)
2553 and a.flags() == b.flags())
2563 and a.flags() == b.flags())
2554 else:
2564 else:
2555 return False
2565 return False
2556 else:
2566 else:
2557 return f not in base.manifest()
2567 return f not in base.manifest()
2558 files = [f for f in files if not samefile(f)]
2568 files = [f for f in files if not samefile(f)]
2559
2569
2560 def filectxfn(repo, ctx_, path):
2570 def filectxfn(repo, ctx_, path):
2561 try:
2571 try:
2562 fctx = ctx[path]
2572 fctx = ctx[path]
2563 flags = fctx.flags()
2573 flags = fctx.flags()
2564 mctx = context.memfilectx(repo,
2574 mctx = context.memfilectx(repo,
2565 fctx.path(), fctx.data(),
2575 fctx.path(), fctx.data(),
2566 islink='l' in flags,
2576 islink='l' in flags,
2567 isexec='x' in flags,
2577 isexec='x' in flags,
2568 copied=copied.get(path))
2578 copied=copied.get(path))
2569 return mctx
2579 return mctx
2570 except KeyError:
2580 except KeyError:
2571 return None
2581 return None
2572 else:
2582 else:
2573 ui.note(_('copying changeset %s to %s\n') % (old, base))
2583 ui.note(_('copying changeset %s to %s\n') % (old, base))
2574
2584
2575 # Use version of files as in the old cset
2585 # Use version of files as in the old cset
2576 def filectxfn(repo, ctx_, path):
2586 def filectxfn(repo, ctx_, path):
2577 try:
2587 try:
2578 return old.filectx(path)
2588 return old.filectx(path)
2579 except KeyError:
2589 except KeyError:
2580 return None
2590 return None
2581
2591
2582 user = opts.get('user') or old.user()
2592 user = opts.get('user') or old.user()
2583 date = opts.get('date') or old.date()
2593 date = opts.get('date') or old.date()
2584 editform = mergeeditform(old, 'commit.amend')
2594 editform = mergeeditform(old, 'commit.amend')
2585 editor = getcommiteditor(editform=editform, **opts)
2595 editor = getcommiteditor(editform=editform, **opts)
2586 if not message:
2596 if not message:
2587 editor = getcommiteditor(edit=True, editform=editform)
2597 editor = getcommiteditor(edit=True, editform=editform)
2588 message = old.description()
2598 message = old.description()
2589
2599
2590 pureextra = extra.copy()
2600 pureextra = extra.copy()
2591 extra['amend_source'] = old.hex()
2601 extra['amend_source'] = old.hex()
2592
2602
2593 new = context.memctx(repo,
2603 new = context.memctx(repo,
2594 parents=[base.node(), old.p2().node()],
2604 parents=[base.node(), old.p2().node()],
2595 text=message,
2605 text=message,
2596 files=files,
2606 files=files,
2597 filectxfn=filectxfn,
2607 filectxfn=filectxfn,
2598 user=user,
2608 user=user,
2599 date=date,
2609 date=date,
2600 extra=extra,
2610 extra=extra,
2601 editor=editor)
2611 editor=editor)
2602
2612
2603 newdesc = changelog.stripdesc(new.description())
2613 newdesc = changelog.stripdesc(new.description())
2604 if ((not node)
2614 if ((not node)
2605 and newdesc == old.description()
2615 and newdesc == old.description()
2606 and user == old.user()
2616 and user == old.user()
2607 and date == old.date()
2617 and date == old.date()
2608 and pureextra == old.extra()):
2618 and pureextra == old.extra()):
2609 # nothing changed. continuing here would create a new node
2619 # nothing changed. continuing here would create a new node
2610 # anyway because of the amend_source noise.
2620 # anyway because of the amend_source noise.
2611 #
2621 #
2612 # This not what we expect from amend.
2622 # This not what we expect from amend.
2613 return old.node()
2623 return old.node()
2614
2624
2615 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2625 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2616 try:
2626 try:
2617 if opts.get('secret'):
2627 if opts.get('secret'):
2618 commitphase = 'secret'
2628 commitphase = 'secret'
2619 else:
2629 else:
2620 commitphase = old.phase()
2630 commitphase = old.phase()
2621 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2631 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2622 newid = repo.commitctx(new)
2632 newid = repo.commitctx(new)
2623 finally:
2633 finally:
2624 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2634 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2625 if newid != old.node():
2635 if newid != old.node():
2626 # Reroute the working copy parent to the new changeset
2636 # Reroute the working copy parent to the new changeset
2627 repo.setparents(newid, nullid)
2637 repo.setparents(newid, nullid)
2628
2638
2629 # Move bookmarks from old parent to amend commit
2639 # Move bookmarks from old parent to amend commit
2630 bms = repo.nodebookmarks(old.node())
2640 bms = repo.nodebookmarks(old.node())
2631 if bms:
2641 if bms:
2632 marks = repo._bookmarks
2642 marks = repo._bookmarks
2633 for bm in bms:
2643 for bm in bms:
2634 marks[bm] = newid
2644 marks[bm] = newid
2635 marks.write()
2645 marks.write()
2636 #commit the whole amend process
2646 #commit the whole amend process
2637 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2647 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2638 if createmarkers and newid != old.node():
2648 if createmarkers and newid != old.node():
2639 # mark the new changeset as successor of the rewritten one
2649 # mark the new changeset as successor of the rewritten one
2640 new = repo[newid]
2650 new = repo[newid]
2641 obs = [(old, (new,))]
2651 obs = [(old, (new,))]
2642 if node:
2652 if node:
2643 obs.append((ctx, ()))
2653 obs.append((ctx, ()))
2644
2654
2645 obsolete.createmarkers(repo, obs)
2655 obsolete.createmarkers(repo, obs)
2646 tr.close()
2656 tr.close()
2647 finally:
2657 finally:
2648 tr.release()
2658 tr.release()
2649 dsguard.close()
2659 dsguard.close()
2650 if not createmarkers and newid != old.node():
2660 if not createmarkers and newid != old.node():
2651 # Strip the intermediate commit (if there was one) and the amended
2661 # Strip the intermediate commit (if there was one) and the amended
2652 # commit
2662 # commit
2653 if node:
2663 if node:
2654 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2664 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2655 ui.note(_('stripping amended changeset %s\n') % old)
2665 ui.note(_('stripping amended changeset %s\n') % old)
2656 repair.strip(ui, repo, old.node(), topic='amend-backup')
2666 repair.strip(ui, repo, old.node(), topic='amend-backup')
2657 finally:
2667 finally:
2658 lockmod.release(lock, dsguard, wlock)
2668 lockmod.release(lock, dsguard, wlock)
2659 return newid
2669 return newid
2660
2670
2661 def commiteditor(repo, ctx, subs, editform=''):
2671 def commiteditor(repo, ctx, subs, editform=''):
2662 if ctx.description():
2672 if ctx.description():
2663 return ctx.description()
2673 return ctx.description()
2664 return commitforceeditor(repo, ctx, subs, editform=editform)
2674 return commitforceeditor(repo, ctx, subs, editform=editform)
2665
2675
2666 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2676 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2667 editform=''):
2677 editform=''):
2668 if not extramsg:
2678 if not extramsg:
2669 extramsg = _("Leave message empty to abort commit.")
2679 extramsg = _("Leave message empty to abort commit.")
2670
2680
2671 forms = [e for e in editform.split('.') if e]
2681 forms = [e for e in editform.split('.') if e]
2672 forms.insert(0, 'changeset')
2682 forms.insert(0, 'changeset')
2673 while forms:
2683 while forms:
2674 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2684 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2675 if tmpl:
2685 if tmpl:
2676 committext = buildcommittemplate(repo, ctx, subs, extramsg, tmpl)
2686 committext = buildcommittemplate(repo, ctx, subs, extramsg, tmpl)
2677 break
2687 break
2678 forms.pop()
2688 forms.pop()
2679 else:
2689 else:
2680 committext = buildcommittext(repo, ctx, subs, extramsg)
2690 committext = buildcommittext(repo, ctx, subs, extramsg)
2681
2691
2682 # run editor in the repository root
2692 # run editor in the repository root
2683 olddir = os.getcwd()
2693 olddir = os.getcwd()
2684 os.chdir(repo.root)
2694 os.chdir(repo.root)
2685 text = repo.ui.edit(committext, ctx.user(), ctx.extra(), editform=editform)
2695 text = repo.ui.edit(committext, ctx.user(), ctx.extra(), editform=editform)
2686 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2696 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2687 os.chdir(olddir)
2697 os.chdir(olddir)
2688
2698
2689 if finishdesc:
2699 if finishdesc:
2690 text = finishdesc(text)
2700 text = finishdesc(text)
2691 if not text.strip():
2701 if not text.strip():
2692 raise util.Abort(_("empty commit message"))
2702 raise util.Abort(_("empty commit message"))
2693
2703
2694 return text
2704 return text
2695
2705
2696 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2706 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2697 ui = repo.ui
2707 ui = repo.ui
2698 tmpl, mapfile = gettemplate(ui, tmpl, None)
2708 tmpl, mapfile = gettemplate(ui, tmpl, None)
2699
2709
2700 try:
2710 try:
2701 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2711 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2702 except SyntaxError, inst:
2712 except SyntaxError, inst:
2703 raise util.Abort(inst.args[0])
2713 raise util.Abort(inst.args[0])
2704
2714
2705 for k, v in repo.ui.configitems('committemplate'):
2715 for k, v in repo.ui.configitems('committemplate'):
2706 if k != 'changeset':
2716 if k != 'changeset':
2707 t.t.cache[k] = v
2717 t.t.cache[k] = v
2708
2718
2709 if not extramsg:
2719 if not extramsg:
2710 extramsg = '' # ensure that extramsg is string
2720 extramsg = '' # ensure that extramsg is string
2711
2721
2712 ui.pushbuffer()
2722 ui.pushbuffer()
2713 t.show(ctx, extramsg=extramsg)
2723 t.show(ctx, extramsg=extramsg)
2714 return ui.popbuffer()
2724 return ui.popbuffer()
2715
2725
2716 def buildcommittext(repo, ctx, subs, extramsg):
2726 def buildcommittext(repo, ctx, subs, extramsg):
2717 edittext = []
2727 edittext = []
2718 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2728 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2719 if ctx.description():
2729 if ctx.description():
2720 edittext.append(ctx.description())
2730 edittext.append(ctx.description())
2721 edittext.append("")
2731 edittext.append("")
2722 edittext.append("") # Empty line between message and comments.
2732 edittext.append("") # Empty line between message and comments.
2723 edittext.append(_("HG: Enter commit message."
2733 edittext.append(_("HG: Enter commit message."
2724 " Lines beginning with 'HG:' are removed."))
2734 " Lines beginning with 'HG:' are removed."))
2725 edittext.append("HG: %s" % extramsg)
2735 edittext.append("HG: %s" % extramsg)
2726 edittext.append("HG: --")
2736 edittext.append("HG: --")
2727 edittext.append(_("HG: user: %s") % ctx.user())
2737 edittext.append(_("HG: user: %s") % ctx.user())
2728 if ctx.p2():
2738 if ctx.p2():
2729 edittext.append(_("HG: branch merge"))
2739 edittext.append(_("HG: branch merge"))
2730 if ctx.branch():
2740 if ctx.branch():
2731 edittext.append(_("HG: branch '%s'") % ctx.branch())
2741 edittext.append(_("HG: branch '%s'") % ctx.branch())
2732 if bookmarks.isactivewdirparent(repo):
2742 if bookmarks.isactivewdirparent(repo):
2733 edittext.append(_("HG: bookmark '%s'") % repo._activebookmark)
2743 edittext.append(_("HG: bookmark '%s'") % repo._activebookmark)
2734 edittext.extend([_("HG: subrepo %s") % s for s in subs])
2744 edittext.extend([_("HG: subrepo %s") % s for s in subs])
2735 edittext.extend([_("HG: added %s") % f for f in added])
2745 edittext.extend([_("HG: added %s") % f for f in added])
2736 edittext.extend([_("HG: changed %s") % f for f in modified])
2746 edittext.extend([_("HG: changed %s") % f for f in modified])
2737 edittext.extend([_("HG: removed %s") % f for f in removed])
2747 edittext.extend([_("HG: removed %s") % f for f in removed])
2738 if not added and not modified and not removed:
2748 if not added and not modified and not removed:
2739 edittext.append(_("HG: no files changed"))
2749 edittext.append(_("HG: no files changed"))
2740 edittext.append("")
2750 edittext.append("")
2741
2751
2742 return "\n".join(edittext)
2752 return "\n".join(edittext)
2743
2753
2744 def commitstatus(repo, node, branch, bheads=None, opts={}):
2754 def commitstatus(repo, node, branch, bheads=None, opts={}):
2745 ctx = repo[node]
2755 ctx = repo[node]
2746 parents = ctx.parents()
2756 parents = ctx.parents()
2747
2757
2748 if (not opts.get('amend') and bheads and node not in bheads and not
2758 if (not opts.get('amend') and bheads and node not in bheads and not
2749 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2759 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2750 repo.ui.status(_('created new head\n'))
2760 repo.ui.status(_('created new head\n'))
2751 # The message is not printed for initial roots. For the other
2761 # The message is not printed for initial roots. For the other
2752 # changesets, it is printed in the following situations:
2762 # changesets, it is printed in the following situations:
2753 #
2763 #
2754 # Par column: for the 2 parents with ...
2764 # Par column: for the 2 parents with ...
2755 # N: null or no parent
2765 # N: null or no parent
2756 # B: parent is on another named branch
2766 # B: parent is on another named branch
2757 # C: parent is a regular non head changeset
2767 # C: parent is a regular non head changeset
2758 # H: parent was a branch head of the current branch
2768 # H: parent was a branch head of the current branch
2759 # Msg column: whether we print "created new head" message
2769 # Msg column: whether we print "created new head" message
2760 # In the following, it is assumed that there already exists some
2770 # In the following, it is assumed that there already exists some
2761 # initial branch heads of the current branch, otherwise nothing is
2771 # initial branch heads of the current branch, otherwise nothing is
2762 # printed anyway.
2772 # printed anyway.
2763 #
2773 #
2764 # Par Msg Comment
2774 # Par Msg Comment
2765 # N N y additional topo root
2775 # N N y additional topo root
2766 #
2776 #
2767 # B N y additional branch root
2777 # B N y additional branch root
2768 # C N y additional topo head
2778 # C N y additional topo head
2769 # H N n usual case
2779 # H N n usual case
2770 #
2780 #
2771 # B B y weird additional branch root
2781 # B B y weird additional branch root
2772 # C B y branch merge
2782 # C B y branch merge
2773 # H B n merge with named branch
2783 # H B n merge with named branch
2774 #
2784 #
2775 # C C y additional head from merge
2785 # C C y additional head from merge
2776 # C H n merge with a head
2786 # C H n merge with a head
2777 #
2787 #
2778 # H H n head merge: head count decreases
2788 # H H n head merge: head count decreases
2779
2789
2780 if not opts.get('close_branch'):
2790 if not opts.get('close_branch'):
2781 for r in parents:
2791 for r in parents:
2782 if r.closesbranch() and r.branch() == branch:
2792 if r.closesbranch() and r.branch() == branch:
2783 repo.ui.status(_('reopening closed branch head %d\n') % r)
2793 repo.ui.status(_('reopening closed branch head %d\n') % r)
2784
2794
2785 if repo.ui.debugflag:
2795 if repo.ui.debugflag:
2786 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2796 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2787 elif repo.ui.verbose:
2797 elif repo.ui.verbose:
2788 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2798 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2789
2799
2790 def revert(ui, repo, ctx, parents, *pats, **opts):
2800 def revert(ui, repo, ctx, parents, *pats, **opts):
2791 parent, p2 = parents
2801 parent, p2 = parents
2792 node = ctx.node()
2802 node = ctx.node()
2793
2803
2794 mf = ctx.manifest()
2804 mf = ctx.manifest()
2795 if node == p2:
2805 if node == p2:
2796 parent = p2
2806 parent = p2
2797 if node == parent:
2807 if node == parent:
2798 pmf = mf
2808 pmf = mf
2799 else:
2809 else:
2800 pmf = None
2810 pmf = None
2801
2811
2802 # need all matching names in dirstate and manifest of target rev,
2812 # need all matching names in dirstate and manifest of target rev,
2803 # so have to walk both. do not print errors if files exist in one
2813 # so have to walk both. do not print errors if files exist in one
2804 # but not other. in both cases, filesets should be evaluated against
2814 # but not other. in both cases, filesets should be evaluated against
2805 # workingctx to get consistent result (issue4497). this means 'set:**'
2815 # workingctx to get consistent result (issue4497). this means 'set:**'
2806 # cannot be used to select missing files from target rev.
2816 # cannot be used to select missing files from target rev.
2807
2817
2808 # `names` is a mapping for all elements in working copy and target revision
2818 # `names` is a mapping for all elements in working copy and target revision
2809 # The mapping is in the form:
2819 # The mapping is in the form:
2810 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2820 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2811 names = {}
2821 names = {}
2812
2822
2813 wlock = repo.wlock()
2823 wlock = repo.wlock()
2814 try:
2824 try:
2815 ## filling of the `names` mapping
2825 ## filling of the `names` mapping
2816 # walk dirstate to fill `names`
2826 # walk dirstate to fill `names`
2817
2827
2818 interactive = opts.get('interactive', False)
2828 interactive = opts.get('interactive', False)
2819 wctx = repo[None]
2829 wctx = repo[None]
2820 m = scmutil.match(wctx, pats, opts)
2830 m = scmutil.match(wctx, pats, opts)
2821
2831
2822 # we'll need this later
2832 # we'll need this later
2823 targetsubs = sorted(s for s in wctx.substate if m(s))
2833 targetsubs = sorted(s for s in wctx.substate if m(s))
2824
2834
2825 if not m.always():
2835 if not m.always():
2826 m.bad = lambda x, y: False
2836 m.bad = lambda x, y: False
2827 for abs in repo.walk(m):
2837 for abs in repo.walk(m):
2828 names[abs] = m.rel(abs), m.exact(abs)
2838 names[abs] = m.rel(abs), m.exact(abs)
2829
2839
2830 # walk target manifest to fill `names`
2840 # walk target manifest to fill `names`
2831
2841
2832 def badfn(path, msg):
2842 def badfn(path, msg):
2833 if path in names:
2843 if path in names:
2834 return
2844 return
2835 if path in ctx.substate:
2845 if path in ctx.substate:
2836 return
2846 return
2837 path_ = path + '/'
2847 path_ = path + '/'
2838 for f in names:
2848 for f in names:
2839 if f.startswith(path_):
2849 if f.startswith(path_):
2840 return
2850 return
2841 ui.warn("%s: %s\n" % (m.rel(path), msg))
2851 ui.warn("%s: %s\n" % (m.rel(path), msg))
2842
2852
2843 m.bad = badfn
2853 m.bad = badfn
2844 for abs in ctx.walk(m):
2854 for abs in ctx.walk(m):
2845 if abs not in names:
2855 if abs not in names:
2846 names[abs] = m.rel(abs), m.exact(abs)
2856 names[abs] = m.rel(abs), m.exact(abs)
2847
2857
2848 # Find status of all file in `names`.
2858 # Find status of all file in `names`.
2849 m = scmutil.matchfiles(repo, names)
2859 m = scmutil.matchfiles(repo, names)
2850
2860
2851 changes = repo.status(node1=node, match=m,
2861 changes = repo.status(node1=node, match=m,
2852 unknown=True, ignored=True, clean=True)
2862 unknown=True, ignored=True, clean=True)
2853 else:
2863 else:
2854 changes = repo.status(node1=node, match=m)
2864 changes = repo.status(node1=node, match=m)
2855 for kind in changes:
2865 for kind in changes:
2856 for abs in kind:
2866 for abs in kind:
2857 names[abs] = m.rel(abs), m.exact(abs)
2867 names[abs] = m.rel(abs), m.exact(abs)
2858
2868
2859 m = scmutil.matchfiles(repo, names)
2869 m = scmutil.matchfiles(repo, names)
2860
2870
2861 modified = set(changes.modified)
2871 modified = set(changes.modified)
2862 added = set(changes.added)
2872 added = set(changes.added)
2863 removed = set(changes.removed)
2873 removed = set(changes.removed)
2864 _deleted = set(changes.deleted)
2874 _deleted = set(changes.deleted)
2865 unknown = set(changes.unknown)
2875 unknown = set(changes.unknown)
2866 unknown.update(changes.ignored)
2876 unknown.update(changes.ignored)
2867 clean = set(changes.clean)
2877 clean = set(changes.clean)
2868 modadded = set()
2878 modadded = set()
2869
2879
2870 # split between files known in target manifest and the others
2880 # split between files known in target manifest and the others
2871 smf = set(mf)
2881 smf = set(mf)
2872
2882
2873 # determine the exact nature of the deleted changesets
2883 # determine the exact nature of the deleted changesets
2874 deladded = _deleted - smf
2884 deladded = _deleted - smf
2875 deleted = _deleted - deladded
2885 deleted = _deleted - deladded
2876
2886
2877 # We need to account for the state of the file in the dirstate,
2887 # We need to account for the state of the file in the dirstate,
2878 # even when we revert against something else than parent. This will
2888 # even when we revert against something else than parent. This will
2879 # slightly alter the behavior of revert (doing back up or not, delete
2889 # slightly alter the behavior of revert (doing back up or not, delete
2880 # or just forget etc).
2890 # or just forget etc).
2881 if parent == node:
2891 if parent == node:
2882 dsmodified = modified
2892 dsmodified = modified
2883 dsadded = added
2893 dsadded = added
2884 dsremoved = removed
2894 dsremoved = removed
2885 # store all local modifications, useful later for rename detection
2895 # store all local modifications, useful later for rename detection
2886 localchanges = dsmodified | dsadded
2896 localchanges = dsmodified | dsadded
2887 modified, added, removed = set(), set(), set()
2897 modified, added, removed = set(), set(), set()
2888 else:
2898 else:
2889 changes = repo.status(node1=parent, match=m)
2899 changes = repo.status(node1=parent, match=m)
2890 dsmodified = set(changes.modified)
2900 dsmodified = set(changes.modified)
2891 dsadded = set(changes.added)
2901 dsadded = set(changes.added)
2892 dsremoved = set(changes.removed)
2902 dsremoved = set(changes.removed)
2893 # store all local modifications, useful later for rename detection
2903 # store all local modifications, useful later for rename detection
2894 localchanges = dsmodified | dsadded
2904 localchanges = dsmodified | dsadded
2895
2905
2896 # only take into account for removes between wc and target
2906 # only take into account for removes between wc and target
2897 clean |= dsremoved - removed
2907 clean |= dsremoved - removed
2898 dsremoved &= removed
2908 dsremoved &= removed
2899 # distinct between dirstate remove and other
2909 # distinct between dirstate remove and other
2900 removed -= dsremoved
2910 removed -= dsremoved
2901
2911
2902 modadded = added & dsmodified
2912 modadded = added & dsmodified
2903 added -= modadded
2913 added -= modadded
2904
2914
2905 # tell newly modified apart.
2915 # tell newly modified apart.
2906 dsmodified &= modified
2916 dsmodified &= modified
2907 dsmodified |= modified & dsadded # dirstate added may needs backup
2917 dsmodified |= modified & dsadded # dirstate added may needs backup
2908 modified -= dsmodified
2918 modified -= dsmodified
2909
2919
2910 # We need to wait for some post-processing to update this set
2920 # We need to wait for some post-processing to update this set
2911 # before making the distinction. The dirstate will be used for
2921 # before making the distinction. The dirstate will be used for
2912 # that purpose.
2922 # that purpose.
2913 dsadded = added
2923 dsadded = added
2914
2924
2915 # in case of merge, files that are actually added can be reported as
2925 # in case of merge, files that are actually added can be reported as
2916 # modified, we need to post process the result
2926 # modified, we need to post process the result
2917 if p2 != nullid:
2927 if p2 != nullid:
2918 if pmf is None:
2928 if pmf is None:
2919 # only need parent manifest in the merge case,
2929 # only need parent manifest in the merge case,
2920 # so do not read by default
2930 # so do not read by default
2921 pmf = repo[parent].manifest()
2931 pmf = repo[parent].manifest()
2922 mergeadd = dsmodified - set(pmf)
2932 mergeadd = dsmodified - set(pmf)
2923 dsadded |= mergeadd
2933 dsadded |= mergeadd
2924 dsmodified -= mergeadd
2934 dsmodified -= mergeadd
2925
2935
2926 # if f is a rename, update `names` to also revert the source
2936 # if f is a rename, update `names` to also revert the source
2927 cwd = repo.getcwd()
2937 cwd = repo.getcwd()
2928 for f in localchanges:
2938 for f in localchanges:
2929 src = repo.dirstate.copied(f)
2939 src = repo.dirstate.copied(f)
2930 # XXX should we check for rename down to target node?
2940 # XXX should we check for rename down to target node?
2931 if src and src not in names and repo.dirstate[src] == 'r':
2941 if src and src not in names and repo.dirstate[src] == 'r':
2932 dsremoved.add(src)
2942 dsremoved.add(src)
2933 names[src] = (repo.pathto(src, cwd), True)
2943 names[src] = (repo.pathto(src, cwd), True)
2934
2944
2935 # distinguish between file to forget and the other
2945 # distinguish between file to forget and the other
2936 added = set()
2946 added = set()
2937 for abs in dsadded:
2947 for abs in dsadded:
2938 if repo.dirstate[abs] != 'a':
2948 if repo.dirstate[abs] != 'a':
2939 added.add(abs)
2949 added.add(abs)
2940 dsadded -= added
2950 dsadded -= added
2941
2951
2942 for abs in deladded:
2952 for abs in deladded:
2943 if repo.dirstate[abs] == 'a':
2953 if repo.dirstate[abs] == 'a':
2944 dsadded.add(abs)
2954 dsadded.add(abs)
2945 deladded -= dsadded
2955 deladded -= dsadded
2946
2956
2947 # For files marked as removed, we check if an unknown file is present at
2957 # For files marked as removed, we check if an unknown file is present at
2948 # the same path. If a such file exists it may need to be backed up.
2958 # the same path. If a such file exists it may need to be backed up.
2949 # Making the distinction at this stage helps have simpler backup
2959 # Making the distinction at this stage helps have simpler backup
2950 # logic.
2960 # logic.
2951 removunk = set()
2961 removunk = set()
2952 for abs in removed:
2962 for abs in removed:
2953 target = repo.wjoin(abs)
2963 target = repo.wjoin(abs)
2954 if os.path.lexists(target):
2964 if os.path.lexists(target):
2955 removunk.add(abs)
2965 removunk.add(abs)
2956 removed -= removunk
2966 removed -= removunk
2957
2967
2958 dsremovunk = set()
2968 dsremovunk = set()
2959 for abs in dsremoved:
2969 for abs in dsremoved:
2960 target = repo.wjoin(abs)
2970 target = repo.wjoin(abs)
2961 if os.path.lexists(target):
2971 if os.path.lexists(target):
2962 dsremovunk.add(abs)
2972 dsremovunk.add(abs)
2963 dsremoved -= dsremovunk
2973 dsremoved -= dsremovunk
2964
2974
2965 # action to be actually performed by revert
2975 # action to be actually performed by revert
2966 # (<list of file>, message>) tuple
2976 # (<list of file>, message>) tuple
2967 actions = {'revert': ([], _('reverting %s\n')),
2977 actions = {'revert': ([], _('reverting %s\n')),
2968 'add': ([], _('adding %s\n')),
2978 'add': ([], _('adding %s\n')),
2969 'remove': ([], _('removing %s\n')),
2979 'remove': ([], _('removing %s\n')),
2970 'drop': ([], _('removing %s\n')),
2980 'drop': ([], _('removing %s\n')),
2971 'forget': ([], _('forgetting %s\n')),
2981 'forget': ([], _('forgetting %s\n')),
2972 'undelete': ([], _('undeleting %s\n')),
2982 'undelete': ([], _('undeleting %s\n')),
2973 'noop': (None, _('no changes needed to %s\n')),
2983 'noop': (None, _('no changes needed to %s\n')),
2974 'unknown': (None, _('file not managed: %s\n')),
2984 'unknown': (None, _('file not managed: %s\n')),
2975 }
2985 }
2976
2986
2977 # "constant" that convey the backup strategy.
2987 # "constant" that convey the backup strategy.
2978 # All set to `discard` if `no-backup` is set do avoid checking
2988 # All set to `discard` if `no-backup` is set do avoid checking
2979 # no_backup lower in the code.
2989 # no_backup lower in the code.
2980 # These values are ordered for comparison purposes
2990 # These values are ordered for comparison purposes
2981 backup = 2 # unconditionally do backup
2991 backup = 2 # unconditionally do backup
2982 check = 1 # check if the existing file differs from target
2992 check = 1 # check if the existing file differs from target
2983 discard = 0 # never do backup
2993 discard = 0 # never do backup
2984 if opts.get('no_backup'):
2994 if opts.get('no_backup'):
2985 backup = check = discard
2995 backup = check = discard
2986
2996
2987 backupanddel = actions['remove']
2997 backupanddel = actions['remove']
2988 if not opts.get('no_backup'):
2998 if not opts.get('no_backup'):
2989 backupanddel = actions['drop']
2999 backupanddel = actions['drop']
2990
3000
2991 disptable = (
3001 disptable = (
2992 # dispatch table:
3002 # dispatch table:
2993 # file state
3003 # file state
2994 # action
3004 # action
2995 # make backup
3005 # make backup
2996
3006
2997 ## Sets that results that will change file on disk
3007 ## Sets that results that will change file on disk
2998 # Modified compared to target, no local change
3008 # Modified compared to target, no local change
2999 (modified, actions['revert'], discard),
3009 (modified, actions['revert'], discard),
3000 # Modified compared to target, but local file is deleted
3010 # Modified compared to target, but local file is deleted
3001 (deleted, actions['revert'], discard),
3011 (deleted, actions['revert'], discard),
3002 # Modified compared to target, local change
3012 # Modified compared to target, local change
3003 (dsmodified, actions['revert'], backup),
3013 (dsmodified, actions['revert'], backup),
3004 # Added since target
3014 # Added since target
3005 (added, actions['remove'], discard),
3015 (added, actions['remove'], discard),
3006 # Added in working directory
3016 # Added in working directory
3007 (dsadded, actions['forget'], discard),
3017 (dsadded, actions['forget'], discard),
3008 # Added since target, have local modification
3018 # Added since target, have local modification
3009 (modadded, backupanddel, backup),
3019 (modadded, backupanddel, backup),
3010 # Added since target but file is missing in working directory
3020 # Added since target but file is missing in working directory
3011 (deladded, actions['drop'], discard),
3021 (deladded, actions['drop'], discard),
3012 # Removed since target, before working copy parent
3022 # Removed since target, before working copy parent
3013 (removed, actions['add'], discard),
3023 (removed, actions['add'], discard),
3014 # Same as `removed` but an unknown file exists at the same path
3024 # Same as `removed` but an unknown file exists at the same path
3015 (removunk, actions['add'], check),
3025 (removunk, actions['add'], check),
3016 # Removed since targe, marked as such in working copy parent
3026 # Removed since targe, marked as such in working copy parent
3017 (dsremoved, actions['undelete'], discard),
3027 (dsremoved, actions['undelete'], discard),
3018 # Same as `dsremoved` but an unknown file exists at the same path
3028 # Same as `dsremoved` but an unknown file exists at the same path
3019 (dsremovunk, actions['undelete'], check),
3029 (dsremovunk, actions['undelete'], check),
3020 ## the following sets does not result in any file changes
3030 ## the following sets does not result in any file changes
3021 # File with no modification
3031 # File with no modification
3022 (clean, actions['noop'], discard),
3032 (clean, actions['noop'], discard),
3023 # Existing file, not tracked anywhere
3033 # Existing file, not tracked anywhere
3024 (unknown, actions['unknown'], discard),
3034 (unknown, actions['unknown'], discard),
3025 )
3035 )
3026
3036
3027 for abs, (rel, exact) in sorted(names.items()):
3037 for abs, (rel, exact) in sorted(names.items()):
3028 # target file to be touch on disk (relative to cwd)
3038 # target file to be touch on disk (relative to cwd)
3029 target = repo.wjoin(abs)
3039 target = repo.wjoin(abs)
3030 # search the entry in the dispatch table.
3040 # search the entry in the dispatch table.
3031 # if the file is in any of these sets, it was touched in the working
3041 # if the file is in any of these sets, it was touched in the working
3032 # directory parent and we are sure it needs to be reverted.
3042 # directory parent and we are sure it needs to be reverted.
3033 for table, (xlist, msg), dobackup in disptable:
3043 for table, (xlist, msg), dobackup in disptable:
3034 if abs not in table:
3044 if abs not in table:
3035 continue
3045 continue
3036 if xlist is not None:
3046 if xlist is not None:
3037 xlist.append(abs)
3047 xlist.append(abs)
3038 if dobackup and (backup <= dobackup
3048 if dobackup and (backup <= dobackup
3039 or wctx[abs].cmp(ctx[abs])):
3049 or wctx[abs].cmp(ctx[abs])):
3040 bakname = "%s.orig" % rel
3050 bakname = "%s.orig" % rel
3041 ui.note(_('saving current version of %s as %s\n') %
3051 ui.note(_('saving current version of %s as %s\n') %
3042 (rel, bakname))
3052 (rel, bakname))
3043 if not opts.get('dry_run'):
3053 if not opts.get('dry_run'):
3044 if interactive:
3054 if interactive:
3045 util.copyfile(target, bakname)
3055 util.copyfile(target, bakname)
3046 else:
3056 else:
3047 util.rename(target, bakname)
3057 util.rename(target, bakname)
3048 if ui.verbose or not exact:
3058 if ui.verbose or not exact:
3049 if not isinstance(msg, basestring):
3059 if not isinstance(msg, basestring):
3050 msg = msg(abs)
3060 msg = msg(abs)
3051 ui.status(msg % rel)
3061 ui.status(msg % rel)
3052 elif exact:
3062 elif exact:
3053 ui.warn(msg % rel)
3063 ui.warn(msg % rel)
3054 break
3064 break
3055
3065
3056 if not opts.get('dry_run'):
3066 if not opts.get('dry_run'):
3057 needdata = ('revert', 'add', 'undelete')
3067 needdata = ('revert', 'add', 'undelete')
3058 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3068 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3059 _performrevert(repo, parents, ctx, actions, interactive)
3069 _performrevert(repo, parents, ctx, actions, interactive)
3060
3070
3061 if targetsubs:
3071 if targetsubs:
3062 # Revert the subrepos on the revert list
3072 # Revert the subrepos on the revert list
3063 for sub in targetsubs:
3073 for sub in targetsubs:
3064 try:
3074 try:
3065 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3075 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3066 except KeyError:
3076 except KeyError:
3067 raise util.Abort("subrepository '%s' does not exist in %s!"
3077 raise util.Abort("subrepository '%s' does not exist in %s!"
3068 % (sub, short(ctx.node())))
3078 % (sub, short(ctx.node())))
3069 finally:
3079 finally:
3070 wlock.release()
3080 wlock.release()
3071
3081
3072 def _revertprefetch(repo, ctx, *files):
3082 def _revertprefetch(repo, ctx, *files):
3073 """Let extension changing the storage layer prefetch content"""
3083 """Let extension changing the storage layer prefetch content"""
3074 pass
3084 pass
3075
3085
3076 def _performrevert(repo, parents, ctx, actions, interactive=False):
3086 def _performrevert(repo, parents, ctx, actions, interactive=False):
3077 """function that actually perform all the actions computed for revert
3087 """function that actually perform all the actions computed for revert
3078
3088
3079 This is an independent function to let extension to plug in and react to
3089 This is an independent function to let extension to plug in and react to
3080 the imminent revert.
3090 the imminent revert.
3081
3091
3082 Make sure you have the working directory locked when calling this function.
3092 Make sure you have the working directory locked when calling this function.
3083 """
3093 """
3084 parent, p2 = parents
3094 parent, p2 = parents
3085 node = ctx.node()
3095 node = ctx.node()
3086 def checkout(f):
3096 def checkout(f):
3087 fc = ctx[f]
3097 fc = ctx[f]
3088 return repo.wwrite(f, fc.data(), fc.flags())
3098 return repo.wwrite(f, fc.data(), fc.flags())
3089
3099
3090 audit_path = pathutil.pathauditor(repo.root)
3100 audit_path = pathutil.pathauditor(repo.root)
3091 for f in actions['forget'][0]:
3101 for f in actions['forget'][0]:
3092 repo.dirstate.drop(f)
3102 repo.dirstate.drop(f)
3093 for f in actions['remove'][0]:
3103 for f in actions['remove'][0]:
3094 audit_path(f)
3104 audit_path(f)
3095 try:
3105 try:
3096 util.unlinkpath(repo.wjoin(f))
3106 util.unlinkpath(repo.wjoin(f))
3097 except OSError:
3107 except OSError:
3098 pass
3108 pass
3099 repo.dirstate.remove(f)
3109 repo.dirstate.remove(f)
3100 for f in actions['drop'][0]:
3110 for f in actions['drop'][0]:
3101 audit_path(f)
3111 audit_path(f)
3102 repo.dirstate.remove(f)
3112 repo.dirstate.remove(f)
3103
3113
3104 normal = None
3114 normal = None
3105 if node == parent:
3115 if node == parent:
3106 # We're reverting to our parent. If possible, we'd like status
3116 # We're reverting to our parent. If possible, we'd like status
3107 # to report the file as clean. We have to use normallookup for
3117 # to report the file as clean. We have to use normallookup for
3108 # merges to avoid losing information about merged/dirty files.
3118 # merges to avoid losing information about merged/dirty files.
3109 if p2 != nullid:
3119 if p2 != nullid:
3110 normal = repo.dirstate.normallookup
3120 normal = repo.dirstate.normallookup
3111 else:
3121 else:
3112 normal = repo.dirstate.normal
3122 normal = repo.dirstate.normal
3113
3123
3114 if interactive:
3124 if interactive:
3115 # Prompt the user for changes to revert
3125 # Prompt the user for changes to revert
3116 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3126 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3117 m = scmutil.match(ctx, torevert, {})
3127 m = scmutil.match(ctx, torevert, {})
3118 diff = patch.diff(repo, None, ctx.node(), m)
3128 diff = patch.diff(repo, None, ctx.node(), m)
3119 originalchunks = patch.parsepatch(diff)
3129 originalchunks = patch.parsepatch(diff)
3120 try:
3130 try:
3121 chunks = recordfilter(repo.ui, originalchunks)
3131 chunks = recordfilter(repo.ui, originalchunks)
3122 except patch.PatchError, err:
3132 except patch.PatchError, err:
3123 raise util.Abort(_('error parsing patch: %s') % err)
3133 raise util.Abort(_('error parsing patch: %s') % err)
3124
3134
3125 # Apply changes
3135 # Apply changes
3126 fp = cStringIO.StringIO()
3136 fp = cStringIO.StringIO()
3127 for c in chunks:
3137 for c in chunks:
3128 c.write(fp)
3138 c.write(fp)
3129 dopatch = fp.tell()
3139 dopatch = fp.tell()
3130 fp.seek(0)
3140 fp.seek(0)
3131 if dopatch:
3141 if dopatch:
3132 try:
3142 try:
3133 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3143 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3134 except patch.PatchError, err:
3144 except patch.PatchError, err:
3135 raise util.Abort(str(err))
3145 raise util.Abort(str(err))
3136 del fp
3146 del fp
3137 else:
3147 else:
3138 for f in actions['revert'][0]:
3148 for f in actions['revert'][0]:
3139 wsize = checkout(f)
3149 wsize = checkout(f)
3140 if normal:
3150 if normal:
3141 normal(f)
3151 normal(f)
3142 elif wsize == repo.dirstate._map[f][2]:
3152 elif wsize == repo.dirstate._map[f][2]:
3143 # changes may be overlooked without normallookup,
3153 # changes may be overlooked without normallookup,
3144 # if size isn't changed at reverting
3154 # if size isn't changed at reverting
3145 repo.dirstate.normallookup(f)
3155 repo.dirstate.normallookup(f)
3146
3156
3147 for f in actions['add'][0]:
3157 for f in actions['add'][0]:
3148 checkout(f)
3158 checkout(f)
3149 repo.dirstate.add(f)
3159 repo.dirstate.add(f)
3150
3160
3151 normal = repo.dirstate.normallookup
3161 normal = repo.dirstate.normallookup
3152 if node == parent and p2 == nullid:
3162 if node == parent and p2 == nullid:
3153 normal = repo.dirstate.normal
3163 normal = repo.dirstate.normal
3154 for f in actions['undelete'][0]:
3164 for f in actions['undelete'][0]:
3155 checkout(f)
3165 checkout(f)
3156 normal(f)
3166 normal(f)
3157
3167
3158 copied = copies.pathcopies(repo[parent], ctx)
3168 copied = copies.pathcopies(repo[parent], ctx)
3159
3169
3160 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3170 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3161 if f in copied:
3171 if f in copied:
3162 repo.dirstate.copy(copied[f], f)
3172 repo.dirstate.copy(copied[f], f)
3163
3173
3164 def command(table):
3174 def command(table):
3165 """Returns a function object to be used as a decorator for making commands.
3175 """Returns a function object to be used as a decorator for making commands.
3166
3176
3167 This function receives a command table as its argument. The table should
3177 This function receives a command table as its argument. The table should
3168 be a dict.
3178 be a dict.
3169
3179
3170 The returned function can be used as a decorator for adding commands
3180 The returned function can be used as a decorator for adding commands
3171 to that command table. This function accepts multiple arguments to define
3181 to that command table. This function accepts multiple arguments to define
3172 a command.
3182 a command.
3173
3183
3174 The first argument is the command name.
3184 The first argument is the command name.
3175
3185
3176 The options argument is an iterable of tuples defining command arguments.
3186 The options argument is an iterable of tuples defining command arguments.
3177 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
3187 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
3178
3188
3179 The synopsis argument defines a short, one line summary of how to use the
3189 The synopsis argument defines a short, one line summary of how to use the
3180 command. This shows up in the help output.
3190 command. This shows up in the help output.
3181
3191
3182 The norepo argument defines whether the command does not require a
3192 The norepo argument defines whether the command does not require a
3183 local repository. Most commands operate against a repository, thus the
3193 local repository. Most commands operate against a repository, thus the
3184 default is False.
3194 default is False.
3185
3195
3186 The optionalrepo argument defines whether the command optionally requires
3196 The optionalrepo argument defines whether the command optionally requires
3187 a local repository.
3197 a local repository.
3188
3198
3189 The inferrepo argument defines whether to try to find a repository from the
3199 The inferrepo argument defines whether to try to find a repository from the
3190 command line arguments. If True, arguments will be examined for potential
3200 command line arguments. If True, arguments will be examined for potential
3191 repository locations. See ``findrepo()``. If a repository is found, it
3201 repository locations. See ``findrepo()``. If a repository is found, it
3192 will be used.
3202 will be used.
3193 """
3203 """
3194 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
3204 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
3195 inferrepo=False):
3205 inferrepo=False):
3196 def decorator(func):
3206 def decorator(func):
3197 if synopsis:
3207 if synopsis:
3198 table[name] = func, list(options), synopsis
3208 table[name] = func, list(options), synopsis
3199 else:
3209 else:
3200 table[name] = func, list(options)
3210 table[name] = func, list(options)
3201
3211
3202 if norepo:
3212 if norepo:
3203 # Avoid import cycle.
3213 # Avoid import cycle.
3204 import commands
3214 import commands
3205 commands.norepo += ' %s' % ' '.join(parsealiases(name))
3215 commands.norepo += ' %s' % ' '.join(parsealiases(name))
3206
3216
3207 if optionalrepo:
3217 if optionalrepo:
3208 import commands
3218 import commands
3209 commands.optionalrepo += ' %s' % ' '.join(parsealiases(name))
3219 commands.optionalrepo += ' %s' % ' '.join(parsealiases(name))
3210
3220
3211 if inferrepo:
3221 if inferrepo:
3212 import commands
3222 import commands
3213 commands.inferrepo += ' %s' % ' '.join(parsealiases(name))
3223 commands.inferrepo += ' %s' % ' '.join(parsealiases(name))
3214
3224
3215 return func
3225 return func
3216 return decorator
3226 return decorator
3217
3227
3218 return cmd
3228 return cmd
3219
3229
3220 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3230 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3221 # commands.outgoing. "missing" is "missing" of the result of
3231 # commands.outgoing. "missing" is "missing" of the result of
3222 # "findcommonoutgoing()"
3232 # "findcommonoutgoing()"
3223 outgoinghooks = util.hooks()
3233 outgoinghooks = util.hooks()
3224
3234
3225 # a list of (ui, repo) functions called by commands.summary
3235 # a list of (ui, repo) functions called by commands.summary
3226 summaryhooks = util.hooks()
3236 summaryhooks = util.hooks()
3227
3237
3228 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3238 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3229 #
3239 #
3230 # functions should return tuple of booleans below, if 'changes' is None:
3240 # functions should return tuple of booleans below, if 'changes' is None:
3231 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3241 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3232 #
3242 #
3233 # otherwise, 'changes' is a tuple of tuples below:
3243 # otherwise, 'changes' is a tuple of tuples below:
3234 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3244 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3235 # - (desturl, destbranch, destpeer, outgoing)
3245 # - (desturl, destbranch, destpeer, outgoing)
3236 summaryremotehooks = util.hooks()
3246 summaryremotehooks = util.hooks()
3237
3247
3238 # A list of state files kept by multistep operations like graft.
3248 # A list of state files kept by multistep operations like graft.
3239 # Since graft cannot be aborted, it is considered 'clearable' by update.
3249 # Since graft cannot be aborted, it is considered 'clearable' by update.
3240 # note: bisect is intentionally excluded
3250 # note: bisect is intentionally excluded
3241 # (state file, clearable, allowcommit, error, hint)
3251 # (state file, clearable, allowcommit, error, hint)
3242 unfinishedstates = [
3252 unfinishedstates = [
3243 ('graftstate', True, False, _('graft in progress'),
3253 ('graftstate', True, False, _('graft in progress'),
3244 _("use 'hg graft --continue' or 'hg update' to abort")),
3254 _("use 'hg graft --continue' or 'hg update' to abort")),
3245 ('updatestate', True, False, _('last update was interrupted'),
3255 ('updatestate', True, False, _('last update was interrupted'),
3246 _("use 'hg update' to get a consistent checkout"))
3256 _("use 'hg update' to get a consistent checkout"))
3247 ]
3257 ]
3248
3258
3249 def checkunfinished(repo, commit=False):
3259 def checkunfinished(repo, commit=False):
3250 '''Look for an unfinished multistep operation, like graft, and abort
3260 '''Look for an unfinished multistep operation, like graft, and abort
3251 if found. It's probably good to check this right before
3261 if found. It's probably good to check this right before
3252 bailifchanged().
3262 bailifchanged().
3253 '''
3263 '''
3254 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3264 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3255 if commit and allowcommit:
3265 if commit and allowcommit:
3256 continue
3266 continue
3257 if repo.vfs.exists(f):
3267 if repo.vfs.exists(f):
3258 raise util.Abort(msg, hint=hint)
3268 raise util.Abort(msg, hint=hint)
3259
3269
3260 def clearunfinished(repo):
3270 def clearunfinished(repo):
3261 '''Check for unfinished operations (as above), and clear the ones
3271 '''Check for unfinished operations (as above), and clear the ones
3262 that are clearable.
3272 that are clearable.
3263 '''
3273 '''
3264 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3274 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3265 if not clearable and repo.vfs.exists(f):
3275 if not clearable and repo.vfs.exists(f):
3266 raise util.Abort(msg, hint=hint)
3276 raise util.Abort(msg, hint=hint)
3267 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3277 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3268 if clearable and repo.vfs.exists(f):
3278 if clearable and repo.vfs.exists(f):
3269 util.unlink(repo.join(f))
3279 util.unlink(repo.join(f))
3270
3280
3271 class dirstateguard(object):
3281 class dirstateguard(object):
3272 '''Restore dirstate at unexpected failure.
3282 '''Restore dirstate at unexpected failure.
3273
3283
3274 At the construction, this class does:
3284 At the construction, this class does:
3275
3285
3276 - write current ``repo.dirstate`` out, and
3286 - write current ``repo.dirstate`` out, and
3277 - save ``.hg/dirstate`` into the backup file
3287 - save ``.hg/dirstate`` into the backup file
3278
3288
3279 This restores ``.hg/dirstate`` from backup file, if ``release()``
3289 This restores ``.hg/dirstate`` from backup file, if ``release()``
3280 is invoked before ``close()``.
3290 is invoked before ``close()``.
3281
3291
3282 This just removes the backup file at ``close()`` before ``release()``.
3292 This just removes the backup file at ``close()`` before ``release()``.
3283 '''
3293 '''
3284
3294
3285 def __init__(self, repo, name):
3295 def __init__(self, repo, name):
3286 repo.dirstate.write()
3296 repo.dirstate.write()
3287 self._repo = repo
3297 self._repo = repo
3288 self._filename = 'dirstate.backup.%s.%d' % (name, id(self))
3298 self._filename = 'dirstate.backup.%s.%d' % (name, id(self))
3289 repo.vfs.write(self._filename, repo.vfs.tryread('dirstate'))
3299 repo.vfs.write(self._filename, repo.vfs.tryread('dirstate'))
3290 self._active = True
3300 self._active = True
3291 self._closed = False
3301 self._closed = False
3292
3302
3293 def __del__(self):
3303 def __del__(self):
3294 if self._active: # still active
3304 if self._active: # still active
3295 # this may occur, even if this class is used correctly:
3305 # this may occur, even if this class is used correctly:
3296 # for example, releasing other resources like transaction
3306 # for example, releasing other resources like transaction
3297 # may raise exception before ``dirstateguard.release`` in
3307 # may raise exception before ``dirstateguard.release`` in
3298 # ``release(tr, ....)``.
3308 # ``release(tr, ....)``.
3299 self._abort()
3309 self._abort()
3300
3310
3301 def close(self):
3311 def close(self):
3302 if not self._active: # already inactivated
3312 if not self._active: # already inactivated
3303 msg = (_("can't close already inactivated backup: %s")
3313 msg = (_("can't close already inactivated backup: %s")
3304 % self._filename)
3314 % self._filename)
3305 raise util.Abort(msg)
3315 raise util.Abort(msg)
3306
3316
3307 self._repo.vfs.unlink(self._filename)
3317 self._repo.vfs.unlink(self._filename)
3308 self._active = False
3318 self._active = False
3309 self._closed = True
3319 self._closed = True
3310
3320
3311 def _abort(self):
3321 def _abort(self):
3312 # this "invalidate()" prevents "wlock.release()" from writing
3322 # this "invalidate()" prevents "wlock.release()" from writing
3313 # changes of dirstate out after restoring to original status
3323 # changes of dirstate out after restoring to original status
3314 self._repo.dirstate.invalidate()
3324 self._repo.dirstate.invalidate()
3315
3325
3316 self._repo.vfs.rename(self._filename, 'dirstate')
3326 self._repo.vfs.rename(self._filename, 'dirstate')
3317 self._active = False
3327 self._active = False
3318
3328
3319 def release(self):
3329 def release(self):
3320 if not self._closed:
3330 if not self._closed:
3321 if not self._active: # already inactivated
3331 if not self._active: # already inactivated
3322 msg = (_("can't release already inactivated backup: %s")
3332 msg = (_("can't release already inactivated backup: %s")
3323 % self._filename)
3333 % self._filename)
3324 raise util.Abort(msg)
3334 raise util.Abort(msg)
3325 self._abort()
3335 self._abort()
@@ -1,6479 +1,6482 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 node import hex, bin, nullid, nullrev, short
8 from node import hex, bin, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _
10 from i18n import _
11 import os, re, difflib, time, tempfile, errno, shlex
11 import os, re, difflib, time, tempfile, errno, shlex
12 import sys, socket
12 import sys, socket
13 import hg, scmutil, util, revlog, copies, error, bookmarks
13 import hg, scmutil, util, revlog, copies, error, bookmarks
14 import patch, help, encoding, templatekw, discovery
14 import patch, help, encoding, templatekw, discovery
15 import archival, changegroup, cmdutil, hbisect
15 import archival, changegroup, cmdutil, hbisect
16 import sshserver, hgweb, commandserver
16 import sshserver, hgweb, commandserver
17 import extensions
17 import extensions
18 from hgweb import server as hgweb_server
18 from hgweb import server as hgweb_server
19 import merge as mergemod
19 import merge as mergemod
20 import minirst, revset, fileset
20 import minirst, revset, fileset
21 import dagparser, context, simplemerge, graphmod, copies
21 import dagparser, context, simplemerge, graphmod, copies
22 import random
22 import random
23 import setdiscovery, treediscovery, dagutil, pvec, localrepo
23 import setdiscovery, treediscovery, dagutil, pvec, localrepo
24 import phases, obsolete, exchange, bundle2
24 import phases, obsolete, exchange, bundle2
25 import ui as uimod
25 import ui as uimod
26
26
27 table = {}
27 table = {}
28
28
29 command = cmdutil.command(table)
29 command = cmdutil.command(table)
30
30
31 # Space delimited list of commands that don't require local repositories.
31 # Space delimited list of commands that don't require local repositories.
32 # This should be populated by passing norepo=True into the @command decorator.
32 # This should be populated by passing norepo=True into the @command decorator.
33 norepo = ''
33 norepo = ''
34 # Space delimited list of commands that optionally require local repositories.
34 # Space delimited list of commands that optionally require local repositories.
35 # This should be populated by passing optionalrepo=True into the @command
35 # This should be populated by passing optionalrepo=True into the @command
36 # decorator.
36 # decorator.
37 optionalrepo = ''
37 optionalrepo = ''
38 # Space delimited list of commands that will examine arguments looking for
38 # Space delimited list of commands that will examine arguments looking for
39 # a repository. This should be populated by passing inferrepo=True into the
39 # a repository. This should be populated by passing inferrepo=True into the
40 # @command decorator.
40 # @command decorator.
41 inferrepo = ''
41 inferrepo = ''
42
42
43 # common command options
43 # common command options
44
44
45 globalopts = [
45 globalopts = [
46 ('R', 'repository', '',
46 ('R', 'repository', '',
47 _('repository root directory or name of overlay bundle file'),
47 _('repository root directory or name of overlay bundle file'),
48 _('REPO')),
48 _('REPO')),
49 ('', 'cwd', '',
49 ('', 'cwd', '',
50 _('change working directory'), _('DIR')),
50 _('change working directory'), _('DIR')),
51 ('y', 'noninteractive', None,
51 ('y', 'noninteractive', None,
52 _('do not prompt, automatically pick the first choice for all prompts')),
52 _('do not prompt, automatically pick the first choice for all prompts')),
53 ('q', 'quiet', None, _('suppress output')),
53 ('q', 'quiet', None, _('suppress output')),
54 ('v', 'verbose', None, _('enable additional output')),
54 ('v', 'verbose', None, _('enable additional output')),
55 ('', 'config', [],
55 ('', 'config', [],
56 _('set/override config option (use \'section.name=value\')'),
56 _('set/override config option (use \'section.name=value\')'),
57 _('CONFIG')),
57 _('CONFIG')),
58 ('', 'debug', None, _('enable debugging output')),
58 ('', 'debug', None, _('enable debugging output')),
59 ('', 'debugger', None, _('start debugger')),
59 ('', 'debugger', None, _('start debugger')),
60 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
60 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
61 _('ENCODE')),
61 _('ENCODE')),
62 ('', 'encodingmode', encoding.encodingmode,
62 ('', 'encodingmode', encoding.encodingmode,
63 _('set the charset encoding mode'), _('MODE')),
63 _('set the charset encoding mode'), _('MODE')),
64 ('', 'traceback', None, _('always print a traceback on exception')),
64 ('', 'traceback', None, _('always print a traceback on exception')),
65 ('', 'time', None, _('time how long the command takes')),
65 ('', 'time', None, _('time how long the command takes')),
66 ('', 'profile', None, _('print command execution profile')),
66 ('', 'profile', None, _('print command execution profile')),
67 ('', 'version', None, _('output version information and exit')),
67 ('', 'version', None, _('output version information and exit')),
68 ('h', 'help', None, _('display help and exit')),
68 ('h', 'help', None, _('display help and exit')),
69 ('', 'hidden', False, _('consider hidden changesets')),
69 ('', 'hidden', False, _('consider hidden changesets')),
70 ]
70 ]
71
71
72 dryrunopts = [('n', 'dry-run', None,
72 dryrunopts = [('n', 'dry-run', None,
73 _('do not perform actions, just print output'))]
73 _('do not perform actions, just print output'))]
74
74
75 remoteopts = [
75 remoteopts = [
76 ('e', 'ssh', '',
76 ('e', 'ssh', '',
77 _('specify ssh command to use'), _('CMD')),
77 _('specify ssh command to use'), _('CMD')),
78 ('', 'remotecmd', '',
78 ('', 'remotecmd', '',
79 _('specify hg command to run on the remote side'), _('CMD')),
79 _('specify hg command to run on the remote side'), _('CMD')),
80 ('', 'insecure', None,
80 ('', 'insecure', None,
81 _('do not verify server certificate (ignoring web.cacerts config)')),
81 _('do not verify server certificate (ignoring web.cacerts config)')),
82 ]
82 ]
83
83
84 walkopts = [
84 walkopts = [
85 ('I', 'include', [],
85 ('I', 'include', [],
86 _('include names matching the given patterns'), _('PATTERN')),
86 _('include names matching the given patterns'), _('PATTERN')),
87 ('X', 'exclude', [],
87 ('X', 'exclude', [],
88 _('exclude names matching the given patterns'), _('PATTERN')),
88 _('exclude names matching the given patterns'), _('PATTERN')),
89 ]
89 ]
90
90
91 commitopts = [
91 commitopts = [
92 ('m', 'message', '',
92 ('m', 'message', '',
93 _('use text as commit message'), _('TEXT')),
93 _('use text as commit message'), _('TEXT')),
94 ('l', 'logfile', '',
94 ('l', 'logfile', '',
95 _('read commit message from file'), _('FILE')),
95 _('read commit message from file'), _('FILE')),
96 ]
96 ]
97
97
98 commitopts2 = [
98 commitopts2 = [
99 ('d', 'date', '',
99 ('d', 'date', '',
100 _('record the specified date as commit date'), _('DATE')),
100 _('record the specified date as commit date'), _('DATE')),
101 ('u', 'user', '',
101 ('u', 'user', '',
102 _('record the specified user as committer'), _('USER')),
102 _('record the specified user as committer'), _('USER')),
103 ]
103 ]
104
104
105 # hidden for now
105 # hidden for now
106 formatteropts = [
106 formatteropts = [
107 ('T', 'template', '',
107 ('T', 'template', '',
108 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
108 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
109 ]
109 ]
110
110
111 templateopts = [
111 templateopts = [
112 ('', 'style', '',
112 ('', 'style', '',
113 _('display using template map file (DEPRECATED)'), _('STYLE')),
113 _('display using template map file (DEPRECATED)'), _('STYLE')),
114 ('T', 'template', '',
114 ('T', 'template', '',
115 _('display with template'), _('TEMPLATE')),
115 _('display with template'), _('TEMPLATE')),
116 ]
116 ]
117
117
118 logopts = [
118 logopts = [
119 ('p', 'patch', None, _('show patch')),
119 ('p', 'patch', None, _('show patch')),
120 ('g', 'git', None, _('use git extended diff format')),
120 ('g', 'git', None, _('use git extended diff format')),
121 ('l', 'limit', '',
121 ('l', 'limit', '',
122 _('limit number of changes displayed'), _('NUM')),
122 _('limit number of changes displayed'), _('NUM')),
123 ('M', 'no-merges', None, _('do not show merges')),
123 ('M', 'no-merges', None, _('do not show merges')),
124 ('', 'stat', None, _('output diffstat-style summary of changes')),
124 ('', 'stat', None, _('output diffstat-style summary of changes')),
125 ('G', 'graph', None, _("show the revision DAG")),
125 ('G', 'graph', None, _("show the revision DAG")),
126 ] + templateopts
126 ] + templateopts
127
127
128 diffopts = [
128 diffopts = [
129 ('a', 'text', None, _('treat all files as text')),
129 ('a', 'text', None, _('treat all files as text')),
130 ('g', 'git', None, _('use git extended diff format')),
130 ('g', 'git', None, _('use git extended diff format')),
131 ('', 'nodates', None, _('omit dates from diff headers'))
131 ('', 'nodates', None, _('omit dates from diff headers'))
132 ]
132 ]
133
133
134 diffwsopts = [
134 diffwsopts = [
135 ('w', 'ignore-all-space', None,
135 ('w', 'ignore-all-space', None,
136 _('ignore white space when comparing lines')),
136 _('ignore white space when comparing lines')),
137 ('b', 'ignore-space-change', None,
137 ('b', 'ignore-space-change', None,
138 _('ignore changes in the amount of white space')),
138 _('ignore changes in the amount of white space')),
139 ('B', 'ignore-blank-lines', None,
139 ('B', 'ignore-blank-lines', None,
140 _('ignore changes whose lines are all blank')),
140 _('ignore changes whose lines are all blank')),
141 ]
141 ]
142
142
143 diffopts2 = [
143 diffopts2 = [
144 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
144 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
145 ('p', 'show-function', None, _('show which function each change is in')),
145 ('p', 'show-function', None, _('show which function each change is in')),
146 ('', 'reverse', None, _('produce a diff that undoes the changes')),
146 ('', 'reverse', None, _('produce a diff that undoes the changes')),
147 ] + diffwsopts + [
147 ] + diffwsopts + [
148 ('U', 'unified', '',
148 ('U', 'unified', '',
149 _('number of lines of context to show'), _('NUM')),
149 _('number of lines of context to show'), _('NUM')),
150 ('', 'stat', None, _('output diffstat-style summary of changes')),
150 ('', 'stat', None, _('output diffstat-style summary of changes')),
151 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
151 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
152 ]
152 ]
153
153
154 mergetoolopts = [
154 mergetoolopts = [
155 ('t', 'tool', '', _('specify merge tool')),
155 ('t', 'tool', '', _('specify merge tool')),
156 ]
156 ]
157
157
158 similarityopts = [
158 similarityopts = [
159 ('s', 'similarity', '',
159 ('s', 'similarity', '',
160 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
160 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
161 ]
161 ]
162
162
163 subrepoopts = [
163 subrepoopts = [
164 ('S', 'subrepos', None,
164 ('S', 'subrepos', None,
165 _('recurse into subrepositories'))
165 _('recurse into subrepositories'))
166 ]
166 ]
167
167
168 # Commands start here, listed alphabetically
168 # Commands start here, listed alphabetically
169
169
170 @command('^add',
170 @command('^add',
171 walkopts + subrepoopts + dryrunopts,
171 walkopts + subrepoopts + dryrunopts,
172 _('[OPTION]... [FILE]...'),
172 _('[OPTION]... [FILE]...'),
173 inferrepo=True)
173 inferrepo=True)
174 def add(ui, repo, *pats, **opts):
174 def add(ui, repo, *pats, **opts):
175 """add the specified files on the next commit
175 """add the specified files on the next commit
176
176
177 Schedule files to be version controlled and added to the
177 Schedule files to be version controlled and added to the
178 repository.
178 repository.
179
179
180 The files will be added to the repository at the next commit. To
180 The files will be added to the repository at the next commit. To
181 undo an add before that, see :hg:`forget`.
181 undo an add before that, see :hg:`forget`.
182
182
183 If no names are given, add all files to the repository.
183 If no names are given, add all files to the repository.
184
184
185 .. container:: verbose
185 .. container:: verbose
186
186
187 An example showing how new (unknown) files are added
187 An example showing how new (unknown) files are added
188 automatically by :hg:`add`::
188 automatically by :hg:`add`::
189
189
190 $ ls
190 $ ls
191 foo.c
191 foo.c
192 $ hg status
192 $ hg status
193 ? foo.c
193 ? foo.c
194 $ hg add
194 $ hg add
195 adding foo.c
195 adding foo.c
196 $ hg status
196 $ hg status
197 A foo.c
197 A foo.c
198
198
199 Returns 0 if all files are successfully added.
199 Returns 0 if all files are successfully added.
200 """
200 """
201
201
202 m = scmutil.match(repo[None], pats, opts)
202 m = scmutil.match(repo[None], pats, opts)
203 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
203 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
204 return rejected and 1 or 0
204 return rejected and 1 or 0
205
205
206 @command('addremove',
206 @command('addremove',
207 similarityopts + subrepoopts + walkopts + dryrunopts,
207 similarityopts + subrepoopts + walkopts + dryrunopts,
208 _('[OPTION]... [FILE]...'),
208 _('[OPTION]... [FILE]...'),
209 inferrepo=True)
209 inferrepo=True)
210 def addremove(ui, repo, *pats, **opts):
210 def addremove(ui, repo, *pats, **opts):
211 """add all new files, delete all missing files
211 """add all new files, delete all missing files
212
212
213 Add all new files and remove all missing files from the
213 Add all new files and remove all missing files from the
214 repository.
214 repository.
215
215
216 New files are ignored if they match any of the patterns in
216 New files are ignored if they match any of the patterns in
217 ``.hgignore``. As with add, these changes take effect at the next
217 ``.hgignore``. As with add, these changes take effect at the next
218 commit.
218 commit.
219
219
220 Use the -s/--similarity option to detect renamed files. This
220 Use the -s/--similarity option to detect renamed files. This
221 option takes a percentage between 0 (disabled) and 100 (files must
221 option takes a percentage between 0 (disabled) and 100 (files must
222 be identical) as its parameter. With a parameter greater than 0,
222 be identical) as its parameter. With a parameter greater than 0,
223 this compares every removed file with every added file and records
223 this compares every removed file with every added file and records
224 those similar enough as renames. Detecting renamed files this way
224 those similar enough as renames. Detecting renamed files this way
225 can be expensive. After using this option, :hg:`status -C` can be
225 can be expensive. After using this option, :hg:`status -C` can be
226 used to check which files were identified as moved or renamed. If
226 used to check which files were identified as moved or renamed. If
227 not specified, -s/--similarity defaults to 100 and only renames of
227 not specified, -s/--similarity defaults to 100 and only renames of
228 identical files are detected.
228 identical files are detected.
229
229
230 Returns 0 if all files are successfully added.
230 Returns 0 if all files are successfully added.
231 """
231 """
232 try:
232 try:
233 sim = float(opts.get('similarity') or 100)
233 sim = float(opts.get('similarity') or 100)
234 except ValueError:
234 except ValueError:
235 raise util.Abort(_('similarity must be a number'))
235 raise util.Abort(_('similarity must be a number'))
236 if sim < 0 or sim > 100:
236 if sim < 0 or sim > 100:
237 raise util.Abort(_('similarity must be between 0 and 100'))
237 raise util.Abort(_('similarity must be between 0 and 100'))
238 matcher = scmutil.match(repo[None], pats, opts)
238 matcher = scmutil.match(repo[None], pats, opts)
239 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
239 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
240
240
241 @command('^annotate|blame',
241 @command('^annotate|blame',
242 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
242 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
243 ('', 'follow', None,
243 ('', 'follow', None,
244 _('follow copies/renames and list the filename (DEPRECATED)')),
244 _('follow copies/renames and list the filename (DEPRECATED)')),
245 ('', 'no-follow', None, _("don't follow copies and renames")),
245 ('', 'no-follow', None, _("don't follow copies and renames")),
246 ('a', 'text', None, _('treat all files as text')),
246 ('a', 'text', None, _('treat all files as text')),
247 ('u', 'user', None, _('list the author (long with -v)')),
247 ('u', 'user', None, _('list the author (long with -v)')),
248 ('f', 'file', None, _('list the filename')),
248 ('f', 'file', None, _('list the filename')),
249 ('d', 'date', None, _('list the date (short with -q)')),
249 ('d', 'date', None, _('list the date (short with -q)')),
250 ('n', 'number', None, _('list the revision number (default)')),
250 ('n', 'number', None, _('list the revision number (default)')),
251 ('c', 'changeset', None, _('list the changeset')),
251 ('c', 'changeset', None, _('list the changeset')),
252 ('l', 'line-number', None, _('show line number at the first appearance'))
252 ('l', 'line-number', None, _('show line number at the first appearance'))
253 ] + diffwsopts + walkopts + formatteropts,
253 ] + diffwsopts + walkopts + formatteropts,
254 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
254 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
255 inferrepo=True)
255 inferrepo=True)
256 def annotate(ui, repo, *pats, **opts):
256 def annotate(ui, repo, *pats, **opts):
257 """show changeset information by line for each file
257 """show changeset information by line for each file
258
258
259 List changes in files, showing the revision id responsible for
259 List changes in files, showing the revision id responsible for
260 each line
260 each line
261
261
262 This command is useful for discovering when a change was made and
262 This command is useful for discovering when a change was made and
263 by whom.
263 by whom.
264
264
265 Without the -a/--text option, annotate will avoid processing files
265 Without the -a/--text option, annotate will avoid processing files
266 it detects as binary. With -a, annotate will annotate the file
266 it detects as binary. With -a, annotate will annotate the file
267 anyway, although the results will probably be neither useful
267 anyway, although the results will probably be neither useful
268 nor desirable.
268 nor desirable.
269
269
270 Returns 0 on success.
270 Returns 0 on success.
271 """
271 """
272 if not pats:
272 if not pats:
273 raise util.Abort(_('at least one filename or pattern is required'))
273 raise util.Abort(_('at least one filename or pattern is required'))
274
274
275 if opts.get('follow'):
275 if opts.get('follow'):
276 # --follow is deprecated and now just an alias for -f/--file
276 # --follow is deprecated and now just an alias for -f/--file
277 # to mimic the behavior of Mercurial before version 1.5
277 # to mimic the behavior of Mercurial before version 1.5
278 opts['file'] = True
278 opts['file'] = True
279
279
280 ctx = scmutil.revsingle(repo, opts.get('rev'))
280 ctx = scmutil.revsingle(repo, opts.get('rev'))
281
281
282 fm = ui.formatter('annotate', opts)
282 fm = ui.formatter('annotate', opts)
283 if ui.quiet:
283 if ui.quiet:
284 datefunc = util.shortdate
284 datefunc = util.shortdate
285 else:
285 else:
286 datefunc = util.datestr
286 datefunc = util.datestr
287 if ctx.rev() is None:
287 if ctx.rev() is None:
288 def hexfn(node):
288 def hexfn(node):
289 if node is None:
289 if node is None:
290 return None
290 return None
291 else:
291 else:
292 return fm.hexfunc(node)
292 return fm.hexfunc(node)
293 if opts.get('changeset'):
293 if opts.get('changeset'):
294 # omit "+" suffix which is appended to node hex
294 # omit "+" suffix which is appended to node hex
295 def formatrev(rev):
295 def formatrev(rev):
296 if rev is None:
296 if rev is None:
297 return '%d' % ctx.p1().rev()
297 return '%d' % ctx.p1().rev()
298 else:
298 else:
299 return '%d' % rev
299 return '%d' % rev
300 else:
300 else:
301 def formatrev(rev):
301 def formatrev(rev):
302 if rev is None:
302 if rev is None:
303 return '%d+' % ctx.p1().rev()
303 return '%d+' % ctx.p1().rev()
304 else:
304 else:
305 return '%d ' % rev
305 return '%d ' % rev
306 def formathex(hex):
306 def formathex(hex):
307 if hex is None:
307 if hex is None:
308 return '%s+' % fm.hexfunc(ctx.p1().node())
308 return '%s+' % fm.hexfunc(ctx.p1().node())
309 else:
309 else:
310 return '%s ' % hex
310 return '%s ' % hex
311 else:
311 else:
312 hexfn = fm.hexfunc
312 hexfn = fm.hexfunc
313 formatrev = formathex = str
313 formatrev = formathex = str
314
314
315 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
315 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
316 ('number', ' ', lambda x: x[0].rev(), formatrev),
316 ('number', ' ', lambda x: x[0].rev(), formatrev),
317 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
317 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
318 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
318 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
319 ('file', ' ', lambda x: x[0].path(), str),
319 ('file', ' ', lambda x: x[0].path(), str),
320 ('line_number', ':', lambda x: x[1], str),
320 ('line_number', ':', lambda x: x[1], str),
321 ]
321 ]
322 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
322 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
323
323
324 if (not opts.get('user') and not opts.get('changeset')
324 if (not opts.get('user') and not opts.get('changeset')
325 and not opts.get('date') and not opts.get('file')):
325 and not opts.get('date') and not opts.get('file')):
326 opts['number'] = True
326 opts['number'] = True
327
327
328 linenumber = opts.get('line_number') is not None
328 linenumber = opts.get('line_number') is not None
329 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
329 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
330 raise util.Abort(_('at least one of -n/-c is required for -l'))
330 raise util.Abort(_('at least one of -n/-c is required for -l'))
331
331
332 if fm:
332 if fm:
333 def makefunc(get, fmt):
333 def makefunc(get, fmt):
334 return get
334 return get
335 else:
335 else:
336 def makefunc(get, fmt):
336 def makefunc(get, fmt):
337 return lambda x: fmt(get(x))
337 return lambda x: fmt(get(x))
338 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
338 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
339 if opts.get(op)]
339 if opts.get(op)]
340 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
340 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
341 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
341 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
342 if opts.get(op))
342 if opts.get(op))
343
343
344 def bad(x, y):
344 def bad(x, y):
345 raise util.Abort("%s: %s" % (x, y))
345 raise util.Abort("%s: %s" % (x, y))
346
346
347 m = scmutil.match(ctx, pats, opts)
347 m = scmutil.match(ctx, pats, opts)
348 m.bad = bad
348 m.bad = bad
349 follow = not opts.get('no_follow')
349 follow = not opts.get('no_follow')
350 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
350 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
351 whitespace=True)
351 whitespace=True)
352 for abs in ctx.walk(m):
352 for abs in ctx.walk(m):
353 fctx = ctx[abs]
353 fctx = ctx[abs]
354 if not opts.get('text') and util.binary(fctx.data()):
354 if not opts.get('text') and util.binary(fctx.data()):
355 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
355 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
356 continue
356 continue
357
357
358 lines = fctx.annotate(follow=follow, linenumber=linenumber,
358 lines = fctx.annotate(follow=follow, linenumber=linenumber,
359 diffopts=diffopts)
359 diffopts=diffopts)
360 formats = []
360 formats = []
361 pieces = []
361 pieces = []
362
362
363 for f, sep in funcmap:
363 for f, sep in funcmap:
364 l = [f(n) for n, dummy in lines]
364 l = [f(n) for n, dummy in lines]
365 if l:
365 if l:
366 if fm:
366 if fm:
367 formats.append(['%s' for x in l])
367 formats.append(['%s' for x in l])
368 else:
368 else:
369 sizes = [encoding.colwidth(x) for x in l]
369 sizes = [encoding.colwidth(x) for x in l]
370 ml = max(sizes)
370 ml = max(sizes)
371 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
371 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
372 pieces.append(l)
372 pieces.append(l)
373
373
374 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
374 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
375 fm.startitem()
375 fm.startitem()
376 fm.write(fields, "".join(f), *p)
376 fm.write(fields, "".join(f), *p)
377 fm.write('line', ": %s", l[1])
377 fm.write('line', ": %s", l[1])
378
378
379 if lines and not lines[-1][1].endswith('\n'):
379 if lines and not lines[-1][1].endswith('\n'):
380 fm.plain('\n')
380 fm.plain('\n')
381
381
382 fm.end()
382 fm.end()
383
383
384 @command('archive',
384 @command('archive',
385 [('', 'no-decode', None, _('do not pass files through decoders')),
385 [('', 'no-decode', None, _('do not pass files through decoders')),
386 ('p', 'prefix', '', _('directory prefix for files in archive'),
386 ('p', 'prefix', '', _('directory prefix for files in archive'),
387 _('PREFIX')),
387 _('PREFIX')),
388 ('r', 'rev', '', _('revision to distribute'), _('REV')),
388 ('r', 'rev', '', _('revision to distribute'), _('REV')),
389 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
389 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
390 ] + subrepoopts + walkopts,
390 ] + subrepoopts + walkopts,
391 _('[OPTION]... DEST'))
391 _('[OPTION]... DEST'))
392 def archive(ui, repo, dest, **opts):
392 def archive(ui, repo, dest, **opts):
393 '''create an unversioned archive of a repository revision
393 '''create an unversioned archive of a repository revision
394
394
395 By default, the revision used is the parent of the working
395 By default, the revision used is the parent of the working
396 directory; use -r/--rev to specify a different revision.
396 directory; use -r/--rev to specify a different revision.
397
397
398 The archive type is automatically detected based on file
398 The archive type is automatically detected based on file
399 extension (or override using -t/--type).
399 extension (or override using -t/--type).
400
400
401 .. container:: verbose
401 .. container:: verbose
402
402
403 Examples:
403 Examples:
404
404
405 - create a zip file containing the 1.0 release::
405 - create a zip file containing the 1.0 release::
406
406
407 hg archive -r 1.0 project-1.0.zip
407 hg archive -r 1.0 project-1.0.zip
408
408
409 - create a tarball excluding .hg files::
409 - create a tarball excluding .hg files::
410
410
411 hg archive project.tar.gz -X ".hg*"
411 hg archive project.tar.gz -X ".hg*"
412
412
413 Valid types are:
413 Valid types are:
414
414
415 :``files``: a directory full of files (default)
415 :``files``: a directory full of files (default)
416 :``tar``: tar archive, uncompressed
416 :``tar``: tar archive, uncompressed
417 :``tbz2``: tar archive, compressed using bzip2
417 :``tbz2``: tar archive, compressed using bzip2
418 :``tgz``: tar archive, compressed using gzip
418 :``tgz``: tar archive, compressed using gzip
419 :``uzip``: zip archive, uncompressed
419 :``uzip``: zip archive, uncompressed
420 :``zip``: zip archive, compressed using deflate
420 :``zip``: zip archive, compressed using deflate
421
421
422 The exact name of the destination archive or directory is given
422 The exact name of the destination archive or directory is given
423 using a format string; see :hg:`help export` for details.
423 using a format string; see :hg:`help export` for details.
424
424
425 Each member added to an archive file has a directory prefix
425 Each member added to an archive file has a directory prefix
426 prepended. Use -p/--prefix to specify a format string for the
426 prepended. Use -p/--prefix to specify a format string for the
427 prefix. The default is the basename of the archive, with suffixes
427 prefix. The default is the basename of the archive, with suffixes
428 removed.
428 removed.
429
429
430 Returns 0 on success.
430 Returns 0 on success.
431 '''
431 '''
432
432
433 ctx = scmutil.revsingle(repo, opts.get('rev'))
433 ctx = scmutil.revsingle(repo, opts.get('rev'))
434 if not ctx:
434 if not ctx:
435 raise util.Abort(_('no working directory: please specify a revision'))
435 raise util.Abort(_('no working directory: please specify a revision'))
436 node = ctx.node()
436 node = ctx.node()
437 dest = cmdutil.makefilename(repo, dest, node)
437 dest = cmdutil.makefilename(repo, dest, node)
438 if os.path.realpath(dest) == repo.root:
438 if os.path.realpath(dest) == repo.root:
439 raise util.Abort(_('repository root cannot be destination'))
439 raise util.Abort(_('repository root cannot be destination'))
440
440
441 kind = opts.get('type') or archival.guesskind(dest) or 'files'
441 kind = opts.get('type') or archival.guesskind(dest) or 'files'
442 prefix = opts.get('prefix')
442 prefix = opts.get('prefix')
443
443
444 if dest == '-':
444 if dest == '-':
445 if kind == 'files':
445 if kind == 'files':
446 raise util.Abort(_('cannot archive plain files to stdout'))
446 raise util.Abort(_('cannot archive plain files to stdout'))
447 dest = cmdutil.makefileobj(repo, dest)
447 dest = cmdutil.makefileobj(repo, dest)
448 if not prefix:
448 if not prefix:
449 prefix = os.path.basename(repo.root) + '-%h'
449 prefix = os.path.basename(repo.root) + '-%h'
450
450
451 prefix = cmdutil.makefilename(repo, prefix, node)
451 prefix = cmdutil.makefilename(repo, prefix, node)
452 matchfn = scmutil.match(ctx, [], opts)
452 matchfn = scmutil.match(ctx, [], opts)
453 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
453 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
454 matchfn, prefix, subrepos=opts.get('subrepos'))
454 matchfn, prefix, subrepos=opts.get('subrepos'))
455
455
456 @command('backout',
456 @command('backout',
457 [('', 'merge', None, _('merge with old dirstate parent after backout')),
457 [('', 'merge', None, _('merge with old dirstate parent after backout')),
458 ('', 'commit', None, _('commit if no conflicts were encountered')),
458 ('', 'commit', None, _('commit if no conflicts were encountered')),
459 ('', 'parent', '',
459 ('', 'parent', '',
460 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
460 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
461 ('r', 'rev', '', _('revision to backout'), _('REV')),
461 ('r', 'rev', '', _('revision to backout'), _('REV')),
462 ('e', 'edit', False, _('invoke editor on commit messages')),
462 ('e', 'edit', False, _('invoke editor on commit messages')),
463 ] + mergetoolopts + walkopts + commitopts + commitopts2,
463 ] + mergetoolopts + walkopts + commitopts + commitopts2,
464 _('[OPTION]... [-r] REV'))
464 _('[OPTION]... [-r] REV'))
465 def backout(ui, repo, node=None, rev=None, commit=False, **opts):
465 def backout(ui, repo, node=None, rev=None, commit=False, **opts):
466 '''reverse effect of earlier changeset
466 '''reverse effect of earlier changeset
467
467
468 Prepare a new changeset with the effect of REV undone in the
468 Prepare a new changeset with the effect of REV undone in the
469 current working directory.
469 current working directory.
470
470
471 If REV is the parent of the working directory, then this new changeset
471 If REV is the parent of the working directory, then this new changeset
472 is committed automatically. Otherwise, hg needs to merge the
472 is committed automatically. Otherwise, hg needs to merge the
473 changes and the merged result is left uncommitted.
473 changes and the merged result is left uncommitted.
474
474
475 .. note::
475 .. note::
476
476
477 backout cannot be used to fix either an unwanted or
477 backout cannot be used to fix either an unwanted or
478 incorrect merge.
478 incorrect merge.
479
479
480 .. container:: verbose
480 .. container:: verbose
481
481
482 By default, the pending changeset will have one parent,
482 By default, the pending changeset will have one parent,
483 maintaining a linear history. With --merge, the pending
483 maintaining a linear history. With --merge, the pending
484 changeset will instead have two parents: the old parent of the
484 changeset will instead have two parents: the old parent of the
485 working directory and a new child of REV that simply undoes REV.
485 working directory and a new child of REV that simply undoes REV.
486
486
487 Before version 1.7, the behavior without --merge was equivalent
487 Before version 1.7, the behavior without --merge was equivalent
488 to specifying --merge followed by :hg:`update --clean .` to
488 to specifying --merge followed by :hg:`update --clean .` to
489 cancel the merge and leave the child of REV as a head to be
489 cancel the merge and leave the child of REV as a head to be
490 merged separately.
490 merged separately.
491
491
492 See :hg:`help dates` for a list of formats valid for -d/--date.
492 See :hg:`help dates` for a list of formats valid for -d/--date.
493
493
494 Returns 0 on success, 1 if nothing to backout or there are unresolved
494 Returns 0 on success, 1 if nothing to backout or there are unresolved
495 files.
495 files.
496 '''
496 '''
497 if rev and node:
497 if rev and node:
498 raise util.Abort(_("please specify just one revision"))
498 raise util.Abort(_("please specify just one revision"))
499
499
500 if not rev:
500 if not rev:
501 rev = node
501 rev = node
502
502
503 if not rev:
503 if not rev:
504 raise util.Abort(_("please specify a revision to backout"))
504 raise util.Abort(_("please specify a revision to backout"))
505
505
506 date = opts.get('date')
506 date = opts.get('date')
507 if date:
507 if date:
508 opts['date'] = util.parsedate(date)
508 opts['date'] = util.parsedate(date)
509
509
510 cmdutil.checkunfinished(repo)
510 cmdutil.checkunfinished(repo)
511 cmdutil.bailifchanged(repo)
511 cmdutil.bailifchanged(repo)
512 node = scmutil.revsingle(repo, rev).node()
512 node = scmutil.revsingle(repo, rev).node()
513
513
514 op1, op2 = repo.dirstate.parents()
514 op1, op2 = repo.dirstate.parents()
515 if not repo.changelog.isancestor(node, op1):
515 if not repo.changelog.isancestor(node, op1):
516 raise util.Abort(_('cannot backout change that is not an ancestor'))
516 raise util.Abort(_('cannot backout change that is not an ancestor'))
517
517
518 p1, p2 = repo.changelog.parents(node)
518 p1, p2 = repo.changelog.parents(node)
519 if p1 == nullid:
519 if p1 == nullid:
520 raise util.Abort(_('cannot backout a change with no parents'))
520 raise util.Abort(_('cannot backout a change with no parents'))
521 if p2 != nullid:
521 if p2 != nullid:
522 if not opts.get('parent'):
522 if not opts.get('parent'):
523 raise util.Abort(_('cannot backout a merge changeset'))
523 raise util.Abort(_('cannot backout a merge changeset'))
524 p = repo.lookup(opts['parent'])
524 p = repo.lookup(opts['parent'])
525 if p not in (p1, p2):
525 if p not in (p1, p2):
526 raise util.Abort(_('%s is not a parent of %s') %
526 raise util.Abort(_('%s is not a parent of %s') %
527 (short(p), short(node)))
527 (short(p), short(node)))
528 parent = p
528 parent = p
529 else:
529 else:
530 if opts.get('parent'):
530 if opts.get('parent'):
531 raise util.Abort(_('cannot use --parent on non-merge changeset'))
531 raise util.Abort(_('cannot use --parent on non-merge changeset'))
532 parent = p1
532 parent = p1
533
533
534 # the backout should appear on the same branch
534 # the backout should appear on the same branch
535 wlock = repo.wlock()
535 wlock = repo.wlock()
536 try:
536 try:
537 branch = repo.dirstate.branch()
537 branch = repo.dirstate.branch()
538 bheads = repo.branchheads(branch)
538 bheads = repo.branchheads(branch)
539 rctx = scmutil.revsingle(repo, hex(parent))
539 rctx = scmutil.revsingle(repo, hex(parent))
540 if not opts.get('merge') and op1 != node:
540 if not opts.get('merge') and op1 != node:
541 try:
541 try:
542 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
542 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
543 'backout')
543 'backout')
544 repo.dirstate.beginparentchange()
544 repo.dirstate.beginparentchange()
545 stats = mergemod.update(repo, parent, True, True, False,
545 stats = mergemod.update(repo, parent, True, True, False,
546 node, False)
546 node, False)
547 repo.setparents(op1, op2)
547 repo.setparents(op1, op2)
548 repo.dirstate.endparentchange()
548 repo.dirstate.endparentchange()
549 hg._showstats(repo, stats)
549 hg._showstats(repo, stats)
550 if stats[3]:
550 if stats[3]:
551 repo.ui.status(_("use 'hg resolve' to retry unresolved "
551 repo.ui.status(_("use 'hg resolve' to retry unresolved "
552 "file merges\n"))
552 "file merges\n"))
553 return 1
553 return 1
554 elif not commit:
554 elif not commit:
555 msg = _("changeset %s backed out, "
555 msg = _("changeset %s backed out, "
556 "don't forget to commit.\n")
556 "don't forget to commit.\n")
557 ui.status(msg % short(node))
557 ui.status(msg % short(node))
558 return 0
558 return 0
559 finally:
559 finally:
560 ui.setconfig('ui', 'forcemerge', '', '')
560 ui.setconfig('ui', 'forcemerge', '', '')
561 else:
561 else:
562 hg.clean(repo, node, show_stats=False)
562 hg.clean(repo, node, show_stats=False)
563 repo.dirstate.setbranch(branch)
563 repo.dirstate.setbranch(branch)
564 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
564 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
565
565
566
566
567 def commitfunc(ui, repo, message, match, opts):
567 def commitfunc(ui, repo, message, match, opts):
568 editform = 'backout'
568 editform = 'backout'
569 e = cmdutil.getcommiteditor(editform=editform, **opts)
569 e = cmdutil.getcommiteditor(editform=editform, **opts)
570 if not message:
570 if not message:
571 # we don't translate commit messages
571 # we don't translate commit messages
572 message = "Backed out changeset %s" % short(node)
572 message = "Backed out changeset %s" % short(node)
573 e = cmdutil.getcommiteditor(edit=True, editform=editform)
573 e = cmdutil.getcommiteditor(edit=True, editform=editform)
574 return repo.commit(message, opts.get('user'), opts.get('date'),
574 return repo.commit(message, opts.get('user'), opts.get('date'),
575 match, editor=e)
575 match, editor=e)
576 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
576 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
577 if not newnode:
577 if not newnode:
578 ui.status(_("nothing changed\n"))
578 ui.status(_("nothing changed\n"))
579 return 1
579 return 1
580 cmdutil.commitstatus(repo, newnode, branch, bheads)
580 cmdutil.commitstatus(repo, newnode, branch, bheads)
581
581
582 def nice(node):
582 def nice(node):
583 return '%d:%s' % (repo.changelog.rev(node), short(node))
583 return '%d:%s' % (repo.changelog.rev(node), short(node))
584 ui.status(_('changeset %s backs out changeset %s\n') %
584 ui.status(_('changeset %s backs out changeset %s\n') %
585 (nice(repo.changelog.tip()), nice(node)))
585 (nice(repo.changelog.tip()), nice(node)))
586 if opts.get('merge') and op1 != node:
586 if opts.get('merge') and op1 != node:
587 hg.clean(repo, op1, show_stats=False)
587 hg.clean(repo, op1, show_stats=False)
588 ui.status(_('merging with changeset %s\n')
588 ui.status(_('merging with changeset %s\n')
589 % nice(repo.changelog.tip()))
589 % nice(repo.changelog.tip()))
590 try:
590 try:
591 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
591 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
592 'backout')
592 'backout')
593 return hg.merge(repo, hex(repo.changelog.tip()))
593 return hg.merge(repo, hex(repo.changelog.tip()))
594 finally:
594 finally:
595 ui.setconfig('ui', 'forcemerge', '', '')
595 ui.setconfig('ui', 'forcemerge', '', '')
596 finally:
596 finally:
597 wlock.release()
597 wlock.release()
598 return 0
598 return 0
599
599
600 @command('bisect',
600 @command('bisect',
601 [('r', 'reset', False, _('reset bisect state')),
601 [('r', 'reset', False, _('reset bisect state')),
602 ('g', 'good', False, _('mark changeset good')),
602 ('g', 'good', False, _('mark changeset good')),
603 ('b', 'bad', False, _('mark changeset bad')),
603 ('b', 'bad', False, _('mark changeset bad')),
604 ('s', 'skip', False, _('skip testing changeset')),
604 ('s', 'skip', False, _('skip testing changeset')),
605 ('e', 'extend', False, _('extend the bisect range')),
605 ('e', 'extend', False, _('extend the bisect range')),
606 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
606 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
607 ('U', 'noupdate', False, _('do not update to target'))],
607 ('U', 'noupdate', False, _('do not update to target'))],
608 _("[-gbsr] [-U] [-c CMD] [REV]"))
608 _("[-gbsr] [-U] [-c CMD] [REV]"))
609 def bisect(ui, repo, rev=None, extra=None, command=None,
609 def bisect(ui, repo, rev=None, extra=None, command=None,
610 reset=None, good=None, bad=None, skip=None, extend=None,
610 reset=None, good=None, bad=None, skip=None, extend=None,
611 noupdate=None):
611 noupdate=None):
612 """subdivision search of changesets
612 """subdivision search of changesets
613
613
614 This command helps to find changesets which introduce problems. To
614 This command helps to find changesets which introduce problems. To
615 use, mark the earliest changeset you know exhibits the problem as
615 use, mark the earliest changeset you know exhibits the problem as
616 bad, then mark the latest changeset which is free from the problem
616 bad, then mark the latest changeset which is free from the problem
617 as good. Bisect will update your working directory to a revision
617 as good. Bisect will update your working directory to a revision
618 for testing (unless the -U/--noupdate option is specified). Once
618 for testing (unless the -U/--noupdate option is specified). Once
619 you have performed tests, mark the working directory as good or
619 you have performed tests, mark the working directory as good or
620 bad, and bisect will either update to another candidate changeset
620 bad, and bisect will either update to another candidate changeset
621 or announce that it has found the bad revision.
621 or announce that it has found the bad revision.
622
622
623 As a shortcut, you can also use the revision argument to mark a
623 As a shortcut, you can also use the revision argument to mark a
624 revision as good or bad without checking it out first.
624 revision as good or bad without checking it out first.
625
625
626 If you supply a command, it will be used for automatic bisection.
626 If you supply a command, it will be used for automatic bisection.
627 The environment variable HG_NODE will contain the ID of the
627 The environment variable HG_NODE will contain the ID of the
628 changeset being tested. The exit status of the command will be
628 changeset being tested. The exit status of the command will be
629 used to mark revisions as good or bad: status 0 means good, 125
629 used to mark revisions as good or bad: status 0 means good, 125
630 means to skip the revision, 127 (command not found) will abort the
630 means to skip the revision, 127 (command not found) will abort the
631 bisection, and any other non-zero exit status means the revision
631 bisection, and any other non-zero exit status means the revision
632 is bad.
632 is bad.
633
633
634 .. container:: verbose
634 .. container:: verbose
635
635
636 Some examples:
636 Some examples:
637
637
638 - start a bisection with known bad revision 34, and good revision 12::
638 - start a bisection with known bad revision 34, and good revision 12::
639
639
640 hg bisect --bad 34
640 hg bisect --bad 34
641 hg bisect --good 12
641 hg bisect --good 12
642
642
643 - advance the current bisection by marking current revision as good or
643 - advance the current bisection by marking current revision as good or
644 bad::
644 bad::
645
645
646 hg bisect --good
646 hg bisect --good
647 hg bisect --bad
647 hg bisect --bad
648
648
649 - mark the current revision, or a known revision, to be skipped (e.g. if
649 - mark the current revision, or a known revision, to be skipped (e.g. if
650 that revision is not usable because of another issue)::
650 that revision is not usable because of another issue)::
651
651
652 hg bisect --skip
652 hg bisect --skip
653 hg bisect --skip 23
653 hg bisect --skip 23
654
654
655 - skip all revisions that do not touch directories ``foo`` or ``bar``::
655 - skip all revisions that do not touch directories ``foo`` or ``bar``::
656
656
657 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
657 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
658
658
659 - forget the current bisection::
659 - forget the current bisection::
660
660
661 hg bisect --reset
661 hg bisect --reset
662
662
663 - use 'make && make tests' to automatically find the first broken
663 - use 'make && make tests' to automatically find the first broken
664 revision::
664 revision::
665
665
666 hg bisect --reset
666 hg bisect --reset
667 hg bisect --bad 34
667 hg bisect --bad 34
668 hg bisect --good 12
668 hg bisect --good 12
669 hg bisect --command "make && make tests"
669 hg bisect --command "make && make tests"
670
670
671 - see all changesets whose states are already known in the current
671 - see all changesets whose states are already known in the current
672 bisection::
672 bisection::
673
673
674 hg log -r "bisect(pruned)"
674 hg log -r "bisect(pruned)"
675
675
676 - see the changeset currently being bisected (especially useful
676 - see the changeset currently being bisected (especially useful
677 if running with -U/--noupdate)::
677 if running with -U/--noupdate)::
678
678
679 hg log -r "bisect(current)"
679 hg log -r "bisect(current)"
680
680
681 - see all changesets that took part in the current bisection::
681 - see all changesets that took part in the current bisection::
682
682
683 hg log -r "bisect(range)"
683 hg log -r "bisect(range)"
684
684
685 - you can even get a nice graph::
685 - you can even get a nice graph::
686
686
687 hg log --graph -r "bisect(range)"
687 hg log --graph -r "bisect(range)"
688
688
689 See :hg:`help revsets` for more about the `bisect()` keyword.
689 See :hg:`help revsets` for more about the `bisect()` keyword.
690
690
691 Returns 0 on success.
691 Returns 0 on success.
692 """
692 """
693 def extendbisectrange(nodes, good):
693 def extendbisectrange(nodes, good):
694 # bisect is incomplete when it ends on a merge node and
694 # bisect is incomplete when it ends on a merge node and
695 # one of the parent was not checked.
695 # one of the parent was not checked.
696 parents = repo[nodes[0]].parents()
696 parents = repo[nodes[0]].parents()
697 if len(parents) > 1:
697 if len(parents) > 1:
698 if good:
698 if good:
699 side = state['bad']
699 side = state['bad']
700 else:
700 else:
701 side = state['good']
701 side = state['good']
702 num = len(set(i.node() for i in parents) & set(side))
702 num = len(set(i.node() for i in parents) & set(side))
703 if num == 1:
703 if num == 1:
704 return parents[0].ancestor(parents[1])
704 return parents[0].ancestor(parents[1])
705 return None
705 return None
706
706
707 def print_result(nodes, good):
707 def print_result(nodes, good):
708 displayer = cmdutil.show_changeset(ui, repo, {})
708 displayer = cmdutil.show_changeset(ui, repo, {})
709 if len(nodes) == 1:
709 if len(nodes) == 1:
710 # narrowed it down to a single revision
710 # narrowed it down to a single revision
711 if good:
711 if good:
712 ui.write(_("The first good revision is:\n"))
712 ui.write(_("The first good revision is:\n"))
713 else:
713 else:
714 ui.write(_("The first bad revision is:\n"))
714 ui.write(_("The first bad revision is:\n"))
715 displayer.show(repo[nodes[0]])
715 displayer.show(repo[nodes[0]])
716 extendnode = extendbisectrange(nodes, good)
716 extendnode = extendbisectrange(nodes, good)
717 if extendnode is not None:
717 if extendnode is not None:
718 ui.write(_('Not all ancestors of this changeset have been'
718 ui.write(_('Not all ancestors of this changeset have been'
719 ' checked.\nUse bisect --extend to continue the '
719 ' checked.\nUse bisect --extend to continue the '
720 'bisection from\nthe common ancestor, %s.\n')
720 'bisection from\nthe common ancestor, %s.\n')
721 % extendnode)
721 % extendnode)
722 else:
722 else:
723 # multiple possible revisions
723 # multiple possible revisions
724 if good:
724 if good:
725 ui.write(_("Due to skipped revisions, the first "
725 ui.write(_("Due to skipped revisions, the first "
726 "good revision could be any of:\n"))
726 "good revision could be any of:\n"))
727 else:
727 else:
728 ui.write(_("Due to skipped revisions, the first "
728 ui.write(_("Due to skipped revisions, the first "
729 "bad revision could be any of:\n"))
729 "bad revision could be any of:\n"))
730 for n in nodes:
730 for n in nodes:
731 displayer.show(repo[n])
731 displayer.show(repo[n])
732 displayer.close()
732 displayer.close()
733
733
734 def check_state(state, interactive=True):
734 def check_state(state, interactive=True):
735 if not state['good'] or not state['bad']:
735 if not state['good'] or not state['bad']:
736 if (good or bad or skip or reset) and interactive:
736 if (good or bad or skip or reset) and interactive:
737 return
737 return
738 if not state['good']:
738 if not state['good']:
739 raise util.Abort(_('cannot bisect (no known good revisions)'))
739 raise util.Abort(_('cannot bisect (no known good revisions)'))
740 else:
740 else:
741 raise util.Abort(_('cannot bisect (no known bad revisions)'))
741 raise util.Abort(_('cannot bisect (no known bad revisions)'))
742 return True
742 return True
743
743
744 # backward compatibility
744 # backward compatibility
745 if rev in "good bad reset init".split():
745 if rev in "good bad reset init".split():
746 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
746 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
747 cmd, rev, extra = rev, extra, None
747 cmd, rev, extra = rev, extra, None
748 if cmd == "good":
748 if cmd == "good":
749 good = True
749 good = True
750 elif cmd == "bad":
750 elif cmd == "bad":
751 bad = True
751 bad = True
752 else:
752 else:
753 reset = True
753 reset = True
754 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
754 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
755 raise util.Abort(_('incompatible arguments'))
755 raise util.Abort(_('incompatible arguments'))
756
756
757 cmdutil.checkunfinished(repo)
757 cmdutil.checkunfinished(repo)
758
758
759 if reset:
759 if reset:
760 p = repo.join("bisect.state")
760 p = repo.join("bisect.state")
761 if os.path.exists(p):
761 if os.path.exists(p):
762 os.unlink(p)
762 os.unlink(p)
763 return
763 return
764
764
765 state = hbisect.load_state(repo)
765 state = hbisect.load_state(repo)
766
766
767 if command:
767 if command:
768 changesets = 1
768 changesets = 1
769 if noupdate:
769 if noupdate:
770 try:
770 try:
771 node = state['current'][0]
771 node = state['current'][0]
772 except LookupError:
772 except LookupError:
773 raise util.Abort(_('current bisect revision is unknown - '
773 raise util.Abort(_('current bisect revision is unknown - '
774 'start a new bisect to fix'))
774 'start a new bisect to fix'))
775 else:
775 else:
776 node, p2 = repo.dirstate.parents()
776 node, p2 = repo.dirstate.parents()
777 if p2 != nullid:
777 if p2 != nullid:
778 raise util.Abort(_('current bisect revision is a merge'))
778 raise util.Abort(_('current bisect revision is a merge'))
779 try:
779 try:
780 while changesets:
780 while changesets:
781 # update state
781 # update state
782 state['current'] = [node]
782 state['current'] = [node]
783 hbisect.save_state(repo, state)
783 hbisect.save_state(repo, state)
784 status = ui.system(command, environ={'HG_NODE': hex(node)})
784 status = ui.system(command, environ={'HG_NODE': hex(node)})
785 if status == 125:
785 if status == 125:
786 transition = "skip"
786 transition = "skip"
787 elif status == 0:
787 elif status == 0:
788 transition = "good"
788 transition = "good"
789 # status < 0 means process was killed
789 # status < 0 means process was killed
790 elif status == 127:
790 elif status == 127:
791 raise util.Abort(_("failed to execute %s") % command)
791 raise util.Abort(_("failed to execute %s") % command)
792 elif status < 0:
792 elif status < 0:
793 raise util.Abort(_("%s killed") % command)
793 raise util.Abort(_("%s killed") % command)
794 else:
794 else:
795 transition = "bad"
795 transition = "bad"
796 ctx = scmutil.revsingle(repo, rev, node)
796 ctx = scmutil.revsingle(repo, rev, node)
797 rev = None # clear for future iterations
797 rev = None # clear for future iterations
798 state[transition].append(ctx.node())
798 state[transition].append(ctx.node())
799 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
799 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
800 check_state(state, interactive=False)
800 check_state(state, interactive=False)
801 # bisect
801 # bisect
802 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
802 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
803 # update to next check
803 # update to next check
804 node = nodes[0]
804 node = nodes[0]
805 if not noupdate:
805 if not noupdate:
806 cmdutil.bailifchanged(repo)
806 cmdutil.bailifchanged(repo)
807 hg.clean(repo, node, show_stats=False)
807 hg.clean(repo, node, show_stats=False)
808 finally:
808 finally:
809 state['current'] = [node]
809 state['current'] = [node]
810 hbisect.save_state(repo, state)
810 hbisect.save_state(repo, state)
811 print_result(nodes, bgood)
811 print_result(nodes, bgood)
812 return
812 return
813
813
814 # update state
814 # update state
815
815
816 if rev:
816 if rev:
817 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
817 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
818 else:
818 else:
819 nodes = [repo.lookup('.')]
819 nodes = [repo.lookup('.')]
820
820
821 if good or bad or skip:
821 if good or bad or skip:
822 if good:
822 if good:
823 state['good'] += nodes
823 state['good'] += nodes
824 elif bad:
824 elif bad:
825 state['bad'] += nodes
825 state['bad'] += nodes
826 elif skip:
826 elif skip:
827 state['skip'] += nodes
827 state['skip'] += nodes
828 hbisect.save_state(repo, state)
828 hbisect.save_state(repo, state)
829
829
830 if not check_state(state):
830 if not check_state(state):
831 return
831 return
832
832
833 # actually bisect
833 # actually bisect
834 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
834 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
835 if extend:
835 if extend:
836 if not changesets:
836 if not changesets:
837 extendnode = extendbisectrange(nodes, good)
837 extendnode = extendbisectrange(nodes, good)
838 if extendnode is not None:
838 if extendnode is not None:
839 ui.write(_("Extending search to changeset %d:%s\n")
839 ui.write(_("Extending search to changeset %d:%s\n")
840 % (extendnode.rev(), extendnode))
840 % (extendnode.rev(), extendnode))
841 state['current'] = [extendnode.node()]
841 state['current'] = [extendnode.node()]
842 hbisect.save_state(repo, state)
842 hbisect.save_state(repo, state)
843 if noupdate:
843 if noupdate:
844 return
844 return
845 cmdutil.bailifchanged(repo)
845 cmdutil.bailifchanged(repo)
846 return hg.clean(repo, extendnode.node())
846 return hg.clean(repo, extendnode.node())
847 raise util.Abort(_("nothing to extend"))
847 raise util.Abort(_("nothing to extend"))
848
848
849 if changesets == 0:
849 if changesets == 0:
850 print_result(nodes, good)
850 print_result(nodes, good)
851 else:
851 else:
852 assert len(nodes) == 1 # only a single node can be tested next
852 assert len(nodes) == 1 # only a single node can be tested next
853 node = nodes[0]
853 node = nodes[0]
854 # compute the approximate number of remaining tests
854 # compute the approximate number of remaining tests
855 tests, size = 0, 2
855 tests, size = 0, 2
856 while size <= changesets:
856 while size <= changesets:
857 tests, size = tests + 1, size * 2
857 tests, size = tests + 1, size * 2
858 rev = repo.changelog.rev(node)
858 rev = repo.changelog.rev(node)
859 ui.write(_("Testing changeset %d:%s "
859 ui.write(_("Testing changeset %d:%s "
860 "(%d changesets remaining, ~%d tests)\n")
860 "(%d changesets remaining, ~%d tests)\n")
861 % (rev, short(node), changesets, tests))
861 % (rev, short(node), changesets, tests))
862 state['current'] = [node]
862 state['current'] = [node]
863 hbisect.save_state(repo, state)
863 hbisect.save_state(repo, state)
864 if not noupdate:
864 if not noupdate:
865 cmdutil.bailifchanged(repo)
865 cmdutil.bailifchanged(repo)
866 return hg.clean(repo, node)
866 return hg.clean(repo, node)
867
867
868 @command('bookmarks|bookmark',
868 @command('bookmarks|bookmark',
869 [('f', 'force', False, _('force')),
869 [('f', 'force', False, _('force')),
870 ('r', 'rev', '', _('revision'), _('REV')),
870 ('r', 'rev', '', _('revision'), _('REV')),
871 ('d', 'delete', False, _('delete a given bookmark')),
871 ('d', 'delete', False, _('delete a given bookmark')),
872 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
872 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
873 ('i', 'inactive', False, _('mark a bookmark inactive')),
873 ('i', 'inactive', False, _('mark a bookmark inactive')),
874 ] + formatteropts,
874 ] + formatteropts,
875 _('hg bookmarks [OPTIONS]... [NAME]...'))
875 _('hg bookmarks [OPTIONS]... [NAME]...'))
876 def bookmark(ui, repo, *names, **opts):
876 def bookmark(ui, repo, *names, **opts):
877 '''create a new bookmark or list existing bookmarks
877 '''create a new bookmark or list existing bookmarks
878
878
879 Bookmarks are labels on changesets to help track lines of development.
879 Bookmarks are labels on changesets to help track lines of development.
880 Bookmarks are unversioned and can be moved, renamed and deleted.
880 Bookmarks are unversioned and can be moved, renamed and deleted.
881 Deleting or moving a bookmark has no effect on the associated changesets.
881 Deleting or moving a bookmark has no effect on the associated changesets.
882
882
883 Creating or updating to a bookmark causes it to be marked as 'active'.
883 Creating or updating to a bookmark causes it to be marked as 'active'.
884 The active bookmark is indicated with a '*'.
884 The active bookmark is indicated with a '*'.
885 When a commit is made, the active bookmark will advance to the new commit.
885 When a commit is made, the active bookmark will advance to the new commit.
886 A plain :hg:`update` will also advance an active bookmark, if possible.
886 A plain :hg:`update` will also advance an active bookmark, if possible.
887 Updating away from a bookmark will cause it to be deactivated.
887 Updating away from a bookmark will cause it to be deactivated.
888
888
889 Bookmarks can be pushed and pulled between repositories (see
889 Bookmarks can be pushed and pulled between repositories (see
890 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
890 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
891 diverged, a new 'divergent bookmark' of the form 'name@path' will
891 diverged, a new 'divergent bookmark' of the form 'name@path' will
892 be created. Using :hg:`merge` will resolve the divergence.
892 be created. Using :hg:`merge` will resolve the divergence.
893
893
894 A bookmark named '@' has the special property that :hg:`clone` will
894 A bookmark named '@' has the special property that :hg:`clone` will
895 check it out by default if it exists.
895 check it out by default if it exists.
896
896
897 .. container:: verbose
897 .. container:: verbose
898
898
899 Examples:
899 Examples:
900
900
901 - create an active bookmark for a new line of development::
901 - create an active bookmark for a new line of development::
902
902
903 hg book new-feature
903 hg book new-feature
904
904
905 - create an inactive bookmark as a place marker::
905 - create an inactive bookmark as a place marker::
906
906
907 hg book -i reviewed
907 hg book -i reviewed
908
908
909 - create an inactive bookmark on another changeset::
909 - create an inactive bookmark on another changeset::
910
910
911 hg book -r .^ tested
911 hg book -r .^ tested
912
912
913 - move the '@' bookmark from another branch::
913 - move the '@' bookmark from another branch::
914
914
915 hg book -f @
915 hg book -f @
916 '''
916 '''
917 force = opts.get('force')
917 force = opts.get('force')
918 rev = opts.get('rev')
918 rev = opts.get('rev')
919 delete = opts.get('delete')
919 delete = opts.get('delete')
920 rename = opts.get('rename')
920 rename = opts.get('rename')
921 inactive = opts.get('inactive')
921 inactive = opts.get('inactive')
922
922
923 def checkformat(mark):
923 def checkformat(mark):
924 mark = mark.strip()
924 mark = mark.strip()
925 if not mark:
925 if not mark:
926 raise util.Abort(_("bookmark names cannot consist entirely of "
926 raise util.Abort(_("bookmark names cannot consist entirely of "
927 "whitespace"))
927 "whitespace"))
928 scmutil.checknewlabel(repo, mark, 'bookmark')
928 scmutil.checknewlabel(repo, mark, 'bookmark')
929 return mark
929 return mark
930
930
931 def checkconflict(repo, mark, cur, force=False, target=None):
931 def checkconflict(repo, mark, cur, force=False, target=None):
932 if mark in marks and not force:
932 if mark in marks and not force:
933 if target:
933 if target:
934 if marks[mark] == target and target == cur:
934 if marks[mark] == target and target == cur:
935 # re-activating a bookmark
935 # re-activating a bookmark
936 return
936 return
937 anc = repo.changelog.ancestors([repo[target].rev()])
937 anc = repo.changelog.ancestors([repo[target].rev()])
938 bmctx = repo[marks[mark]]
938 bmctx = repo[marks[mark]]
939 divs = [repo[b].node() for b in marks
939 divs = [repo[b].node() for b in marks
940 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
940 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
941
941
942 # allow resolving a single divergent bookmark even if moving
942 # allow resolving a single divergent bookmark even if moving
943 # the bookmark across branches when a revision is specified
943 # the bookmark across branches when a revision is specified
944 # that contains a divergent bookmark
944 # that contains a divergent bookmark
945 if bmctx.rev() not in anc and target in divs:
945 if bmctx.rev() not in anc and target in divs:
946 bookmarks.deletedivergent(repo, [target], mark)
946 bookmarks.deletedivergent(repo, [target], mark)
947 return
947 return
948
948
949 deletefrom = [b for b in divs
949 deletefrom = [b for b in divs
950 if repo[b].rev() in anc or b == target]
950 if repo[b].rev() in anc or b == target]
951 bookmarks.deletedivergent(repo, deletefrom, mark)
951 bookmarks.deletedivergent(repo, deletefrom, mark)
952 if bookmarks.validdest(repo, bmctx, repo[target]):
952 if bookmarks.validdest(repo, bmctx, repo[target]):
953 ui.status(_("moving bookmark '%s' forward from %s\n") %
953 ui.status(_("moving bookmark '%s' forward from %s\n") %
954 (mark, short(bmctx.node())))
954 (mark, short(bmctx.node())))
955 return
955 return
956 raise util.Abort(_("bookmark '%s' already exists "
956 raise util.Abort(_("bookmark '%s' already exists "
957 "(use -f to force)") % mark)
957 "(use -f to force)") % mark)
958 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
958 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
959 and not force):
959 and not force):
960 raise util.Abort(
960 raise util.Abort(
961 _("a bookmark cannot have the name of an existing branch"))
961 _("a bookmark cannot have the name of an existing branch"))
962
962
963 if delete and rename:
963 if delete and rename:
964 raise util.Abort(_("--delete and --rename are incompatible"))
964 raise util.Abort(_("--delete and --rename are incompatible"))
965 if delete and rev:
965 if delete and rev:
966 raise util.Abort(_("--rev is incompatible with --delete"))
966 raise util.Abort(_("--rev is incompatible with --delete"))
967 if rename and rev:
967 if rename and rev:
968 raise util.Abort(_("--rev is incompatible with --rename"))
968 raise util.Abort(_("--rev is incompatible with --rename"))
969 if not names and (delete or rev):
969 if not names and (delete or rev):
970 raise util.Abort(_("bookmark name required"))
970 raise util.Abort(_("bookmark name required"))
971
971
972 if delete or rename or names or inactive:
972 if delete or rename or names or inactive:
973 wlock = repo.wlock()
973 wlock = repo.wlock()
974 try:
974 try:
975 cur = repo.changectx('.').node()
975 cur = repo.changectx('.').node()
976 marks = repo._bookmarks
976 marks = repo._bookmarks
977 if delete:
977 if delete:
978 for mark in names:
978 for mark in names:
979 if mark not in marks:
979 if mark not in marks:
980 raise util.Abort(_("bookmark '%s' does not exist") %
980 raise util.Abort(_("bookmark '%s' does not exist") %
981 mark)
981 mark)
982 if mark == repo._activebookmark:
982 if mark == repo._activebookmark:
983 bookmarks.deactivate(repo)
983 bookmarks.deactivate(repo)
984 del marks[mark]
984 del marks[mark]
985 marks.write()
985 marks.write()
986
986
987 elif rename:
987 elif rename:
988 if not names:
988 if not names:
989 raise util.Abort(_("new bookmark name required"))
989 raise util.Abort(_("new bookmark name required"))
990 elif len(names) > 1:
990 elif len(names) > 1:
991 raise util.Abort(_("only one new bookmark name allowed"))
991 raise util.Abort(_("only one new bookmark name allowed"))
992 mark = checkformat(names[0])
992 mark = checkformat(names[0])
993 if rename not in marks:
993 if rename not in marks:
994 raise util.Abort(_("bookmark '%s' does not exist") % rename)
994 raise util.Abort(_("bookmark '%s' does not exist") % rename)
995 checkconflict(repo, mark, cur, force)
995 checkconflict(repo, mark, cur, force)
996 marks[mark] = marks[rename]
996 marks[mark] = marks[rename]
997 if repo._activebookmark == rename and not inactive:
997 if repo._activebookmark == rename and not inactive:
998 bookmarks.activate(repo, mark)
998 bookmarks.activate(repo, mark)
999 del marks[rename]
999 del marks[rename]
1000 marks.write()
1000 marks.write()
1001
1001
1002 elif names:
1002 elif names:
1003 newact = None
1003 newact = None
1004 for mark in names:
1004 for mark in names:
1005 mark = checkformat(mark)
1005 mark = checkformat(mark)
1006 if newact is None:
1006 if newact is None:
1007 newact = mark
1007 newact = mark
1008 if inactive and mark == repo._activebookmark:
1008 if inactive and mark == repo._activebookmark:
1009 bookmarks.deactivate(repo)
1009 bookmarks.deactivate(repo)
1010 return
1010 return
1011 tgt = cur
1011 tgt = cur
1012 if rev:
1012 if rev:
1013 tgt = scmutil.revsingle(repo, rev).node()
1013 tgt = scmutil.revsingle(repo, rev).node()
1014 checkconflict(repo, mark, cur, force, tgt)
1014 checkconflict(repo, mark, cur, force, tgt)
1015 marks[mark] = tgt
1015 marks[mark] = tgt
1016 if not inactive and cur == marks[newact] and not rev:
1016 if not inactive and cur == marks[newact] and not rev:
1017 bookmarks.activate(repo, newact)
1017 bookmarks.activate(repo, newact)
1018 elif cur != tgt and newact == repo._activebookmark:
1018 elif cur != tgt and newact == repo._activebookmark:
1019 bookmarks.deactivate(repo)
1019 bookmarks.deactivate(repo)
1020 marks.write()
1020 marks.write()
1021
1021
1022 elif inactive:
1022 elif inactive:
1023 if len(marks) == 0:
1023 if len(marks) == 0:
1024 ui.status(_("no bookmarks set\n"))
1024 ui.status(_("no bookmarks set\n"))
1025 elif not repo._activebookmark:
1025 elif not repo._activebookmark:
1026 ui.status(_("no active bookmark\n"))
1026 ui.status(_("no active bookmark\n"))
1027 else:
1027 else:
1028 bookmarks.deactivate(repo)
1028 bookmarks.deactivate(repo)
1029 finally:
1029 finally:
1030 wlock.release()
1030 wlock.release()
1031 else: # show bookmarks
1031 else: # show bookmarks
1032 fm = ui.formatter('bookmarks', opts)
1032 fm = ui.formatter('bookmarks', opts)
1033 hexfn = fm.hexfunc
1033 hexfn = fm.hexfunc
1034 marks = repo._bookmarks
1034 marks = repo._bookmarks
1035 if len(marks) == 0 and not fm:
1035 if len(marks) == 0 and not fm:
1036 ui.status(_("no bookmarks set\n"))
1036 ui.status(_("no bookmarks set\n"))
1037 for bmark, n in sorted(marks.iteritems()):
1037 for bmark, n in sorted(marks.iteritems()):
1038 current = repo._activebookmark
1038 current = repo._activebookmark
1039 if bmark == current:
1039 if bmark == current:
1040 prefix, label = '*', 'bookmarks.current'
1040 prefix, label = '*', 'bookmarks.current'
1041 else:
1041 else:
1042 prefix, label = ' ', ''
1042 prefix, label = ' ', ''
1043
1043
1044 fm.startitem()
1044 fm.startitem()
1045 if not ui.quiet:
1045 if not ui.quiet:
1046 fm.plain(' %s ' % prefix, label=label)
1046 fm.plain(' %s ' % prefix, label=label)
1047 fm.write('bookmark', '%s', bmark, label=label)
1047 fm.write('bookmark', '%s', bmark, label=label)
1048 pad = " " * (25 - encoding.colwidth(bmark))
1048 pad = " " * (25 - encoding.colwidth(bmark))
1049 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1049 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1050 repo.changelog.rev(n), hexfn(n), label=label)
1050 repo.changelog.rev(n), hexfn(n), label=label)
1051 fm.data(active=(bmark == current))
1051 fm.data(active=(bmark == current))
1052 fm.plain('\n')
1052 fm.plain('\n')
1053 fm.end()
1053 fm.end()
1054
1054
1055 @command('branch',
1055 @command('branch',
1056 [('f', 'force', None,
1056 [('f', 'force', None,
1057 _('set branch name even if it shadows an existing branch')),
1057 _('set branch name even if it shadows an existing branch')),
1058 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1058 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1059 _('[-fC] [NAME]'))
1059 _('[-fC] [NAME]'))
1060 def branch(ui, repo, label=None, **opts):
1060 def branch(ui, repo, label=None, **opts):
1061 """set or show the current branch name
1061 """set or show the current branch name
1062
1062
1063 .. note::
1063 .. note::
1064
1064
1065 Branch names are permanent and global. Use :hg:`bookmark` to create a
1065 Branch names are permanent and global. Use :hg:`bookmark` to create a
1066 light-weight bookmark instead. See :hg:`help glossary` for more
1066 light-weight bookmark instead. See :hg:`help glossary` for more
1067 information about named branches and bookmarks.
1067 information about named branches and bookmarks.
1068
1068
1069 With no argument, show the current branch name. With one argument,
1069 With no argument, show the current branch name. With one argument,
1070 set the working directory branch name (the branch will not exist
1070 set the working directory branch name (the branch will not exist
1071 in the repository until the next commit). Standard practice
1071 in the repository until the next commit). Standard practice
1072 recommends that primary development take place on the 'default'
1072 recommends that primary development take place on the 'default'
1073 branch.
1073 branch.
1074
1074
1075 Unless -f/--force is specified, branch will not let you set a
1075 Unless -f/--force is specified, branch will not let you set a
1076 branch name that already exists.
1076 branch name that already exists.
1077
1077
1078 Use -C/--clean to reset the working directory branch to that of
1078 Use -C/--clean to reset the working directory branch to that of
1079 the parent of the working directory, negating a previous branch
1079 the parent of the working directory, negating a previous branch
1080 change.
1080 change.
1081
1081
1082 Use the command :hg:`update` to switch to an existing branch. Use
1082 Use the command :hg:`update` to switch to an existing branch. Use
1083 :hg:`commit --close-branch` to mark this branch as closed.
1083 :hg:`commit --close-branch` to mark this branch as closed.
1084
1084
1085 Returns 0 on success.
1085 Returns 0 on success.
1086 """
1086 """
1087 if label:
1087 if label:
1088 label = label.strip()
1088 label = label.strip()
1089
1089
1090 if not opts.get('clean') and not label:
1090 if not opts.get('clean') and not label:
1091 ui.write("%s\n" % repo.dirstate.branch())
1091 ui.write("%s\n" % repo.dirstate.branch())
1092 return
1092 return
1093
1093
1094 wlock = repo.wlock()
1094 wlock = repo.wlock()
1095 try:
1095 try:
1096 if opts.get('clean'):
1096 if opts.get('clean'):
1097 label = repo[None].p1().branch()
1097 label = repo[None].p1().branch()
1098 repo.dirstate.setbranch(label)
1098 repo.dirstate.setbranch(label)
1099 ui.status(_('reset working directory to branch %s\n') % label)
1099 ui.status(_('reset working directory to branch %s\n') % label)
1100 elif label:
1100 elif label:
1101 if not opts.get('force') and label in repo.branchmap():
1101 if not opts.get('force') and label in repo.branchmap():
1102 if label not in [p.branch() for p in repo.parents()]:
1102 if label not in [p.branch() for p in repo.parents()]:
1103 raise util.Abort(_('a branch of the same name already'
1103 raise util.Abort(_('a branch of the same name already'
1104 ' exists'),
1104 ' exists'),
1105 # i18n: "it" refers to an existing branch
1105 # i18n: "it" refers to an existing branch
1106 hint=_("use 'hg update' to switch to it"))
1106 hint=_("use 'hg update' to switch to it"))
1107 scmutil.checknewlabel(repo, label, 'branch')
1107 scmutil.checknewlabel(repo, label, 'branch')
1108 repo.dirstate.setbranch(label)
1108 repo.dirstate.setbranch(label)
1109 ui.status(_('marked working directory as branch %s\n') % label)
1109 ui.status(_('marked working directory as branch %s\n') % label)
1110 ui.status(_('(branches are permanent and global, '
1110 ui.status(_('(branches are permanent and global, '
1111 'did you want a bookmark?)\n'))
1111 'did you want a bookmark?)\n'))
1112 finally:
1112 finally:
1113 wlock.release()
1113 wlock.release()
1114
1114
1115 @command('branches',
1115 @command('branches',
1116 [('a', 'active', False,
1116 [('a', 'active', False,
1117 _('show only branches that have unmerged heads (DEPRECATED)')),
1117 _('show only branches that have unmerged heads (DEPRECATED)')),
1118 ('c', 'closed', False, _('show normal and closed branches')),
1118 ('c', 'closed', False, _('show normal and closed branches')),
1119 ] + formatteropts,
1119 ] + formatteropts,
1120 _('[-ac]'))
1120 _('[-ac]'))
1121 def branches(ui, repo, active=False, closed=False, **opts):
1121 def branches(ui, repo, active=False, closed=False, **opts):
1122 """list repository named branches
1122 """list repository named branches
1123
1123
1124 List the repository's named branches, indicating which ones are
1124 List the repository's named branches, indicating which ones are
1125 inactive. If -c/--closed is specified, also list branches which have
1125 inactive. If -c/--closed is specified, also list branches which have
1126 been marked closed (see :hg:`commit --close-branch`).
1126 been marked closed (see :hg:`commit --close-branch`).
1127
1127
1128 Use the command :hg:`update` to switch to an existing branch.
1128 Use the command :hg:`update` to switch to an existing branch.
1129
1129
1130 Returns 0.
1130 Returns 0.
1131 """
1131 """
1132
1132
1133 fm = ui.formatter('branches', opts)
1133 fm = ui.formatter('branches', opts)
1134 hexfunc = fm.hexfunc
1134 hexfunc = fm.hexfunc
1135
1135
1136 allheads = set(repo.heads())
1136 allheads = set(repo.heads())
1137 branches = []
1137 branches = []
1138 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1138 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1139 isactive = not isclosed and bool(set(heads) & allheads)
1139 isactive = not isclosed and bool(set(heads) & allheads)
1140 branches.append((tag, repo[tip], isactive, not isclosed))
1140 branches.append((tag, repo[tip], isactive, not isclosed))
1141 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1141 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1142 reverse=True)
1142 reverse=True)
1143
1143
1144 for tag, ctx, isactive, isopen in branches:
1144 for tag, ctx, isactive, isopen in branches:
1145 if active and not isactive:
1145 if active and not isactive:
1146 continue
1146 continue
1147 if isactive:
1147 if isactive:
1148 label = 'branches.active'
1148 label = 'branches.active'
1149 notice = ''
1149 notice = ''
1150 elif not isopen:
1150 elif not isopen:
1151 if not closed:
1151 if not closed:
1152 continue
1152 continue
1153 label = 'branches.closed'
1153 label = 'branches.closed'
1154 notice = _(' (closed)')
1154 notice = _(' (closed)')
1155 else:
1155 else:
1156 label = 'branches.inactive'
1156 label = 'branches.inactive'
1157 notice = _(' (inactive)')
1157 notice = _(' (inactive)')
1158 current = (tag == repo.dirstate.branch())
1158 current = (tag == repo.dirstate.branch())
1159 if current:
1159 if current:
1160 label = 'branches.current'
1160 label = 'branches.current'
1161
1161
1162 fm.startitem()
1162 fm.startitem()
1163 fm.write('branch', '%s', tag, label=label)
1163 fm.write('branch', '%s', tag, label=label)
1164 rev = ctx.rev()
1164 rev = ctx.rev()
1165 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1165 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1166 fmt = ' ' * padsize + ' %d:%s'
1166 fmt = ' ' * padsize + ' %d:%s'
1167 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1167 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1168 label='log.changeset changeset.%s' % ctx.phasestr())
1168 label='log.changeset changeset.%s' % ctx.phasestr())
1169 fm.data(active=isactive, closed=not isopen, current=current)
1169 fm.data(active=isactive, closed=not isopen, current=current)
1170 if not ui.quiet:
1170 if not ui.quiet:
1171 fm.plain(notice)
1171 fm.plain(notice)
1172 fm.plain('\n')
1172 fm.plain('\n')
1173 fm.end()
1173 fm.end()
1174
1174
1175 @command('bundle',
1175 @command('bundle',
1176 [('f', 'force', None, _('run even when the destination is unrelated')),
1176 [('f', 'force', None, _('run even when the destination is unrelated')),
1177 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1177 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1178 _('REV')),
1178 _('REV')),
1179 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1179 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1180 _('BRANCH')),
1180 _('BRANCH')),
1181 ('', 'base', [],
1181 ('', 'base', [],
1182 _('a base changeset assumed to be available at the destination'),
1182 _('a base changeset assumed to be available at the destination'),
1183 _('REV')),
1183 _('REV')),
1184 ('a', 'all', None, _('bundle all changesets in the repository')),
1184 ('a', 'all', None, _('bundle all changesets in the repository')),
1185 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1185 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1186 ] + remoteopts,
1186 ] + remoteopts,
1187 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1187 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1188 def bundle(ui, repo, fname, dest=None, **opts):
1188 def bundle(ui, repo, fname, dest=None, **opts):
1189 """create a changegroup file
1189 """create a changegroup file
1190
1190
1191 Generate a compressed changegroup file collecting changesets not
1191 Generate a compressed changegroup file collecting changesets not
1192 known to be in another repository.
1192 known to be in another repository.
1193
1193
1194 If you omit the destination repository, then hg assumes the
1194 If you omit the destination repository, then hg assumes the
1195 destination will have all the nodes you specify with --base
1195 destination will have all the nodes you specify with --base
1196 parameters. To create a bundle containing all changesets, use
1196 parameters. To create a bundle containing all changesets, use
1197 -a/--all (or --base null).
1197 -a/--all (or --base null).
1198
1198
1199 You can change compression method with the -t/--type option.
1199 You can change compression method with the -t/--type option.
1200 The available compression methods are: none, bzip2, and
1200 The available compression methods are: none, bzip2, and
1201 gzip (by default, bundles are compressed using bzip2).
1201 gzip (by default, bundles are compressed using bzip2).
1202
1202
1203 The bundle file can then be transferred using conventional means
1203 The bundle file can then be transferred using conventional means
1204 and applied to another repository with the unbundle or pull
1204 and applied to another repository with the unbundle or pull
1205 command. This is useful when direct push and pull are not
1205 command. This is useful when direct push and pull are not
1206 available or when exporting an entire repository is undesirable.
1206 available or when exporting an entire repository is undesirable.
1207
1207
1208 Applying bundles preserves all changeset contents including
1208 Applying bundles preserves all changeset contents including
1209 permissions, copy/rename information, and revision history.
1209 permissions, copy/rename information, and revision history.
1210
1210
1211 Returns 0 on success, 1 if no changes found.
1211 Returns 0 on success, 1 if no changes found.
1212 """
1212 """
1213 revs = None
1213 revs = None
1214 if 'rev' in opts:
1214 if 'rev' in opts:
1215 revs = scmutil.revrange(repo, opts['rev'])
1215 revs = scmutil.revrange(repo, opts['rev'])
1216
1216
1217 bundletype = opts.get('type', 'bzip2').lower()
1217 bundletype = opts.get('type', 'bzip2').lower()
1218 btypes = {'none': 'HG10UN',
1218 btypes = {'none': 'HG10UN',
1219 'bzip2': 'HG10BZ',
1219 'bzip2': 'HG10BZ',
1220 'gzip': 'HG10GZ',
1220 'gzip': 'HG10GZ',
1221 'bundle2': 'HG20'}
1221 'bundle2': 'HG20'}
1222 bundletype = btypes.get(bundletype)
1222 bundletype = btypes.get(bundletype)
1223 if bundletype not in changegroup.bundletypes:
1223 if bundletype not in changegroup.bundletypes:
1224 raise util.Abort(_('unknown bundle type specified with --type'))
1224 raise util.Abort(_('unknown bundle type specified with --type'))
1225
1225
1226 if opts.get('all'):
1226 if opts.get('all'):
1227 base = ['null']
1227 base = ['null']
1228 else:
1228 else:
1229 base = scmutil.revrange(repo, opts.get('base'))
1229 base = scmutil.revrange(repo, opts.get('base'))
1230 # TODO: get desired bundlecaps from command line.
1230 # TODO: get desired bundlecaps from command line.
1231 bundlecaps = None
1231 bundlecaps = None
1232 if base:
1232 if base:
1233 if dest:
1233 if dest:
1234 raise util.Abort(_("--base is incompatible with specifying "
1234 raise util.Abort(_("--base is incompatible with specifying "
1235 "a destination"))
1235 "a destination"))
1236 common = [repo.lookup(rev) for rev in base]
1236 common = [repo.lookup(rev) for rev in base]
1237 heads = revs and map(repo.lookup, revs) or revs
1237 heads = revs and map(repo.lookup, revs) or revs
1238 cg = changegroup.getchangegroup(repo, 'bundle', heads=heads,
1238 cg = changegroup.getchangegroup(repo, 'bundle', heads=heads,
1239 common=common, bundlecaps=bundlecaps)
1239 common=common, bundlecaps=bundlecaps)
1240 outgoing = None
1240 outgoing = None
1241 else:
1241 else:
1242 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1242 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1243 dest, branches = hg.parseurl(dest, opts.get('branch'))
1243 dest, branches = hg.parseurl(dest, opts.get('branch'))
1244 other = hg.peer(repo, opts, dest)
1244 other = hg.peer(repo, opts, dest)
1245 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1245 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1246 heads = revs and map(repo.lookup, revs) or revs
1246 heads = revs and map(repo.lookup, revs) or revs
1247 outgoing = discovery.findcommonoutgoing(repo, other,
1247 outgoing = discovery.findcommonoutgoing(repo, other,
1248 onlyheads=heads,
1248 onlyheads=heads,
1249 force=opts.get('force'),
1249 force=opts.get('force'),
1250 portable=True)
1250 portable=True)
1251 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1251 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1252 bundlecaps)
1252 bundlecaps)
1253 if not cg:
1253 if not cg:
1254 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1254 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1255 return 1
1255 return 1
1256
1256
1257 changegroup.writebundle(ui, cg, fname, bundletype)
1257 changegroup.writebundle(ui, cg, fname, bundletype)
1258
1258
1259 @command('cat',
1259 @command('cat',
1260 [('o', 'output', '',
1260 [('o', 'output', '',
1261 _('print output to file with formatted name'), _('FORMAT')),
1261 _('print output to file with formatted name'), _('FORMAT')),
1262 ('r', 'rev', '', _('print the given revision'), _('REV')),
1262 ('r', 'rev', '', _('print the given revision'), _('REV')),
1263 ('', 'decode', None, _('apply any matching decode filter')),
1263 ('', 'decode', None, _('apply any matching decode filter')),
1264 ] + walkopts,
1264 ] + walkopts,
1265 _('[OPTION]... FILE...'),
1265 _('[OPTION]... FILE...'),
1266 inferrepo=True)
1266 inferrepo=True)
1267 def cat(ui, repo, file1, *pats, **opts):
1267 def cat(ui, repo, file1, *pats, **opts):
1268 """output the current or given revision of files
1268 """output the current or given revision of files
1269
1269
1270 Print the specified files as they were at the given revision. If
1270 Print the specified files as they were at the given revision. If
1271 no revision is given, the parent of the working directory is used.
1271 no revision is given, the parent of the working directory is used.
1272
1272
1273 Output may be to a file, in which case the name of the file is
1273 Output may be to a file, in which case the name of the file is
1274 given using a format string. The formatting rules as follows:
1274 given using a format string. The formatting rules as follows:
1275
1275
1276 :``%%``: literal "%" character
1276 :``%%``: literal "%" character
1277 :``%s``: basename of file being printed
1277 :``%s``: basename of file being printed
1278 :``%d``: dirname of file being printed, or '.' if in repository root
1278 :``%d``: dirname of file being printed, or '.' if in repository root
1279 :``%p``: root-relative path name of file being printed
1279 :``%p``: root-relative path name of file being printed
1280 :``%H``: changeset hash (40 hexadecimal digits)
1280 :``%H``: changeset hash (40 hexadecimal digits)
1281 :``%R``: changeset revision number
1281 :``%R``: changeset revision number
1282 :``%h``: short-form changeset hash (12 hexadecimal digits)
1282 :``%h``: short-form changeset hash (12 hexadecimal digits)
1283 :``%r``: zero-padded changeset revision number
1283 :``%r``: zero-padded changeset revision number
1284 :``%b``: basename of the exporting repository
1284 :``%b``: basename of the exporting repository
1285
1285
1286 Returns 0 on success.
1286 Returns 0 on success.
1287 """
1287 """
1288 ctx = scmutil.revsingle(repo, opts.get('rev'))
1288 ctx = scmutil.revsingle(repo, opts.get('rev'))
1289 m = scmutil.match(ctx, (file1,) + pats, opts)
1289 m = scmutil.match(ctx, (file1,) + pats, opts)
1290
1290
1291 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1291 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1292
1292
1293 @command('^clone',
1293 @command('^clone',
1294 [('U', 'noupdate', None, _('the clone will include an empty working '
1294 [('U', 'noupdate', None, _('the clone will include an empty working '
1295 'directory (only a repository)')),
1295 'directory (only a repository)')),
1296 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1296 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1297 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1297 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1298 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1298 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1299 ('', 'pull', None, _('use pull protocol to copy metadata')),
1299 ('', 'pull', None, _('use pull protocol to copy metadata')),
1300 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1300 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1301 ] + remoteopts,
1301 ] + remoteopts,
1302 _('[OPTION]... SOURCE [DEST]'),
1302 _('[OPTION]... SOURCE [DEST]'),
1303 norepo=True)
1303 norepo=True)
1304 def clone(ui, source, dest=None, **opts):
1304 def clone(ui, source, dest=None, **opts):
1305 """make a copy of an existing repository
1305 """make a copy of an existing repository
1306
1306
1307 Create a copy of an existing repository in a new directory.
1307 Create a copy of an existing repository in a new directory.
1308
1308
1309 If no destination directory name is specified, it defaults to the
1309 If no destination directory name is specified, it defaults to the
1310 basename of the source.
1310 basename of the source.
1311
1311
1312 The location of the source is added to the new repository's
1312 The location of the source is added to the new repository's
1313 ``.hg/hgrc`` file, as the default to be used for future pulls.
1313 ``.hg/hgrc`` file, as the default to be used for future pulls.
1314
1314
1315 Only local paths and ``ssh://`` URLs are supported as
1315 Only local paths and ``ssh://`` URLs are supported as
1316 destinations. For ``ssh://`` destinations, no working directory or
1316 destinations. For ``ssh://`` destinations, no working directory or
1317 ``.hg/hgrc`` will be created on the remote side.
1317 ``.hg/hgrc`` will be created on the remote side.
1318
1318
1319 To pull only a subset of changesets, specify one or more revisions
1319 To pull only a subset of changesets, specify one or more revisions
1320 identifiers with -r/--rev or branches with -b/--branch. The
1320 identifiers with -r/--rev or branches with -b/--branch. The
1321 resulting clone will contain only the specified changesets and
1321 resulting clone will contain only the specified changesets and
1322 their ancestors. These options (or 'clone src#rev dest') imply
1322 their ancestors. These options (or 'clone src#rev dest') imply
1323 --pull, even for local source repositories. Note that specifying a
1323 --pull, even for local source repositories. Note that specifying a
1324 tag will include the tagged changeset but not the changeset
1324 tag will include the tagged changeset but not the changeset
1325 containing the tag.
1325 containing the tag.
1326
1326
1327 If the source repository has a bookmark called '@' set, that
1327 If the source repository has a bookmark called '@' set, that
1328 revision will be checked out in the new repository by default.
1328 revision will be checked out in the new repository by default.
1329
1329
1330 To check out a particular version, use -u/--update, or
1330 To check out a particular version, use -u/--update, or
1331 -U/--noupdate to create a clone with no working directory.
1331 -U/--noupdate to create a clone with no working directory.
1332
1332
1333 .. container:: verbose
1333 .. container:: verbose
1334
1334
1335 For efficiency, hardlinks are used for cloning whenever the
1335 For efficiency, hardlinks are used for cloning whenever the
1336 source and destination are on the same filesystem (note this
1336 source and destination are on the same filesystem (note this
1337 applies only to the repository data, not to the working
1337 applies only to the repository data, not to the working
1338 directory). Some filesystems, such as AFS, implement hardlinking
1338 directory). Some filesystems, such as AFS, implement hardlinking
1339 incorrectly, but do not report errors. In these cases, use the
1339 incorrectly, but do not report errors. In these cases, use the
1340 --pull option to avoid hardlinking.
1340 --pull option to avoid hardlinking.
1341
1341
1342 In some cases, you can clone repositories and the working
1342 In some cases, you can clone repositories and the working
1343 directory using full hardlinks with ::
1343 directory using full hardlinks with ::
1344
1344
1345 $ cp -al REPO REPOCLONE
1345 $ cp -al REPO REPOCLONE
1346
1346
1347 This is the fastest way to clone, but it is not always safe. The
1347 This is the fastest way to clone, but it is not always safe. The
1348 operation is not atomic (making sure REPO is not modified during
1348 operation is not atomic (making sure REPO is not modified during
1349 the operation is up to you) and you have to make sure your
1349 the operation is up to you) and you have to make sure your
1350 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1350 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1351 so). Also, this is not compatible with certain extensions that
1351 so). Also, this is not compatible with certain extensions that
1352 place their metadata under the .hg directory, such as mq.
1352 place their metadata under the .hg directory, such as mq.
1353
1353
1354 Mercurial will update the working directory to the first applicable
1354 Mercurial will update the working directory to the first applicable
1355 revision from this list:
1355 revision from this list:
1356
1356
1357 a) null if -U or the source repository has no changesets
1357 a) null if -U or the source repository has no changesets
1358 b) if -u . and the source repository is local, the first parent of
1358 b) if -u . and the source repository is local, the first parent of
1359 the source repository's working directory
1359 the source repository's working directory
1360 c) the changeset specified with -u (if a branch name, this means the
1360 c) the changeset specified with -u (if a branch name, this means the
1361 latest head of that branch)
1361 latest head of that branch)
1362 d) the changeset specified with -r
1362 d) the changeset specified with -r
1363 e) the tipmost head specified with -b
1363 e) the tipmost head specified with -b
1364 f) the tipmost head specified with the url#branch source syntax
1364 f) the tipmost head specified with the url#branch source syntax
1365 g) the revision marked with the '@' bookmark, if present
1365 g) the revision marked with the '@' bookmark, if present
1366 h) the tipmost head of the default branch
1366 h) the tipmost head of the default branch
1367 i) tip
1367 i) tip
1368
1368
1369 Examples:
1369 Examples:
1370
1370
1371 - clone a remote repository to a new directory named hg/::
1371 - clone a remote repository to a new directory named hg/::
1372
1372
1373 hg clone http://selenic.com/hg
1373 hg clone http://selenic.com/hg
1374
1374
1375 - create a lightweight local clone::
1375 - create a lightweight local clone::
1376
1376
1377 hg clone project/ project-feature/
1377 hg clone project/ project-feature/
1378
1378
1379 - clone from an absolute path on an ssh server (note double-slash)::
1379 - clone from an absolute path on an ssh server (note double-slash)::
1380
1380
1381 hg clone ssh://user@server//home/projects/alpha/
1381 hg clone ssh://user@server//home/projects/alpha/
1382
1382
1383 - do a high-speed clone over a LAN while checking out a
1383 - do a high-speed clone over a LAN while checking out a
1384 specified version::
1384 specified version::
1385
1385
1386 hg clone --uncompressed http://server/repo -u 1.5
1386 hg clone --uncompressed http://server/repo -u 1.5
1387
1387
1388 - create a repository without changesets after a particular revision::
1388 - create a repository without changesets after a particular revision::
1389
1389
1390 hg clone -r 04e544 experimental/ good/
1390 hg clone -r 04e544 experimental/ good/
1391
1391
1392 - clone (and track) a particular named branch::
1392 - clone (and track) a particular named branch::
1393
1393
1394 hg clone http://selenic.com/hg#stable
1394 hg clone http://selenic.com/hg#stable
1395
1395
1396 See :hg:`help urls` for details on specifying URLs.
1396 See :hg:`help urls` for details on specifying URLs.
1397
1397
1398 Returns 0 on success.
1398 Returns 0 on success.
1399 """
1399 """
1400 if opts.get('noupdate') and opts.get('updaterev'):
1400 if opts.get('noupdate') and opts.get('updaterev'):
1401 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1401 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1402
1402
1403 r = hg.clone(ui, opts, source, dest,
1403 r = hg.clone(ui, opts, source, dest,
1404 pull=opts.get('pull'),
1404 pull=opts.get('pull'),
1405 stream=opts.get('uncompressed'),
1405 stream=opts.get('uncompressed'),
1406 rev=opts.get('rev'),
1406 rev=opts.get('rev'),
1407 update=opts.get('updaterev') or not opts.get('noupdate'),
1407 update=opts.get('updaterev') or not opts.get('noupdate'),
1408 branch=opts.get('branch'))
1408 branch=opts.get('branch'))
1409
1409
1410 return r is None
1410 return r is None
1411
1411
1412 @command('^commit|ci',
1412 @command('^commit|ci',
1413 [('A', 'addremove', None,
1413 [('A', 'addremove', None,
1414 _('mark new/missing files as added/removed before committing')),
1414 _('mark new/missing files as added/removed before committing')),
1415 ('', 'close-branch', None,
1415 ('', 'close-branch', None,
1416 _('mark a branch as closed, hiding it from the branch list')),
1416 _('mark a branch as closed, hiding it from the branch list')),
1417 ('', 'amend', None, _('amend the parent of the working directory')),
1417 ('', 'amend', None, _('amend the parent of the working directory')),
1418 ('s', 'secret', None, _('use the secret phase for committing')),
1418 ('s', 'secret', None, _('use the secret phase for committing')),
1419 ('e', 'edit', None, _('invoke editor on commit messages')),
1419 ('e', 'edit', None, _('invoke editor on commit messages')),
1420 ('i', 'interactive', None, _('use interactive mode')),
1420 ('i', 'interactive', None, _('use interactive mode')),
1421 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1421 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1422 _('[OPTION]... [FILE]...'),
1422 _('[OPTION]... [FILE]...'),
1423 inferrepo=True)
1423 inferrepo=True)
1424 def commit(ui, repo, *pats, **opts):
1424 def commit(ui, repo, *pats, **opts):
1425 """commit the specified files or all outstanding changes
1425 """commit the specified files or all outstanding changes
1426
1426
1427 Commit changes to the given files into the repository. Unlike a
1427 Commit changes to the given files into the repository. Unlike a
1428 centralized SCM, this operation is a local operation. See
1428 centralized SCM, this operation is a local operation. See
1429 :hg:`push` for a way to actively distribute your changes.
1429 :hg:`push` for a way to actively distribute your changes.
1430
1430
1431 If a list of files is omitted, all changes reported by :hg:`status`
1431 If a list of files is omitted, all changes reported by :hg:`status`
1432 will be committed.
1432 will be committed.
1433
1433
1434 If you are committing the result of a merge, do not provide any
1434 If you are committing the result of a merge, do not provide any
1435 filenames or -I/-X filters.
1435 filenames or -I/-X filters.
1436
1436
1437 If no commit message is specified, Mercurial starts your
1437 If no commit message is specified, Mercurial starts your
1438 configured editor where you can enter a message. In case your
1438 configured editor where you can enter a message. In case your
1439 commit fails, you will find a backup of your message in
1439 commit fails, you will find a backup of your message in
1440 ``.hg/last-message.txt``.
1440 ``.hg/last-message.txt``.
1441
1441
1442 The --amend flag can be used to amend the parent of the
1442 The --amend flag can be used to amend the parent of the
1443 working directory with a new commit that contains the changes
1443 working directory with a new commit that contains the changes
1444 in the parent in addition to those currently reported by :hg:`status`,
1444 in the parent in addition to those currently reported by :hg:`status`,
1445 if there are any. The old commit is stored in a backup bundle in
1445 if there are any. The old commit is stored in a backup bundle in
1446 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1446 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1447 on how to restore it).
1447 on how to restore it).
1448
1448
1449 Message, user and date are taken from the amended commit unless
1449 Message, user and date are taken from the amended commit unless
1450 specified. When a message isn't specified on the command line,
1450 specified. When a message isn't specified on the command line,
1451 the editor will open with the message of the amended commit.
1451 the editor will open with the message of the amended commit.
1452
1452
1453 It is not possible to amend public changesets (see :hg:`help phases`)
1453 It is not possible to amend public changesets (see :hg:`help phases`)
1454 or changesets that have children.
1454 or changesets that have children.
1455
1455
1456 See :hg:`help dates` for a list of formats valid for -d/--date.
1456 See :hg:`help dates` for a list of formats valid for -d/--date.
1457
1457
1458 Returns 0 on success, 1 if nothing changed.
1458 Returns 0 on success, 1 if nothing changed.
1459 """
1459 """
1460 if opts.get('interactive'):
1460 if opts.get('interactive'):
1461 opts.pop('interactive')
1461 opts.pop('interactive')
1462 cmdutil.dorecord(ui, repo, commit, 'commit', False,
1462 cmdutil.dorecord(ui, repo, commit, 'commit', False,
1463 cmdutil.recordfilter, *pats, **opts)
1463 cmdutil.recordfilter, *pats, **opts)
1464 return
1464 return
1465
1465
1466 if opts.get('subrepos'):
1466 if opts.get('subrepos'):
1467 if opts.get('amend'):
1467 if opts.get('amend'):
1468 raise util.Abort(_('cannot amend with --subrepos'))
1468 raise util.Abort(_('cannot amend with --subrepos'))
1469 # Let --subrepos on the command line override config setting.
1469 # Let --subrepos on the command line override config setting.
1470 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1470 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1471
1471
1472 cmdutil.checkunfinished(repo, commit=True)
1472 cmdutil.checkunfinished(repo, commit=True)
1473
1473
1474 branch = repo[None].branch()
1474 branch = repo[None].branch()
1475 bheads = repo.branchheads(branch)
1475 bheads = repo.branchheads(branch)
1476
1476
1477 extra = {}
1477 extra = {}
1478 if opts.get('close_branch'):
1478 if opts.get('close_branch'):
1479 extra['close'] = 1
1479 extra['close'] = 1
1480
1480
1481 if not bheads:
1481 if not bheads:
1482 raise util.Abort(_('can only close branch heads'))
1482 raise util.Abort(_('can only close branch heads'))
1483 elif opts.get('amend'):
1483 elif opts.get('amend'):
1484 if repo.parents()[0].p1().branch() != branch and \
1484 if repo.parents()[0].p1().branch() != branch and \
1485 repo.parents()[0].p2().branch() != branch:
1485 repo.parents()[0].p2().branch() != branch:
1486 raise util.Abort(_('can only close branch heads'))
1486 raise util.Abort(_('can only close branch heads'))
1487
1487
1488 if opts.get('amend'):
1488 if opts.get('amend'):
1489 if ui.configbool('ui', 'commitsubrepos'):
1489 if ui.configbool('ui', 'commitsubrepos'):
1490 raise util.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1490 raise util.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1491
1491
1492 old = repo['.']
1492 old = repo['.']
1493 if not old.mutable():
1493 if not old.mutable():
1494 raise util.Abort(_('cannot amend public changesets'))
1494 raise util.Abort(_('cannot amend public changesets'))
1495 if len(repo[None].parents()) > 1:
1495 if len(repo[None].parents()) > 1:
1496 raise util.Abort(_('cannot amend while merging'))
1496 raise util.Abort(_('cannot amend while merging'))
1497 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1497 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1498 if not allowunstable and old.children():
1498 if not allowunstable and old.children():
1499 raise util.Abort(_('cannot amend changeset with children'))
1499 raise util.Abort(_('cannot amend changeset with children'))
1500
1500
1501 # commitfunc is used only for temporary amend commit by cmdutil.amend
1501 # commitfunc is used only for temporary amend commit by cmdutil.amend
1502 def commitfunc(ui, repo, message, match, opts):
1502 def commitfunc(ui, repo, message, match, opts):
1503 return repo.commit(message,
1503 return repo.commit(message,
1504 opts.get('user') or old.user(),
1504 opts.get('user') or old.user(),
1505 opts.get('date') or old.date(),
1505 opts.get('date') or old.date(),
1506 match,
1506 match,
1507 extra=extra)
1507 extra=extra)
1508
1508
1509 current = repo._activebookmark
1509 current = repo._activebookmark
1510 marks = old.bookmarks()
1510 marks = old.bookmarks()
1511 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1511 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1512 if node == old.node():
1512 if node == old.node():
1513 ui.status(_("nothing changed\n"))
1513 ui.status(_("nothing changed\n"))
1514 return 1
1514 return 1
1515 elif marks:
1515 elif marks:
1516 ui.debug('moving bookmarks %r from %s to %s\n' %
1516 ui.debug('moving bookmarks %r from %s to %s\n' %
1517 (marks, old.hex(), hex(node)))
1517 (marks, old.hex(), hex(node)))
1518 newmarks = repo._bookmarks
1518 newmarks = repo._bookmarks
1519 for bm in marks:
1519 for bm in marks:
1520 newmarks[bm] = node
1520 newmarks[bm] = node
1521 if bm == current:
1521 if bm == current:
1522 bookmarks.activate(repo, bm)
1522 bookmarks.activate(repo, bm)
1523 newmarks.write()
1523 newmarks.write()
1524 else:
1524 else:
1525 def commitfunc(ui, repo, message, match, opts):
1525 def commitfunc(ui, repo, message, match, opts):
1526 backup = ui.backupconfig('phases', 'new-commit')
1526 backup = ui.backupconfig('phases', 'new-commit')
1527 baseui = repo.baseui
1527 baseui = repo.baseui
1528 basebackup = baseui.backupconfig('phases', 'new-commit')
1528 basebackup = baseui.backupconfig('phases', 'new-commit')
1529 try:
1529 try:
1530 if opts.get('secret'):
1530 if opts.get('secret'):
1531 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1531 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1532 # Propagate to subrepos
1532 # Propagate to subrepos
1533 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1533 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1534
1534
1535 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1535 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1536 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1536 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1537 return repo.commit(message, opts.get('user'), opts.get('date'),
1537 return repo.commit(message, opts.get('user'), opts.get('date'),
1538 match,
1538 match,
1539 editor=editor,
1539 editor=editor,
1540 extra=extra)
1540 extra=extra)
1541 finally:
1541 finally:
1542 ui.restoreconfig(backup)
1542 ui.restoreconfig(backup)
1543 repo.baseui.restoreconfig(basebackup)
1543 repo.baseui.restoreconfig(basebackup)
1544
1544
1545
1545
1546 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1546 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1547
1547
1548 if not node:
1548 if not node:
1549 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1549 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1550 if stat[3]:
1550 if stat[3]:
1551 ui.status(_("nothing changed (%d missing files, see "
1551 ui.status(_("nothing changed (%d missing files, see "
1552 "'hg status')\n") % len(stat[3]))
1552 "'hg status')\n") % len(stat[3]))
1553 else:
1553 else:
1554 ui.status(_("nothing changed\n"))
1554 ui.status(_("nothing changed\n"))
1555 return 1
1555 return 1
1556
1556
1557 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1557 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1558
1558
1559 @command('config|showconfig|debugconfig',
1559 @command('config|showconfig|debugconfig',
1560 [('u', 'untrusted', None, _('show untrusted configuration options')),
1560 [('u', 'untrusted', None, _('show untrusted configuration options')),
1561 ('e', 'edit', None, _('edit user config')),
1561 ('e', 'edit', None, _('edit user config')),
1562 ('l', 'local', None, _('edit repository config')),
1562 ('l', 'local', None, _('edit repository config')),
1563 ('g', 'global', None, _('edit global config'))],
1563 ('g', 'global', None, _('edit global config'))],
1564 _('[-u] [NAME]...'),
1564 _('[-u] [NAME]...'),
1565 optionalrepo=True)
1565 optionalrepo=True)
1566 def config(ui, repo, *values, **opts):
1566 def config(ui, repo, *values, **opts):
1567 """show combined config settings from all hgrc files
1567 """show combined config settings from all hgrc files
1568
1568
1569 With no arguments, print names and values of all config items.
1569 With no arguments, print names and values of all config items.
1570
1570
1571 With one argument of the form section.name, print just the value
1571 With one argument of the form section.name, print just the value
1572 of that config item.
1572 of that config item.
1573
1573
1574 With multiple arguments, print names and values of all config
1574 With multiple arguments, print names and values of all config
1575 items with matching section names.
1575 items with matching section names.
1576
1576
1577 With --edit, start an editor on the user-level config file. With
1577 With --edit, start an editor on the user-level config file. With
1578 --global, edit the system-wide config file. With --local, edit the
1578 --global, edit the system-wide config file. With --local, edit the
1579 repository-level config file.
1579 repository-level config file.
1580
1580
1581 With --debug, the source (filename and line number) is printed
1581 With --debug, the source (filename and line number) is printed
1582 for each config item.
1582 for each config item.
1583
1583
1584 See :hg:`help config` for more information about config files.
1584 See :hg:`help config` for more information about config files.
1585
1585
1586 Returns 0 on success, 1 if NAME does not exist.
1586 Returns 0 on success, 1 if NAME does not exist.
1587
1587
1588 """
1588 """
1589
1589
1590 if opts.get('edit') or opts.get('local') or opts.get('global'):
1590 if opts.get('edit') or opts.get('local') or opts.get('global'):
1591 if opts.get('local') and opts.get('global'):
1591 if opts.get('local') and opts.get('global'):
1592 raise util.Abort(_("can't use --local and --global together"))
1592 raise util.Abort(_("can't use --local and --global together"))
1593
1593
1594 if opts.get('local'):
1594 if opts.get('local'):
1595 if not repo:
1595 if not repo:
1596 raise util.Abort(_("can't use --local outside a repository"))
1596 raise util.Abort(_("can't use --local outside a repository"))
1597 paths = [repo.join('hgrc')]
1597 paths = [repo.join('hgrc')]
1598 elif opts.get('global'):
1598 elif opts.get('global'):
1599 paths = scmutil.systemrcpath()
1599 paths = scmutil.systemrcpath()
1600 else:
1600 else:
1601 paths = scmutil.userrcpath()
1601 paths = scmutil.userrcpath()
1602
1602
1603 for f in paths:
1603 for f in paths:
1604 if os.path.exists(f):
1604 if os.path.exists(f):
1605 break
1605 break
1606 else:
1606 else:
1607 if opts.get('global'):
1607 if opts.get('global'):
1608 samplehgrc = uimod.samplehgrcs['global']
1608 samplehgrc = uimod.samplehgrcs['global']
1609 elif opts.get('local'):
1609 elif opts.get('local'):
1610 samplehgrc = uimod.samplehgrcs['local']
1610 samplehgrc = uimod.samplehgrcs['local']
1611 else:
1611 else:
1612 samplehgrc = uimod.samplehgrcs['user']
1612 samplehgrc = uimod.samplehgrcs['user']
1613
1613
1614 f = paths[0]
1614 f = paths[0]
1615 fp = open(f, "w")
1615 fp = open(f, "w")
1616 fp.write(samplehgrc)
1616 fp.write(samplehgrc)
1617 fp.close()
1617 fp.close()
1618
1618
1619 editor = ui.geteditor()
1619 editor = ui.geteditor()
1620 ui.system("%s \"%s\"" % (editor, f),
1620 ui.system("%s \"%s\"" % (editor, f),
1621 onerr=util.Abort, errprefix=_("edit failed"))
1621 onerr=util.Abort, errprefix=_("edit failed"))
1622 return
1622 return
1623
1623
1624 for f in scmutil.rcpath():
1624 for f in scmutil.rcpath():
1625 ui.debug('read config from: %s\n' % f)
1625 ui.debug('read config from: %s\n' % f)
1626 untrusted = bool(opts.get('untrusted'))
1626 untrusted = bool(opts.get('untrusted'))
1627 if values:
1627 if values:
1628 sections = [v for v in values if '.' not in v]
1628 sections = [v for v in values if '.' not in v]
1629 items = [v for v in values if '.' in v]
1629 items = [v for v in values if '.' in v]
1630 if len(items) > 1 or items and sections:
1630 if len(items) > 1 or items and sections:
1631 raise util.Abort(_('only one config item permitted'))
1631 raise util.Abort(_('only one config item permitted'))
1632 matched = False
1632 matched = False
1633 for section, name, value in ui.walkconfig(untrusted=untrusted):
1633 for section, name, value in ui.walkconfig(untrusted=untrusted):
1634 value = str(value).replace('\n', '\\n')
1634 value = str(value).replace('\n', '\\n')
1635 sectname = section + '.' + name
1635 sectname = section + '.' + name
1636 if values:
1636 if values:
1637 for v in values:
1637 for v in values:
1638 if v == section:
1638 if v == section:
1639 ui.debug('%s: ' %
1639 ui.debug('%s: ' %
1640 ui.configsource(section, name, untrusted))
1640 ui.configsource(section, name, untrusted))
1641 ui.write('%s=%s\n' % (sectname, value))
1641 ui.write('%s=%s\n' % (sectname, value))
1642 matched = True
1642 matched = True
1643 elif v == sectname:
1643 elif v == sectname:
1644 ui.debug('%s: ' %
1644 ui.debug('%s: ' %
1645 ui.configsource(section, name, untrusted))
1645 ui.configsource(section, name, untrusted))
1646 ui.write(value, '\n')
1646 ui.write(value, '\n')
1647 matched = True
1647 matched = True
1648 else:
1648 else:
1649 ui.debug('%s: ' %
1649 ui.debug('%s: ' %
1650 ui.configsource(section, name, untrusted))
1650 ui.configsource(section, name, untrusted))
1651 ui.write('%s=%s\n' % (sectname, value))
1651 ui.write('%s=%s\n' % (sectname, value))
1652 matched = True
1652 matched = True
1653 if matched:
1653 if matched:
1654 return 0
1654 return 0
1655 return 1
1655 return 1
1656
1656
1657 @command('copy|cp',
1657 @command('copy|cp',
1658 [('A', 'after', None, _('record a copy that has already occurred')),
1658 [('A', 'after', None, _('record a copy that has already occurred')),
1659 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1659 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1660 ] + walkopts + dryrunopts,
1660 ] + walkopts + dryrunopts,
1661 _('[OPTION]... [SOURCE]... DEST'))
1661 _('[OPTION]... [SOURCE]... DEST'))
1662 def copy(ui, repo, *pats, **opts):
1662 def copy(ui, repo, *pats, **opts):
1663 """mark files as copied for the next commit
1663 """mark files as copied for the next commit
1664
1664
1665 Mark dest as having copies of source files. If dest is a
1665 Mark dest as having copies of source files. If dest is a
1666 directory, copies are put in that directory. If dest is a file,
1666 directory, copies are put in that directory. If dest is a file,
1667 the source must be a single file.
1667 the source must be a single file.
1668
1668
1669 By default, this command copies the contents of files as they
1669 By default, this command copies the contents of files as they
1670 exist in the working directory. If invoked with -A/--after, the
1670 exist in the working directory. If invoked with -A/--after, the
1671 operation is recorded, but no copying is performed.
1671 operation is recorded, but no copying is performed.
1672
1672
1673 This command takes effect with the next commit. To undo a copy
1673 This command takes effect with the next commit. To undo a copy
1674 before that, see :hg:`revert`.
1674 before that, see :hg:`revert`.
1675
1675
1676 Returns 0 on success, 1 if errors are encountered.
1676 Returns 0 on success, 1 if errors are encountered.
1677 """
1677 """
1678 wlock = repo.wlock(False)
1678 wlock = repo.wlock(False)
1679 try:
1679 try:
1680 return cmdutil.copy(ui, repo, pats, opts)
1680 return cmdutil.copy(ui, repo, pats, opts)
1681 finally:
1681 finally:
1682 wlock.release()
1682 wlock.release()
1683
1683
1684 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1684 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1685 def debugancestor(ui, repo, *args):
1685 def debugancestor(ui, repo, *args):
1686 """find the ancestor revision of two revisions in a given index"""
1686 """find the ancestor revision of two revisions in a given index"""
1687 if len(args) == 3:
1687 if len(args) == 3:
1688 index, rev1, rev2 = args
1688 index, rev1, rev2 = args
1689 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1689 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1690 lookup = r.lookup
1690 lookup = r.lookup
1691 elif len(args) == 2:
1691 elif len(args) == 2:
1692 if not repo:
1692 if not repo:
1693 raise util.Abort(_("there is no Mercurial repository here "
1693 raise util.Abort(_("there is no Mercurial repository here "
1694 "(.hg not found)"))
1694 "(.hg not found)"))
1695 rev1, rev2 = args
1695 rev1, rev2 = args
1696 r = repo.changelog
1696 r = repo.changelog
1697 lookup = repo.lookup
1697 lookup = repo.lookup
1698 else:
1698 else:
1699 raise util.Abort(_('either two or three arguments required'))
1699 raise util.Abort(_('either two or three arguments required'))
1700 a = r.ancestor(lookup(rev1), lookup(rev2))
1700 a = r.ancestor(lookup(rev1), lookup(rev2))
1701 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1701 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1702
1702
1703 @command('debugbuilddag',
1703 @command('debugbuilddag',
1704 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1704 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1705 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1705 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1706 ('n', 'new-file', None, _('add new file at each rev'))],
1706 ('n', 'new-file', None, _('add new file at each rev'))],
1707 _('[OPTION]... [TEXT]'))
1707 _('[OPTION]... [TEXT]'))
1708 def debugbuilddag(ui, repo, text=None,
1708 def debugbuilddag(ui, repo, text=None,
1709 mergeable_file=False,
1709 mergeable_file=False,
1710 overwritten_file=False,
1710 overwritten_file=False,
1711 new_file=False):
1711 new_file=False):
1712 """builds a repo with a given DAG from scratch in the current empty repo
1712 """builds a repo with a given DAG from scratch in the current empty repo
1713
1713
1714 The description of the DAG is read from stdin if not given on the
1714 The description of the DAG is read from stdin if not given on the
1715 command line.
1715 command line.
1716
1716
1717 Elements:
1717 Elements:
1718
1718
1719 - "+n" is a linear run of n nodes based on the current default parent
1719 - "+n" is a linear run of n nodes based on the current default parent
1720 - "." is a single node based on the current default parent
1720 - "." is a single node based on the current default parent
1721 - "$" resets the default parent to null (implied at the start);
1721 - "$" resets the default parent to null (implied at the start);
1722 otherwise the default parent is always the last node created
1722 otherwise the default parent is always the last node created
1723 - "<p" sets the default parent to the backref p
1723 - "<p" sets the default parent to the backref p
1724 - "*p" is a fork at parent p, which is a backref
1724 - "*p" is a fork at parent p, which is a backref
1725 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1725 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1726 - "/p2" is a merge of the preceding node and p2
1726 - "/p2" is a merge of the preceding node and p2
1727 - ":tag" defines a local tag for the preceding node
1727 - ":tag" defines a local tag for the preceding node
1728 - "@branch" sets the named branch for subsequent nodes
1728 - "@branch" sets the named branch for subsequent nodes
1729 - "#...\\n" is a comment up to the end of the line
1729 - "#...\\n" is a comment up to the end of the line
1730
1730
1731 Whitespace between the above elements is ignored.
1731 Whitespace between the above elements is ignored.
1732
1732
1733 A backref is either
1733 A backref is either
1734
1734
1735 - a number n, which references the node curr-n, where curr is the current
1735 - a number n, which references the node curr-n, where curr is the current
1736 node, or
1736 node, or
1737 - the name of a local tag you placed earlier using ":tag", or
1737 - the name of a local tag you placed earlier using ":tag", or
1738 - empty to denote the default parent.
1738 - empty to denote the default parent.
1739
1739
1740 All string valued-elements are either strictly alphanumeric, or must
1740 All string valued-elements are either strictly alphanumeric, or must
1741 be enclosed in double quotes ("..."), with "\\" as escape character.
1741 be enclosed in double quotes ("..."), with "\\" as escape character.
1742 """
1742 """
1743
1743
1744 if text is None:
1744 if text is None:
1745 ui.status(_("reading DAG from stdin\n"))
1745 ui.status(_("reading DAG from stdin\n"))
1746 text = ui.fin.read()
1746 text = ui.fin.read()
1747
1747
1748 cl = repo.changelog
1748 cl = repo.changelog
1749 if len(cl) > 0:
1749 if len(cl) > 0:
1750 raise util.Abort(_('repository is not empty'))
1750 raise util.Abort(_('repository is not empty'))
1751
1751
1752 # determine number of revs in DAG
1752 # determine number of revs in DAG
1753 total = 0
1753 total = 0
1754 for type, data in dagparser.parsedag(text):
1754 for type, data in dagparser.parsedag(text):
1755 if type == 'n':
1755 if type == 'n':
1756 total += 1
1756 total += 1
1757
1757
1758 if mergeable_file:
1758 if mergeable_file:
1759 linesperrev = 2
1759 linesperrev = 2
1760 # make a file with k lines per rev
1760 # make a file with k lines per rev
1761 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1761 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1762 initialmergedlines.append("")
1762 initialmergedlines.append("")
1763
1763
1764 tags = []
1764 tags = []
1765
1765
1766 lock = tr = None
1766 lock = tr = None
1767 try:
1767 try:
1768 lock = repo.lock()
1768 lock = repo.lock()
1769 tr = repo.transaction("builddag")
1769 tr = repo.transaction("builddag")
1770
1770
1771 at = -1
1771 at = -1
1772 atbranch = 'default'
1772 atbranch = 'default'
1773 nodeids = []
1773 nodeids = []
1774 id = 0
1774 id = 0
1775 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1775 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1776 for type, data in dagparser.parsedag(text):
1776 for type, data in dagparser.parsedag(text):
1777 if type == 'n':
1777 if type == 'n':
1778 ui.note(('node %s\n' % str(data)))
1778 ui.note(('node %s\n' % str(data)))
1779 id, ps = data
1779 id, ps = data
1780
1780
1781 files = []
1781 files = []
1782 fctxs = {}
1782 fctxs = {}
1783
1783
1784 p2 = None
1784 p2 = None
1785 if mergeable_file:
1785 if mergeable_file:
1786 fn = "mf"
1786 fn = "mf"
1787 p1 = repo[ps[0]]
1787 p1 = repo[ps[0]]
1788 if len(ps) > 1:
1788 if len(ps) > 1:
1789 p2 = repo[ps[1]]
1789 p2 = repo[ps[1]]
1790 pa = p1.ancestor(p2)
1790 pa = p1.ancestor(p2)
1791 base, local, other = [x[fn].data() for x in (pa, p1,
1791 base, local, other = [x[fn].data() for x in (pa, p1,
1792 p2)]
1792 p2)]
1793 m3 = simplemerge.Merge3Text(base, local, other)
1793 m3 = simplemerge.Merge3Text(base, local, other)
1794 ml = [l.strip() for l in m3.merge_lines()]
1794 ml = [l.strip() for l in m3.merge_lines()]
1795 ml.append("")
1795 ml.append("")
1796 elif at > 0:
1796 elif at > 0:
1797 ml = p1[fn].data().split("\n")
1797 ml = p1[fn].data().split("\n")
1798 else:
1798 else:
1799 ml = initialmergedlines
1799 ml = initialmergedlines
1800 ml[id * linesperrev] += " r%i" % id
1800 ml[id * linesperrev] += " r%i" % id
1801 mergedtext = "\n".join(ml)
1801 mergedtext = "\n".join(ml)
1802 files.append(fn)
1802 files.append(fn)
1803 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
1803 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
1804
1804
1805 if overwritten_file:
1805 if overwritten_file:
1806 fn = "of"
1806 fn = "of"
1807 files.append(fn)
1807 files.append(fn)
1808 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1808 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1809
1809
1810 if new_file:
1810 if new_file:
1811 fn = "nf%i" % id
1811 fn = "nf%i" % id
1812 files.append(fn)
1812 files.append(fn)
1813 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1813 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1814 if len(ps) > 1:
1814 if len(ps) > 1:
1815 if not p2:
1815 if not p2:
1816 p2 = repo[ps[1]]
1816 p2 = repo[ps[1]]
1817 for fn in p2:
1817 for fn in p2:
1818 if fn.startswith("nf"):
1818 if fn.startswith("nf"):
1819 files.append(fn)
1819 files.append(fn)
1820 fctxs[fn] = p2[fn]
1820 fctxs[fn] = p2[fn]
1821
1821
1822 def fctxfn(repo, cx, path):
1822 def fctxfn(repo, cx, path):
1823 return fctxs.get(path)
1823 return fctxs.get(path)
1824
1824
1825 if len(ps) == 0 or ps[0] < 0:
1825 if len(ps) == 0 or ps[0] < 0:
1826 pars = [None, None]
1826 pars = [None, None]
1827 elif len(ps) == 1:
1827 elif len(ps) == 1:
1828 pars = [nodeids[ps[0]], None]
1828 pars = [nodeids[ps[0]], None]
1829 else:
1829 else:
1830 pars = [nodeids[p] for p in ps]
1830 pars = [nodeids[p] for p in ps]
1831 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1831 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1832 date=(id, 0),
1832 date=(id, 0),
1833 user="debugbuilddag",
1833 user="debugbuilddag",
1834 extra={'branch': atbranch})
1834 extra={'branch': atbranch})
1835 nodeid = repo.commitctx(cx)
1835 nodeid = repo.commitctx(cx)
1836 nodeids.append(nodeid)
1836 nodeids.append(nodeid)
1837 at = id
1837 at = id
1838 elif type == 'l':
1838 elif type == 'l':
1839 id, name = data
1839 id, name = data
1840 ui.note(('tag %s\n' % name))
1840 ui.note(('tag %s\n' % name))
1841 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1841 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1842 elif type == 'a':
1842 elif type == 'a':
1843 ui.note(('branch %s\n' % data))
1843 ui.note(('branch %s\n' % data))
1844 atbranch = data
1844 atbranch = data
1845 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1845 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1846 tr.close()
1846 tr.close()
1847
1847
1848 if tags:
1848 if tags:
1849 repo.vfs.write("localtags", "".join(tags))
1849 repo.vfs.write("localtags", "".join(tags))
1850 finally:
1850 finally:
1851 ui.progress(_('building'), None)
1851 ui.progress(_('building'), None)
1852 release(tr, lock)
1852 release(tr, lock)
1853
1853
1854 @command('debugbundle',
1854 @command('debugbundle',
1855 [('a', 'all', None, _('show all details'))],
1855 [('a', 'all', None, _('show all details'))],
1856 _('FILE'),
1856 _('FILE'),
1857 norepo=True)
1857 norepo=True)
1858 def debugbundle(ui, bundlepath, all=None, **opts):
1858 def debugbundle(ui, bundlepath, all=None, **opts):
1859 """lists the contents of a bundle"""
1859 """lists the contents of a bundle"""
1860 f = hg.openpath(ui, bundlepath)
1860 f = hg.openpath(ui, bundlepath)
1861 try:
1861 try:
1862 gen = exchange.readbundle(ui, f, bundlepath)
1862 gen = exchange.readbundle(ui, f, bundlepath)
1863 if isinstance(gen, bundle2.unbundle20):
1863 if isinstance(gen, bundle2.unbundle20):
1864 return _debugbundle2(ui, gen, all=all, **opts)
1864 return _debugbundle2(ui, gen, all=all, **opts)
1865 if all:
1865 if all:
1866 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1866 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1867
1867
1868 def showchunks(named):
1868 def showchunks(named):
1869 ui.write("\n%s\n" % named)
1869 ui.write("\n%s\n" % named)
1870 chain = None
1870 chain = None
1871 while True:
1871 while True:
1872 chunkdata = gen.deltachunk(chain)
1872 chunkdata = gen.deltachunk(chain)
1873 if not chunkdata:
1873 if not chunkdata:
1874 break
1874 break
1875 node = chunkdata['node']
1875 node = chunkdata['node']
1876 p1 = chunkdata['p1']
1876 p1 = chunkdata['p1']
1877 p2 = chunkdata['p2']
1877 p2 = chunkdata['p2']
1878 cs = chunkdata['cs']
1878 cs = chunkdata['cs']
1879 deltabase = chunkdata['deltabase']
1879 deltabase = chunkdata['deltabase']
1880 delta = chunkdata['delta']
1880 delta = chunkdata['delta']
1881 ui.write("%s %s %s %s %s %s\n" %
1881 ui.write("%s %s %s %s %s %s\n" %
1882 (hex(node), hex(p1), hex(p2),
1882 (hex(node), hex(p1), hex(p2),
1883 hex(cs), hex(deltabase), len(delta)))
1883 hex(cs), hex(deltabase), len(delta)))
1884 chain = node
1884 chain = node
1885
1885
1886 chunkdata = gen.changelogheader()
1886 chunkdata = gen.changelogheader()
1887 showchunks("changelog")
1887 showchunks("changelog")
1888 chunkdata = gen.manifestheader()
1888 chunkdata = gen.manifestheader()
1889 showchunks("manifest")
1889 showchunks("manifest")
1890 while True:
1890 while True:
1891 chunkdata = gen.filelogheader()
1891 chunkdata = gen.filelogheader()
1892 if not chunkdata:
1892 if not chunkdata:
1893 break
1893 break
1894 fname = chunkdata['filename']
1894 fname = chunkdata['filename']
1895 showchunks(fname)
1895 showchunks(fname)
1896 else:
1896 else:
1897 if isinstance(gen, bundle2.unbundle20):
1897 if isinstance(gen, bundle2.unbundle20):
1898 raise util.Abort(_('use debugbundle2 for this file'))
1898 raise util.Abort(_('use debugbundle2 for this file'))
1899 chunkdata = gen.changelogheader()
1899 chunkdata = gen.changelogheader()
1900 chain = None
1900 chain = None
1901 while True:
1901 while True:
1902 chunkdata = gen.deltachunk(chain)
1902 chunkdata = gen.deltachunk(chain)
1903 if not chunkdata:
1903 if not chunkdata:
1904 break
1904 break
1905 node = chunkdata['node']
1905 node = chunkdata['node']
1906 ui.write("%s\n" % hex(node))
1906 ui.write("%s\n" % hex(node))
1907 chain = node
1907 chain = node
1908 finally:
1908 finally:
1909 f.close()
1909 f.close()
1910
1910
1911 def _debugbundle2(ui, gen, **opts):
1911 def _debugbundle2(ui, gen, **opts):
1912 """lists the contents of a bundle2"""
1912 """lists the contents of a bundle2"""
1913 if not isinstance(gen, bundle2.unbundle20):
1913 if not isinstance(gen, bundle2.unbundle20):
1914 raise util.Abort(_('not a bundle2 file'))
1914 raise util.Abort(_('not a bundle2 file'))
1915 ui.write(('Stream params: %s\n' % repr(gen.params)))
1915 ui.write(('Stream params: %s\n' % repr(gen.params)))
1916 for part in gen.iterparts():
1916 for part in gen.iterparts():
1917 ui.write('%s -- %r\n' % (part.type, repr(part.params)))
1917 ui.write('%s -- %r\n' % (part.type, repr(part.params)))
1918 if part.type == 'changegroup':
1918 if part.type == 'changegroup':
1919 version = part.params.get('version', '01')
1919 version = part.params.get('version', '01')
1920 cg = changegroup.packermap[version][1](part, 'UN')
1920 cg = changegroup.packermap[version][1](part, 'UN')
1921 chunkdata = cg.changelogheader()
1921 chunkdata = cg.changelogheader()
1922 chain = None
1922 chain = None
1923 while True:
1923 while True:
1924 chunkdata = cg.deltachunk(chain)
1924 chunkdata = cg.deltachunk(chain)
1925 if not chunkdata:
1925 if not chunkdata:
1926 break
1926 break
1927 node = chunkdata['node']
1927 node = chunkdata['node']
1928 ui.write(" %s\n" % hex(node))
1928 ui.write(" %s\n" % hex(node))
1929 chain = node
1929 chain = node
1930
1930
1931 @command('debugcheckstate', [], '')
1931 @command('debugcheckstate', [], '')
1932 def debugcheckstate(ui, repo):
1932 def debugcheckstate(ui, repo):
1933 """validate the correctness of the current dirstate"""
1933 """validate the correctness of the current dirstate"""
1934 parent1, parent2 = repo.dirstate.parents()
1934 parent1, parent2 = repo.dirstate.parents()
1935 m1 = repo[parent1].manifest()
1935 m1 = repo[parent1].manifest()
1936 m2 = repo[parent2].manifest()
1936 m2 = repo[parent2].manifest()
1937 errors = 0
1937 errors = 0
1938 for f in repo.dirstate:
1938 for f in repo.dirstate:
1939 state = repo.dirstate[f]
1939 state = repo.dirstate[f]
1940 if state in "nr" and f not in m1:
1940 if state in "nr" and f not in m1:
1941 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1941 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1942 errors += 1
1942 errors += 1
1943 if state in "a" and f in m1:
1943 if state in "a" and f in m1:
1944 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1944 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1945 errors += 1
1945 errors += 1
1946 if state in "m" and f not in m1 and f not in m2:
1946 if state in "m" and f not in m1 and f not in m2:
1947 ui.warn(_("%s in state %s, but not in either manifest\n") %
1947 ui.warn(_("%s in state %s, but not in either manifest\n") %
1948 (f, state))
1948 (f, state))
1949 errors += 1
1949 errors += 1
1950 for f in m1:
1950 for f in m1:
1951 state = repo.dirstate[f]
1951 state = repo.dirstate[f]
1952 if state not in "nrm":
1952 if state not in "nrm":
1953 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1953 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1954 errors += 1
1954 errors += 1
1955 if errors:
1955 if errors:
1956 error = _(".hg/dirstate inconsistent with current parent's manifest")
1956 error = _(".hg/dirstate inconsistent with current parent's manifest")
1957 raise util.Abort(error)
1957 raise util.Abort(error)
1958
1958
1959 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1959 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1960 def debugcommands(ui, cmd='', *args):
1960 def debugcommands(ui, cmd='', *args):
1961 """list all available commands and options"""
1961 """list all available commands and options"""
1962 for cmd, vals in sorted(table.iteritems()):
1962 for cmd, vals in sorted(table.iteritems()):
1963 cmd = cmd.split('|')[0].strip('^')
1963 cmd = cmd.split('|')[0].strip('^')
1964 opts = ', '.join([i[1] for i in vals[1]])
1964 opts = ', '.join([i[1] for i in vals[1]])
1965 ui.write('%s: %s\n' % (cmd, opts))
1965 ui.write('%s: %s\n' % (cmd, opts))
1966
1966
1967 @command('debugcomplete',
1967 @command('debugcomplete',
1968 [('o', 'options', None, _('show the command options'))],
1968 [('o', 'options', None, _('show the command options'))],
1969 _('[-o] CMD'),
1969 _('[-o] CMD'),
1970 norepo=True)
1970 norepo=True)
1971 def debugcomplete(ui, cmd='', **opts):
1971 def debugcomplete(ui, cmd='', **opts):
1972 """returns the completion list associated with the given command"""
1972 """returns the completion list associated with the given command"""
1973
1973
1974 if opts.get('options'):
1974 if opts.get('options'):
1975 options = []
1975 options = []
1976 otables = [globalopts]
1976 otables = [globalopts]
1977 if cmd:
1977 if cmd:
1978 aliases, entry = cmdutil.findcmd(cmd, table, False)
1978 aliases, entry = cmdutil.findcmd(cmd, table, False)
1979 otables.append(entry[1])
1979 otables.append(entry[1])
1980 for t in otables:
1980 for t in otables:
1981 for o in t:
1981 for o in t:
1982 if "(DEPRECATED)" in o[3]:
1982 if "(DEPRECATED)" in o[3]:
1983 continue
1983 continue
1984 if o[0]:
1984 if o[0]:
1985 options.append('-%s' % o[0])
1985 options.append('-%s' % o[0])
1986 options.append('--%s' % o[1])
1986 options.append('--%s' % o[1])
1987 ui.write("%s\n" % "\n".join(options))
1987 ui.write("%s\n" % "\n".join(options))
1988 return
1988 return
1989
1989
1990 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1990 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1991 if ui.verbose:
1991 if ui.verbose:
1992 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1992 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1993 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1993 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1994
1994
1995 @command('debugdag',
1995 @command('debugdag',
1996 [('t', 'tags', None, _('use tags as labels')),
1996 [('t', 'tags', None, _('use tags as labels')),
1997 ('b', 'branches', None, _('annotate with branch names')),
1997 ('b', 'branches', None, _('annotate with branch names')),
1998 ('', 'dots', None, _('use dots for runs')),
1998 ('', 'dots', None, _('use dots for runs')),
1999 ('s', 'spaces', None, _('separate elements by spaces'))],
1999 ('s', 'spaces', None, _('separate elements by spaces'))],
2000 _('[OPTION]... [FILE [REV]...]'),
2000 _('[OPTION]... [FILE [REV]...]'),
2001 optionalrepo=True)
2001 optionalrepo=True)
2002 def debugdag(ui, repo, file_=None, *revs, **opts):
2002 def debugdag(ui, repo, file_=None, *revs, **opts):
2003 """format the changelog or an index DAG as a concise textual description
2003 """format the changelog or an index DAG as a concise textual description
2004
2004
2005 If you pass a revlog index, the revlog's DAG is emitted. If you list
2005 If you pass a revlog index, the revlog's DAG is emitted. If you list
2006 revision numbers, they get labeled in the output as rN.
2006 revision numbers, they get labeled in the output as rN.
2007
2007
2008 Otherwise, the changelog DAG of the current repo is emitted.
2008 Otherwise, the changelog DAG of the current repo is emitted.
2009 """
2009 """
2010 spaces = opts.get('spaces')
2010 spaces = opts.get('spaces')
2011 dots = opts.get('dots')
2011 dots = opts.get('dots')
2012 if file_:
2012 if file_:
2013 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2013 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2014 revs = set((int(r) for r in revs))
2014 revs = set((int(r) for r in revs))
2015 def events():
2015 def events():
2016 for r in rlog:
2016 for r in rlog:
2017 yield 'n', (r, list(p for p in rlog.parentrevs(r)
2017 yield 'n', (r, list(p for p in rlog.parentrevs(r)
2018 if p != -1))
2018 if p != -1))
2019 if r in revs:
2019 if r in revs:
2020 yield 'l', (r, "r%i" % r)
2020 yield 'l', (r, "r%i" % r)
2021 elif repo:
2021 elif repo:
2022 cl = repo.changelog
2022 cl = repo.changelog
2023 tags = opts.get('tags')
2023 tags = opts.get('tags')
2024 branches = opts.get('branches')
2024 branches = opts.get('branches')
2025 if tags:
2025 if tags:
2026 labels = {}
2026 labels = {}
2027 for l, n in repo.tags().items():
2027 for l, n in repo.tags().items():
2028 labels.setdefault(cl.rev(n), []).append(l)
2028 labels.setdefault(cl.rev(n), []).append(l)
2029 def events():
2029 def events():
2030 b = "default"
2030 b = "default"
2031 for r in cl:
2031 for r in cl:
2032 if branches:
2032 if branches:
2033 newb = cl.read(cl.node(r))[5]['branch']
2033 newb = cl.read(cl.node(r))[5]['branch']
2034 if newb != b:
2034 if newb != b:
2035 yield 'a', newb
2035 yield 'a', newb
2036 b = newb
2036 b = newb
2037 yield 'n', (r, list(p for p in cl.parentrevs(r)
2037 yield 'n', (r, list(p for p in cl.parentrevs(r)
2038 if p != -1))
2038 if p != -1))
2039 if tags:
2039 if tags:
2040 ls = labels.get(r)
2040 ls = labels.get(r)
2041 if ls:
2041 if ls:
2042 for l in ls:
2042 for l in ls:
2043 yield 'l', (r, l)
2043 yield 'l', (r, l)
2044 else:
2044 else:
2045 raise util.Abort(_('need repo for changelog dag'))
2045 raise util.Abort(_('need repo for changelog dag'))
2046
2046
2047 for line in dagparser.dagtextlines(events(),
2047 for line in dagparser.dagtextlines(events(),
2048 addspaces=spaces,
2048 addspaces=spaces,
2049 wraplabels=True,
2049 wraplabels=True,
2050 wrapannotations=True,
2050 wrapannotations=True,
2051 wrapnonlinear=dots,
2051 wrapnonlinear=dots,
2052 usedots=dots,
2052 usedots=dots,
2053 maxlinewidth=70):
2053 maxlinewidth=70):
2054 ui.write(line)
2054 ui.write(line)
2055 ui.write("\n")
2055 ui.write("\n")
2056
2056
2057 @command('debugdata',
2057 @command('debugdata',
2058 [('c', 'changelog', False, _('open changelog')),
2058 [('c', 'changelog', False, _('open changelog')),
2059 ('m', 'manifest', False, _('open manifest'))],
2059 ('m', 'manifest', False, _('open manifest')),
2060 ('', 'dir', False, _('open directory manifest'))],
2060 _('-c|-m|FILE REV'))
2061 _('-c|-m|FILE REV'))
2061 def debugdata(ui, repo, file_, rev=None, **opts):
2062 def debugdata(ui, repo, file_, rev=None, **opts):
2062 """dump the contents of a data file revision"""
2063 """dump the contents of a data file revision"""
2063 if opts.get('changelog') or opts.get('manifest'):
2064 if opts.get('changelog') or opts.get('manifest'):
2064 file_, rev = None, file_
2065 file_, rev = None, file_
2065 elif rev is None:
2066 elif rev is None:
2066 raise error.CommandError('debugdata', _('invalid arguments'))
2067 raise error.CommandError('debugdata', _('invalid arguments'))
2067 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
2068 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
2068 try:
2069 try:
2069 ui.write(r.revision(r.lookup(rev)))
2070 ui.write(r.revision(r.lookup(rev)))
2070 except KeyError:
2071 except KeyError:
2071 raise util.Abort(_('invalid revision identifier %s') % rev)
2072 raise util.Abort(_('invalid revision identifier %s') % rev)
2072
2073
2073 @command('debugdate',
2074 @command('debugdate',
2074 [('e', 'extended', None, _('try extended date formats'))],
2075 [('e', 'extended', None, _('try extended date formats'))],
2075 _('[-e] DATE [RANGE]'),
2076 _('[-e] DATE [RANGE]'),
2076 norepo=True, optionalrepo=True)
2077 norepo=True, optionalrepo=True)
2077 def debugdate(ui, date, range=None, **opts):
2078 def debugdate(ui, date, range=None, **opts):
2078 """parse and display a date"""
2079 """parse and display a date"""
2079 if opts["extended"]:
2080 if opts["extended"]:
2080 d = util.parsedate(date, util.extendeddateformats)
2081 d = util.parsedate(date, util.extendeddateformats)
2081 else:
2082 else:
2082 d = util.parsedate(date)
2083 d = util.parsedate(date)
2083 ui.write(("internal: %s %s\n") % d)
2084 ui.write(("internal: %s %s\n") % d)
2084 ui.write(("standard: %s\n") % util.datestr(d))
2085 ui.write(("standard: %s\n") % util.datestr(d))
2085 if range:
2086 if range:
2086 m = util.matchdate(range)
2087 m = util.matchdate(range)
2087 ui.write(("match: %s\n") % m(d[0]))
2088 ui.write(("match: %s\n") % m(d[0]))
2088
2089
2089 @command('debugdiscovery',
2090 @command('debugdiscovery',
2090 [('', 'old', None, _('use old-style discovery')),
2091 [('', 'old', None, _('use old-style discovery')),
2091 ('', 'nonheads', None,
2092 ('', 'nonheads', None,
2092 _('use old-style discovery with non-heads included')),
2093 _('use old-style discovery with non-heads included')),
2093 ] + remoteopts,
2094 ] + remoteopts,
2094 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
2095 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
2095 def debugdiscovery(ui, repo, remoteurl="default", **opts):
2096 def debugdiscovery(ui, repo, remoteurl="default", **opts):
2096 """runs the changeset discovery protocol in isolation"""
2097 """runs the changeset discovery protocol in isolation"""
2097 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
2098 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
2098 opts.get('branch'))
2099 opts.get('branch'))
2099 remote = hg.peer(repo, opts, remoteurl)
2100 remote = hg.peer(repo, opts, remoteurl)
2100 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
2101 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
2101
2102
2102 # make sure tests are repeatable
2103 # make sure tests are repeatable
2103 random.seed(12323)
2104 random.seed(12323)
2104
2105
2105 def doit(localheads, remoteheads, remote=remote):
2106 def doit(localheads, remoteheads, remote=remote):
2106 if opts.get('old'):
2107 if opts.get('old'):
2107 if localheads:
2108 if localheads:
2108 raise util.Abort('cannot use localheads with old style '
2109 raise util.Abort('cannot use localheads with old style '
2109 'discovery')
2110 'discovery')
2110 if not util.safehasattr(remote, 'branches'):
2111 if not util.safehasattr(remote, 'branches'):
2111 # enable in-client legacy support
2112 # enable in-client legacy support
2112 remote = localrepo.locallegacypeer(remote.local())
2113 remote = localrepo.locallegacypeer(remote.local())
2113 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2114 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2114 force=True)
2115 force=True)
2115 common = set(common)
2116 common = set(common)
2116 if not opts.get('nonheads'):
2117 if not opts.get('nonheads'):
2117 ui.write(("unpruned common: %s\n") %
2118 ui.write(("unpruned common: %s\n") %
2118 " ".join(sorted(short(n) for n in common)))
2119 " ".join(sorted(short(n) for n in common)))
2119 dag = dagutil.revlogdag(repo.changelog)
2120 dag = dagutil.revlogdag(repo.changelog)
2120 all = dag.ancestorset(dag.internalizeall(common))
2121 all = dag.ancestorset(dag.internalizeall(common))
2121 common = dag.externalizeall(dag.headsetofconnecteds(all))
2122 common = dag.externalizeall(dag.headsetofconnecteds(all))
2122 else:
2123 else:
2123 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2124 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2124 common = set(common)
2125 common = set(common)
2125 rheads = set(hds)
2126 rheads = set(hds)
2126 lheads = set(repo.heads())
2127 lheads = set(repo.heads())
2127 ui.write(("common heads: %s\n") %
2128 ui.write(("common heads: %s\n") %
2128 " ".join(sorted(short(n) for n in common)))
2129 " ".join(sorted(short(n) for n in common)))
2129 if lheads <= common:
2130 if lheads <= common:
2130 ui.write(("local is subset\n"))
2131 ui.write(("local is subset\n"))
2131 elif rheads <= common:
2132 elif rheads <= common:
2132 ui.write(("remote is subset\n"))
2133 ui.write(("remote is subset\n"))
2133
2134
2134 serverlogs = opts.get('serverlog')
2135 serverlogs = opts.get('serverlog')
2135 if serverlogs:
2136 if serverlogs:
2136 for filename in serverlogs:
2137 for filename in serverlogs:
2137 logfile = open(filename, 'r')
2138 logfile = open(filename, 'r')
2138 try:
2139 try:
2139 line = logfile.readline()
2140 line = logfile.readline()
2140 while line:
2141 while line:
2141 parts = line.strip().split(';')
2142 parts = line.strip().split(';')
2142 op = parts[1]
2143 op = parts[1]
2143 if op == 'cg':
2144 if op == 'cg':
2144 pass
2145 pass
2145 elif op == 'cgss':
2146 elif op == 'cgss':
2146 doit(parts[2].split(' '), parts[3].split(' '))
2147 doit(parts[2].split(' '), parts[3].split(' '))
2147 elif op == 'unb':
2148 elif op == 'unb':
2148 doit(parts[3].split(' '), parts[2].split(' '))
2149 doit(parts[3].split(' '), parts[2].split(' '))
2149 line = logfile.readline()
2150 line = logfile.readline()
2150 finally:
2151 finally:
2151 logfile.close()
2152 logfile.close()
2152
2153
2153 else:
2154 else:
2154 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2155 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2155 opts.get('remote_head'))
2156 opts.get('remote_head'))
2156 localrevs = opts.get('local_head')
2157 localrevs = opts.get('local_head')
2157 doit(localrevs, remoterevs)
2158 doit(localrevs, remoterevs)
2158
2159
2159 @command('debugfileset',
2160 @command('debugfileset',
2160 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2161 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2161 _('[-r REV] FILESPEC'))
2162 _('[-r REV] FILESPEC'))
2162 def debugfileset(ui, repo, expr, **opts):
2163 def debugfileset(ui, repo, expr, **opts):
2163 '''parse and apply a fileset specification'''
2164 '''parse and apply a fileset specification'''
2164 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2165 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2165 if ui.verbose:
2166 if ui.verbose:
2166 tree = fileset.parse(expr)[0]
2167 tree = fileset.parse(expr)[0]
2167 ui.note(tree, "\n")
2168 ui.note(tree, "\n")
2168
2169
2169 for f in ctx.getfileset(expr):
2170 for f in ctx.getfileset(expr):
2170 ui.write("%s\n" % f)
2171 ui.write("%s\n" % f)
2171
2172
2172 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2173 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2173 def debugfsinfo(ui, path="."):
2174 def debugfsinfo(ui, path="."):
2174 """show information detected about current filesystem"""
2175 """show information detected about current filesystem"""
2175 util.writefile('.debugfsinfo', '')
2176 util.writefile('.debugfsinfo', '')
2176 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2177 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2177 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2178 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2178 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2179 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2179 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2180 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2180 and 'yes' or 'no'))
2181 and 'yes' or 'no'))
2181 os.unlink('.debugfsinfo')
2182 os.unlink('.debugfsinfo')
2182
2183
2183 @command('debuggetbundle',
2184 @command('debuggetbundle',
2184 [('H', 'head', [], _('id of head node'), _('ID')),
2185 [('H', 'head', [], _('id of head node'), _('ID')),
2185 ('C', 'common', [], _('id of common node'), _('ID')),
2186 ('C', 'common', [], _('id of common node'), _('ID')),
2186 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2187 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2187 _('REPO FILE [-H|-C ID]...'),
2188 _('REPO FILE [-H|-C ID]...'),
2188 norepo=True)
2189 norepo=True)
2189 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2190 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2190 """retrieves a bundle from a repo
2191 """retrieves a bundle from a repo
2191
2192
2192 Every ID must be a full-length hex node id string. Saves the bundle to the
2193 Every ID must be a full-length hex node id string. Saves the bundle to the
2193 given file.
2194 given file.
2194 """
2195 """
2195 repo = hg.peer(ui, opts, repopath)
2196 repo = hg.peer(ui, opts, repopath)
2196 if not repo.capable('getbundle'):
2197 if not repo.capable('getbundle'):
2197 raise util.Abort("getbundle() not supported by target repository")
2198 raise util.Abort("getbundle() not supported by target repository")
2198 args = {}
2199 args = {}
2199 if common:
2200 if common:
2200 args['common'] = [bin(s) for s in common]
2201 args['common'] = [bin(s) for s in common]
2201 if head:
2202 if head:
2202 args['heads'] = [bin(s) for s in head]
2203 args['heads'] = [bin(s) for s in head]
2203 # TODO: get desired bundlecaps from command line.
2204 # TODO: get desired bundlecaps from command line.
2204 args['bundlecaps'] = None
2205 args['bundlecaps'] = None
2205 bundle = repo.getbundle('debug', **args)
2206 bundle = repo.getbundle('debug', **args)
2206
2207
2207 bundletype = opts.get('type', 'bzip2').lower()
2208 bundletype = opts.get('type', 'bzip2').lower()
2208 btypes = {'none': 'HG10UN',
2209 btypes = {'none': 'HG10UN',
2209 'bzip2': 'HG10BZ',
2210 'bzip2': 'HG10BZ',
2210 'gzip': 'HG10GZ',
2211 'gzip': 'HG10GZ',
2211 'bundle2': 'HG20'}
2212 'bundle2': 'HG20'}
2212 bundletype = btypes.get(bundletype)
2213 bundletype = btypes.get(bundletype)
2213 if bundletype not in changegroup.bundletypes:
2214 if bundletype not in changegroup.bundletypes:
2214 raise util.Abort(_('unknown bundle type specified with --type'))
2215 raise util.Abort(_('unknown bundle type specified with --type'))
2215 changegroup.writebundle(ui, bundle, bundlepath, bundletype)
2216 changegroup.writebundle(ui, bundle, bundlepath, bundletype)
2216
2217
2217 @command('debugignore', [], '')
2218 @command('debugignore', [], '')
2218 def debugignore(ui, repo, *values, **opts):
2219 def debugignore(ui, repo, *values, **opts):
2219 """display the combined ignore pattern"""
2220 """display the combined ignore pattern"""
2220 ignore = repo.dirstate._ignore
2221 ignore = repo.dirstate._ignore
2221 includepat = getattr(ignore, 'includepat', None)
2222 includepat = getattr(ignore, 'includepat', None)
2222 if includepat is not None:
2223 if includepat is not None:
2223 ui.write("%s\n" % includepat)
2224 ui.write("%s\n" % includepat)
2224 else:
2225 else:
2225 raise util.Abort(_("no ignore patterns found"))
2226 raise util.Abort(_("no ignore patterns found"))
2226
2227
2227 @command('debugindex',
2228 @command('debugindex',
2228 [('c', 'changelog', False, _('open changelog')),
2229 [('c', 'changelog', False, _('open changelog')),
2229 ('m', 'manifest', False, _('open manifest')),
2230 ('m', 'manifest', False, _('open manifest')),
2231 ('', 'dir', False, _('open directory manifest')),
2230 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2232 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2231 _('[-f FORMAT] -c|-m|FILE'),
2233 _('[-f FORMAT] -c|-m|FILE'),
2232 optionalrepo=True)
2234 optionalrepo=True)
2233 def debugindex(ui, repo, file_=None, **opts):
2235 def debugindex(ui, repo, file_=None, **opts):
2234 """dump the contents of an index file"""
2236 """dump the contents of an index file"""
2235 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2237 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2236 format = opts.get('format', 0)
2238 format = opts.get('format', 0)
2237 if format not in (0, 1):
2239 if format not in (0, 1):
2238 raise util.Abort(_("unknown format %d") % format)
2240 raise util.Abort(_("unknown format %d") % format)
2239
2241
2240 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2242 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2241 if generaldelta:
2243 if generaldelta:
2242 basehdr = ' delta'
2244 basehdr = ' delta'
2243 else:
2245 else:
2244 basehdr = ' base'
2246 basehdr = ' base'
2245
2247
2246 if ui.debugflag:
2248 if ui.debugflag:
2247 shortfn = hex
2249 shortfn = hex
2248 else:
2250 else:
2249 shortfn = short
2251 shortfn = short
2250
2252
2251 # There might not be anything in r, so have a sane default
2253 # There might not be anything in r, so have a sane default
2252 idlen = 12
2254 idlen = 12
2253 for i in r:
2255 for i in r:
2254 idlen = len(shortfn(r.node(i)))
2256 idlen = len(shortfn(r.node(i)))
2255 break
2257 break
2256
2258
2257 if format == 0:
2259 if format == 0:
2258 ui.write(" rev offset length " + basehdr + " linkrev"
2260 ui.write(" rev offset length " + basehdr + " linkrev"
2259 " %s %s p2\n" % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2261 " %s %s p2\n" % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2260 elif format == 1:
2262 elif format == 1:
2261 ui.write(" rev flag offset length"
2263 ui.write(" rev flag offset length"
2262 " size " + basehdr + " link p1 p2"
2264 " size " + basehdr + " link p1 p2"
2263 " %s\n" % "nodeid".rjust(idlen))
2265 " %s\n" % "nodeid".rjust(idlen))
2264
2266
2265 for i in r:
2267 for i in r:
2266 node = r.node(i)
2268 node = r.node(i)
2267 if generaldelta:
2269 if generaldelta:
2268 base = r.deltaparent(i)
2270 base = r.deltaparent(i)
2269 else:
2271 else:
2270 base = r.chainbase(i)
2272 base = r.chainbase(i)
2271 if format == 0:
2273 if format == 0:
2272 try:
2274 try:
2273 pp = r.parents(node)
2275 pp = r.parents(node)
2274 except Exception:
2276 except Exception:
2275 pp = [nullid, nullid]
2277 pp = [nullid, nullid]
2276 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2278 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2277 i, r.start(i), r.length(i), base, r.linkrev(i),
2279 i, r.start(i), r.length(i), base, r.linkrev(i),
2278 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2280 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2279 elif format == 1:
2281 elif format == 1:
2280 pr = r.parentrevs(i)
2282 pr = r.parentrevs(i)
2281 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2283 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2282 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2284 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2283 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2285 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2284
2286
2285 @command('debugindexdot', [], _('FILE'), optionalrepo=True)
2287 @command('debugindexdot', [], _('FILE'), optionalrepo=True)
2286 def debugindexdot(ui, repo, file_):
2288 def debugindexdot(ui, repo, file_):
2287 """dump an index DAG as a graphviz dot file"""
2289 """dump an index DAG as a graphviz dot file"""
2288 r = None
2290 r = None
2289 if repo:
2291 if repo:
2290 filelog = repo.file(file_)
2292 filelog = repo.file(file_)
2291 if len(filelog):
2293 if len(filelog):
2292 r = filelog
2294 r = filelog
2293 if not r:
2295 if not r:
2294 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2296 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2295 ui.write(("digraph G {\n"))
2297 ui.write(("digraph G {\n"))
2296 for i in r:
2298 for i in r:
2297 node = r.node(i)
2299 node = r.node(i)
2298 pp = r.parents(node)
2300 pp = r.parents(node)
2299 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2301 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2300 if pp[1] != nullid:
2302 if pp[1] != nullid:
2301 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2303 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2302 ui.write("}\n")
2304 ui.write("}\n")
2303
2305
2304 @command('debuginstall', [], '', norepo=True)
2306 @command('debuginstall', [], '', norepo=True)
2305 def debuginstall(ui):
2307 def debuginstall(ui):
2306 '''test Mercurial installation
2308 '''test Mercurial installation
2307
2309
2308 Returns 0 on success.
2310 Returns 0 on success.
2309 '''
2311 '''
2310
2312
2311 def writetemp(contents):
2313 def writetemp(contents):
2312 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2314 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2313 f = os.fdopen(fd, "wb")
2315 f = os.fdopen(fd, "wb")
2314 f.write(contents)
2316 f.write(contents)
2315 f.close()
2317 f.close()
2316 return name
2318 return name
2317
2319
2318 problems = 0
2320 problems = 0
2319
2321
2320 # encoding
2322 # encoding
2321 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2323 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2322 try:
2324 try:
2323 encoding.fromlocal("test")
2325 encoding.fromlocal("test")
2324 except util.Abort, inst:
2326 except util.Abort, inst:
2325 ui.write(" %s\n" % inst)
2327 ui.write(" %s\n" % inst)
2326 ui.write(_(" (check that your locale is properly set)\n"))
2328 ui.write(_(" (check that your locale is properly set)\n"))
2327 problems += 1
2329 problems += 1
2328
2330
2329 # Python
2331 # Python
2330 ui.status(_("checking Python executable (%s)\n") % sys.executable)
2332 ui.status(_("checking Python executable (%s)\n") % sys.executable)
2331 ui.status(_("checking Python version (%s)\n")
2333 ui.status(_("checking Python version (%s)\n")
2332 % ("%s.%s.%s" % sys.version_info[:3]))
2334 % ("%s.%s.%s" % sys.version_info[:3]))
2333 ui.status(_("checking Python lib (%s)...\n")
2335 ui.status(_("checking Python lib (%s)...\n")
2334 % os.path.dirname(os.__file__))
2336 % os.path.dirname(os.__file__))
2335
2337
2336 # compiled modules
2338 # compiled modules
2337 ui.status(_("checking installed modules (%s)...\n")
2339 ui.status(_("checking installed modules (%s)...\n")
2338 % os.path.dirname(__file__))
2340 % os.path.dirname(__file__))
2339 try:
2341 try:
2340 import bdiff, mpatch, base85, osutil
2342 import bdiff, mpatch, base85, osutil
2341 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2343 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2342 except Exception, inst:
2344 except Exception, inst:
2343 ui.write(" %s\n" % inst)
2345 ui.write(" %s\n" % inst)
2344 ui.write(_(" One or more extensions could not be found"))
2346 ui.write(_(" One or more extensions could not be found"))
2345 ui.write(_(" (check that you compiled the extensions)\n"))
2347 ui.write(_(" (check that you compiled the extensions)\n"))
2346 problems += 1
2348 problems += 1
2347
2349
2348 # templates
2350 # templates
2349 import templater
2351 import templater
2350 p = templater.templatepaths()
2352 p = templater.templatepaths()
2351 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2353 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2352 if p:
2354 if p:
2353 m = templater.templatepath("map-cmdline.default")
2355 m = templater.templatepath("map-cmdline.default")
2354 if m:
2356 if m:
2355 # template found, check if it is working
2357 # template found, check if it is working
2356 try:
2358 try:
2357 templater.templater(m)
2359 templater.templater(m)
2358 except Exception, inst:
2360 except Exception, inst:
2359 ui.write(" %s\n" % inst)
2361 ui.write(" %s\n" % inst)
2360 p = None
2362 p = None
2361 else:
2363 else:
2362 ui.write(_(" template 'default' not found\n"))
2364 ui.write(_(" template 'default' not found\n"))
2363 p = None
2365 p = None
2364 else:
2366 else:
2365 ui.write(_(" no template directories found\n"))
2367 ui.write(_(" no template directories found\n"))
2366 if not p:
2368 if not p:
2367 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2369 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2368 problems += 1
2370 problems += 1
2369
2371
2370 # editor
2372 # editor
2371 ui.status(_("checking commit editor...\n"))
2373 ui.status(_("checking commit editor...\n"))
2372 editor = ui.geteditor()
2374 editor = ui.geteditor()
2373 editor = util.expandpath(editor)
2375 editor = util.expandpath(editor)
2374 cmdpath = util.findexe(shlex.split(editor)[0])
2376 cmdpath = util.findexe(shlex.split(editor)[0])
2375 if not cmdpath:
2377 if not cmdpath:
2376 if editor == 'vi':
2378 if editor == 'vi':
2377 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2379 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2378 ui.write(_(" (specify a commit editor in your configuration"
2380 ui.write(_(" (specify a commit editor in your configuration"
2379 " file)\n"))
2381 " file)\n"))
2380 else:
2382 else:
2381 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2383 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2382 ui.write(_(" (specify a commit editor in your configuration"
2384 ui.write(_(" (specify a commit editor in your configuration"
2383 " file)\n"))
2385 " file)\n"))
2384 problems += 1
2386 problems += 1
2385
2387
2386 # check username
2388 # check username
2387 ui.status(_("checking username...\n"))
2389 ui.status(_("checking username...\n"))
2388 try:
2390 try:
2389 ui.username()
2391 ui.username()
2390 except util.Abort, e:
2392 except util.Abort, e:
2391 ui.write(" %s\n" % e)
2393 ui.write(" %s\n" % e)
2392 ui.write(_(" (specify a username in your configuration file)\n"))
2394 ui.write(_(" (specify a username in your configuration file)\n"))
2393 problems += 1
2395 problems += 1
2394
2396
2395 if not problems:
2397 if not problems:
2396 ui.status(_("no problems detected\n"))
2398 ui.status(_("no problems detected\n"))
2397 else:
2399 else:
2398 ui.write(_("%s problems detected,"
2400 ui.write(_("%s problems detected,"
2399 " please check your install!\n") % problems)
2401 " please check your install!\n") % problems)
2400
2402
2401 return problems
2403 return problems
2402
2404
2403 @command('debugknown', [], _('REPO ID...'), norepo=True)
2405 @command('debugknown', [], _('REPO ID...'), norepo=True)
2404 def debugknown(ui, repopath, *ids, **opts):
2406 def debugknown(ui, repopath, *ids, **opts):
2405 """test whether node ids are known to a repo
2407 """test whether node ids are known to a repo
2406
2408
2407 Every ID must be a full-length hex node id string. Returns a list of 0s
2409 Every ID must be a full-length hex node id string. Returns a list of 0s
2408 and 1s indicating unknown/known.
2410 and 1s indicating unknown/known.
2409 """
2411 """
2410 repo = hg.peer(ui, opts, repopath)
2412 repo = hg.peer(ui, opts, repopath)
2411 if not repo.capable('known'):
2413 if not repo.capable('known'):
2412 raise util.Abort("known() not supported by target repository")
2414 raise util.Abort("known() not supported by target repository")
2413 flags = repo.known([bin(s) for s in ids])
2415 flags = repo.known([bin(s) for s in ids])
2414 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2416 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2415
2417
2416 @command('debuglabelcomplete', [], _('LABEL...'))
2418 @command('debuglabelcomplete', [], _('LABEL...'))
2417 def debuglabelcomplete(ui, repo, *args):
2419 def debuglabelcomplete(ui, repo, *args):
2418 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2420 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2419 debugnamecomplete(ui, repo, *args)
2421 debugnamecomplete(ui, repo, *args)
2420
2422
2421 @command('debugnamecomplete', [], _('NAME...'))
2423 @command('debugnamecomplete', [], _('NAME...'))
2422 def debugnamecomplete(ui, repo, *args):
2424 def debugnamecomplete(ui, repo, *args):
2423 '''complete "names" - tags, open branch names, bookmark names'''
2425 '''complete "names" - tags, open branch names, bookmark names'''
2424
2426
2425 names = set()
2427 names = set()
2426 # since we previously only listed open branches, we will handle that
2428 # since we previously only listed open branches, we will handle that
2427 # specially (after this for loop)
2429 # specially (after this for loop)
2428 for name, ns in repo.names.iteritems():
2430 for name, ns in repo.names.iteritems():
2429 if name != 'branches':
2431 if name != 'branches':
2430 names.update(ns.listnames(repo))
2432 names.update(ns.listnames(repo))
2431 names.update(tag for (tag, heads, tip, closed)
2433 names.update(tag for (tag, heads, tip, closed)
2432 in repo.branchmap().iterbranches() if not closed)
2434 in repo.branchmap().iterbranches() if not closed)
2433 completions = set()
2435 completions = set()
2434 if not args:
2436 if not args:
2435 args = ['']
2437 args = ['']
2436 for a in args:
2438 for a in args:
2437 completions.update(n for n in names if n.startswith(a))
2439 completions.update(n for n in names if n.startswith(a))
2438 ui.write('\n'.join(sorted(completions)))
2440 ui.write('\n'.join(sorted(completions)))
2439 ui.write('\n')
2441 ui.write('\n')
2440
2442
2441 @command('debuglocks',
2443 @command('debuglocks',
2442 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2444 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2443 ('W', 'force-wlock', None,
2445 ('W', 'force-wlock', None,
2444 _('free the working state lock (DANGEROUS)'))],
2446 _('free the working state lock (DANGEROUS)'))],
2445 _('[OPTION]...'))
2447 _('[OPTION]...'))
2446 def debuglocks(ui, repo, **opts):
2448 def debuglocks(ui, repo, **opts):
2447 """show or modify state of locks
2449 """show or modify state of locks
2448
2450
2449 By default, this command will show which locks are held. This
2451 By default, this command will show which locks are held. This
2450 includes the user and process holding the lock, the amount of time
2452 includes the user and process holding the lock, the amount of time
2451 the lock has been held, and the machine name where the process is
2453 the lock has been held, and the machine name where the process is
2452 running if it's not local.
2454 running if it's not local.
2453
2455
2454 Locks protect the integrity of Mercurial's data, so should be
2456 Locks protect the integrity of Mercurial's data, so should be
2455 treated with care. System crashes or other interruptions may cause
2457 treated with care. System crashes or other interruptions may cause
2456 locks to not be properly released, though Mercurial will usually
2458 locks to not be properly released, though Mercurial will usually
2457 detect and remove such stale locks automatically.
2459 detect and remove such stale locks automatically.
2458
2460
2459 However, detecting stale locks may not always be possible (for
2461 However, detecting stale locks may not always be possible (for
2460 instance, on a shared filesystem). Removing locks may also be
2462 instance, on a shared filesystem). Removing locks may also be
2461 blocked by filesystem permissions.
2463 blocked by filesystem permissions.
2462
2464
2463 Returns 0 if no locks are held.
2465 Returns 0 if no locks are held.
2464
2466
2465 """
2467 """
2466
2468
2467 if opts.get('force_lock'):
2469 if opts.get('force_lock'):
2468 repo.svfs.unlink('lock')
2470 repo.svfs.unlink('lock')
2469 if opts.get('force_wlock'):
2471 if opts.get('force_wlock'):
2470 repo.vfs.unlink('wlock')
2472 repo.vfs.unlink('wlock')
2471 if opts.get('force_lock') or opts.get('force_lock'):
2473 if opts.get('force_lock') or opts.get('force_lock'):
2472 return 0
2474 return 0
2473
2475
2474 now = time.time()
2476 now = time.time()
2475 held = 0
2477 held = 0
2476
2478
2477 def report(vfs, name, method):
2479 def report(vfs, name, method):
2478 # this causes stale locks to get reaped for more accurate reporting
2480 # this causes stale locks to get reaped for more accurate reporting
2479 try:
2481 try:
2480 l = method(False)
2482 l = method(False)
2481 except error.LockHeld:
2483 except error.LockHeld:
2482 l = None
2484 l = None
2483
2485
2484 if l:
2486 if l:
2485 l.release()
2487 l.release()
2486 else:
2488 else:
2487 try:
2489 try:
2488 stat = vfs.lstat(name)
2490 stat = vfs.lstat(name)
2489 age = now - stat.st_mtime
2491 age = now - stat.st_mtime
2490 user = util.username(stat.st_uid)
2492 user = util.username(stat.st_uid)
2491 locker = vfs.readlock(name)
2493 locker = vfs.readlock(name)
2492 if ":" in locker:
2494 if ":" in locker:
2493 host, pid = locker.split(':')
2495 host, pid = locker.split(':')
2494 if host == socket.gethostname():
2496 if host == socket.gethostname():
2495 locker = 'user %s, process %s' % (user, pid)
2497 locker = 'user %s, process %s' % (user, pid)
2496 else:
2498 else:
2497 locker = 'user %s, process %s, host %s' \
2499 locker = 'user %s, process %s, host %s' \
2498 % (user, pid, host)
2500 % (user, pid, host)
2499 ui.write("%-6s %s (%ds)\n" % (name + ":", locker, age))
2501 ui.write("%-6s %s (%ds)\n" % (name + ":", locker, age))
2500 return 1
2502 return 1
2501 except OSError, e:
2503 except OSError, e:
2502 if e.errno != errno.ENOENT:
2504 if e.errno != errno.ENOENT:
2503 raise
2505 raise
2504
2506
2505 ui.write("%-6s free\n" % (name + ":"))
2507 ui.write("%-6s free\n" % (name + ":"))
2506 return 0
2508 return 0
2507
2509
2508 held += report(repo.svfs, "lock", repo.lock)
2510 held += report(repo.svfs, "lock", repo.lock)
2509 held += report(repo.vfs, "wlock", repo.wlock)
2511 held += report(repo.vfs, "wlock", repo.wlock)
2510
2512
2511 return held
2513 return held
2512
2514
2513 @command('debugobsolete',
2515 @command('debugobsolete',
2514 [('', 'flags', 0, _('markers flag')),
2516 [('', 'flags', 0, _('markers flag')),
2515 ('', 'record-parents', False,
2517 ('', 'record-parents', False,
2516 _('record parent information for the precursor')),
2518 _('record parent information for the precursor')),
2517 ('r', 'rev', [], _('display markers relevant to REV')),
2519 ('r', 'rev', [], _('display markers relevant to REV')),
2518 ] + commitopts2,
2520 ] + commitopts2,
2519 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2521 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2520 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2522 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2521 """create arbitrary obsolete marker
2523 """create arbitrary obsolete marker
2522
2524
2523 With no arguments, displays the list of obsolescence markers."""
2525 With no arguments, displays the list of obsolescence markers."""
2524
2526
2525 def parsenodeid(s):
2527 def parsenodeid(s):
2526 try:
2528 try:
2527 # We do not use revsingle/revrange functions here to accept
2529 # We do not use revsingle/revrange functions here to accept
2528 # arbitrary node identifiers, possibly not present in the
2530 # arbitrary node identifiers, possibly not present in the
2529 # local repository.
2531 # local repository.
2530 n = bin(s)
2532 n = bin(s)
2531 if len(n) != len(nullid):
2533 if len(n) != len(nullid):
2532 raise TypeError()
2534 raise TypeError()
2533 return n
2535 return n
2534 except TypeError:
2536 except TypeError:
2535 raise util.Abort('changeset references must be full hexadecimal '
2537 raise util.Abort('changeset references must be full hexadecimal '
2536 'node identifiers')
2538 'node identifiers')
2537
2539
2538 if precursor is not None:
2540 if precursor is not None:
2539 if opts['rev']:
2541 if opts['rev']:
2540 raise util.Abort('cannot select revision when creating marker')
2542 raise util.Abort('cannot select revision when creating marker')
2541 metadata = {}
2543 metadata = {}
2542 metadata['user'] = opts['user'] or ui.username()
2544 metadata['user'] = opts['user'] or ui.username()
2543 succs = tuple(parsenodeid(succ) for succ in successors)
2545 succs = tuple(parsenodeid(succ) for succ in successors)
2544 l = repo.lock()
2546 l = repo.lock()
2545 try:
2547 try:
2546 tr = repo.transaction('debugobsolete')
2548 tr = repo.transaction('debugobsolete')
2547 try:
2549 try:
2548 date = opts.get('date')
2550 date = opts.get('date')
2549 if date:
2551 if date:
2550 date = util.parsedate(date)
2552 date = util.parsedate(date)
2551 else:
2553 else:
2552 date = None
2554 date = None
2553 prec = parsenodeid(precursor)
2555 prec = parsenodeid(precursor)
2554 parents = None
2556 parents = None
2555 if opts['record_parents']:
2557 if opts['record_parents']:
2556 if prec not in repo.unfiltered():
2558 if prec not in repo.unfiltered():
2557 raise util.Abort('cannot used --record-parents on '
2559 raise util.Abort('cannot used --record-parents on '
2558 'unknown changesets')
2560 'unknown changesets')
2559 parents = repo.unfiltered()[prec].parents()
2561 parents = repo.unfiltered()[prec].parents()
2560 parents = tuple(p.node() for p in parents)
2562 parents = tuple(p.node() for p in parents)
2561 repo.obsstore.create(tr, prec, succs, opts['flags'],
2563 repo.obsstore.create(tr, prec, succs, opts['flags'],
2562 parents=parents, date=date,
2564 parents=parents, date=date,
2563 metadata=metadata)
2565 metadata=metadata)
2564 tr.close()
2566 tr.close()
2565 except ValueError, exc:
2567 except ValueError, exc:
2566 raise util.Abort(_('bad obsmarker input: %s') % exc)
2568 raise util.Abort(_('bad obsmarker input: %s') % exc)
2567 finally:
2569 finally:
2568 tr.release()
2570 tr.release()
2569 finally:
2571 finally:
2570 l.release()
2572 l.release()
2571 else:
2573 else:
2572 if opts['rev']:
2574 if opts['rev']:
2573 revs = scmutil.revrange(repo, opts['rev'])
2575 revs = scmutil.revrange(repo, opts['rev'])
2574 nodes = [repo[r].node() for r in revs]
2576 nodes = [repo[r].node() for r in revs]
2575 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2577 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2576 markers.sort(key=lambda x: x._data)
2578 markers.sort(key=lambda x: x._data)
2577 else:
2579 else:
2578 markers = obsolete.getmarkers(repo)
2580 markers = obsolete.getmarkers(repo)
2579
2581
2580 for m in markers:
2582 for m in markers:
2581 cmdutil.showmarker(ui, m)
2583 cmdutil.showmarker(ui, m)
2582
2584
2583 @command('debugpathcomplete',
2585 @command('debugpathcomplete',
2584 [('f', 'full', None, _('complete an entire path')),
2586 [('f', 'full', None, _('complete an entire path')),
2585 ('n', 'normal', None, _('show only normal files')),
2587 ('n', 'normal', None, _('show only normal files')),
2586 ('a', 'added', None, _('show only added files')),
2588 ('a', 'added', None, _('show only added files')),
2587 ('r', 'removed', None, _('show only removed files'))],
2589 ('r', 'removed', None, _('show only removed files'))],
2588 _('FILESPEC...'))
2590 _('FILESPEC...'))
2589 def debugpathcomplete(ui, repo, *specs, **opts):
2591 def debugpathcomplete(ui, repo, *specs, **opts):
2590 '''complete part or all of a tracked path
2592 '''complete part or all of a tracked path
2591
2593
2592 This command supports shells that offer path name completion. It
2594 This command supports shells that offer path name completion. It
2593 currently completes only files already known to the dirstate.
2595 currently completes only files already known to the dirstate.
2594
2596
2595 Completion extends only to the next path segment unless
2597 Completion extends only to the next path segment unless
2596 --full is specified, in which case entire paths are used.'''
2598 --full is specified, in which case entire paths are used.'''
2597
2599
2598 def complete(path, acceptable):
2600 def complete(path, acceptable):
2599 dirstate = repo.dirstate
2601 dirstate = repo.dirstate
2600 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2602 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2601 rootdir = repo.root + os.sep
2603 rootdir = repo.root + os.sep
2602 if spec != repo.root and not spec.startswith(rootdir):
2604 if spec != repo.root and not spec.startswith(rootdir):
2603 return [], []
2605 return [], []
2604 if os.path.isdir(spec):
2606 if os.path.isdir(spec):
2605 spec += '/'
2607 spec += '/'
2606 spec = spec[len(rootdir):]
2608 spec = spec[len(rootdir):]
2607 fixpaths = os.sep != '/'
2609 fixpaths = os.sep != '/'
2608 if fixpaths:
2610 if fixpaths:
2609 spec = spec.replace(os.sep, '/')
2611 spec = spec.replace(os.sep, '/')
2610 speclen = len(spec)
2612 speclen = len(spec)
2611 fullpaths = opts['full']
2613 fullpaths = opts['full']
2612 files, dirs = set(), set()
2614 files, dirs = set(), set()
2613 adddir, addfile = dirs.add, files.add
2615 adddir, addfile = dirs.add, files.add
2614 for f, st in dirstate.iteritems():
2616 for f, st in dirstate.iteritems():
2615 if f.startswith(spec) and st[0] in acceptable:
2617 if f.startswith(spec) and st[0] in acceptable:
2616 if fixpaths:
2618 if fixpaths:
2617 f = f.replace('/', os.sep)
2619 f = f.replace('/', os.sep)
2618 if fullpaths:
2620 if fullpaths:
2619 addfile(f)
2621 addfile(f)
2620 continue
2622 continue
2621 s = f.find(os.sep, speclen)
2623 s = f.find(os.sep, speclen)
2622 if s >= 0:
2624 if s >= 0:
2623 adddir(f[:s])
2625 adddir(f[:s])
2624 else:
2626 else:
2625 addfile(f)
2627 addfile(f)
2626 return files, dirs
2628 return files, dirs
2627
2629
2628 acceptable = ''
2630 acceptable = ''
2629 if opts['normal']:
2631 if opts['normal']:
2630 acceptable += 'nm'
2632 acceptable += 'nm'
2631 if opts['added']:
2633 if opts['added']:
2632 acceptable += 'a'
2634 acceptable += 'a'
2633 if opts['removed']:
2635 if opts['removed']:
2634 acceptable += 'r'
2636 acceptable += 'r'
2635 cwd = repo.getcwd()
2637 cwd = repo.getcwd()
2636 if not specs:
2638 if not specs:
2637 specs = ['.']
2639 specs = ['.']
2638
2640
2639 files, dirs = set(), set()
2641 files, dirs = set(), set()
2640 for spec in specs:
2642 for spec in specs:
2641 f, d = complete(spec, acceptable or 'nmar')
2643 f, d = complete(spec, acceptable or 'nmar')
2642 files.update(f)
2644 files.update(f)
2643 dirs.update(d)
2645 dirs.update(d)
2644 files.update(dirs)
2646 files.update(dirs)
2645 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2647 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2646 ui.write('\n')
2648 ui.write('\n')
2647
2649
2648 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2650 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2649 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2651 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2650 '''access the pushkey key/value protocol
2652 '''access the pushkey key/value protocol
2651
2653
2652 With two args, list the keys in the given namespace.
2654 With two args, list the keys in the given namespace.
2653
2655
2654 With five args, set a key to new if it currently is set to old.
2656 With five args, set a key to new if it currently is set to old.
2655 Reports success or failure.
2657 Reports success or failure.
2656 '''
2658 '''
2657
2659
2658 target = hg.peer(ui, {}, repopath)
2660 target = hg.peer(ui, {}, repopath)
2659 if keyinfo:
2661 if keyinfo:
2660 key, old, new = keyinfo
2662 key, old, new = keyinfo
2661 r = target.pushkey(namespace, key, old, new)
2663 r = target.pushkey(namespace, key, old, new)
2662 ui.status(str(r) + '\n')
2664 ui.status(str(r) + '\n')
2663 return not r
2665 return not r
2664 else:
2666 else:
2665 for k, v in sorted(target.listkeys(namespace).iteritems()):
2667 for k, v in sorted(target.listkeys(namespace).iteritems()):
2666 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2668 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2667 v.encode('string-escape')))
2669 v.encode('string-escape')))
2668
2670
2669 @command('debugpvec', [], _('A B'))
2671 @command('debugpvec', [], _('A B'))
2670 def debugpvec(ui, repo, a, b=None):
2672 def debugpvec(ui, repo, a, b=None):
2671 ca = scmutil.revsingle(repo, a)
2673 ca = scmutil.revsingle(repo, a)
2672 cb = scmutil.revsingle(repo, b)
2674 cb = scmutil.revsingle(repo, b)
2673 pa = pvec.ctxpvec(ca)
2675 pa = pvec.ctxpvec(ca)
2674 pb = pvec.ctxpvec(cb)
2676 pb = pvec.ctxpvec(cb)
2675 if pa == pb:
2677 if pa == pb:
2676 rel = "="
2678 rel = "="
2677 elif pa > pb:
2679 elif pa > pb:
2678 rel = ">"
2680 rel = ">"
2679 elif pa < pb:
2681 elif pa < pb:
2680 rel = "<"
2682 rel = "<"
2681 elif pa | pb:
2683 elif pa | pb:
2682 rel = "|"
2684 rel = "|"
2683 ui.write(_("a: %s\n") % pa)
2685 ui.write(_("a: %s\n") % pa)
2684 ui.write(_("b: %s\n") % pb)
2686 ui.write(_("b: %s\n") % pb)
2685 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2687 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2686 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2688 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2687 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2689 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2688 pa.distance(pb), rel))
2690 pa.distance(pb), rel))
2689
2691
2690 @command('debugrebuilddirstate|debugrebuildstate',
2692 @command('debugrebuilddirstate|debugrebuildstate',
2691 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2693 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2692 _('[-r REV]'))
2694 _('[-r REV]'))
2693 def debugrebuilddirstate(ui, repo, rev):
2695 def debugrebuilddirstate(ui, repo, rev):
2694 """rebuild the dirstate as it would look like for the given revision
2696 """rebuild the dirstate as it would look like for the given revision
2695
2697
2696 If no revision is specified the first current parent will be used.
2698 If no revision is specified the first current parent will be used.
2697
2699
2698 The dirstate will be set to the files of the given revision.
2700 The dirstate will be set to the files of the given revision.
2699 The actual working directory content or existing dirstate
2701 The actual working directory content or existing dirstate
2700 information such as adds or removes is not considered.
2702 information such as adds or removes is not considered.
2701
2703
2702 One use of this command is to make the next :hg:`status` invocation
2704 One use of this command is to make the next :hg:`status` invocation
2703 check the actual file content.
2705 check the actual file content.
2704 """
2706 """
2705 ctx = scmutil.revsingle(repo, rev)
2707 ctx = scmutil.revsingle(repo, rev)
2706 wlock = repo.wlock()
2708 wlock = repo.wlock()
2707 try:
2709 try:
2708 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2710 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2709 finally:
2711 finally:
2710 wlock.release()
2712 wlock.release()
2711
2713
2712 @command('debugrename',
2714 @command('debugrename',
2713 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2715 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2714 _('[-r REV] FILE'))
2716 _('[-r REV] FILE'))
2715 def debugrename(ui, repo, file1, *pats, **opts):
2717 def debugrename(ui, repo, file1, *pats, **opts):
2716 """dump rename information"""
2718 """dump rename information"""
2717
2719
2718 ctx = scmutil.revsingle(repo, opts.get('rev'))
2720 ctx = scmutil.revsingle(repo, opts.get('rev'))
2719 m = scmutil.match(ctx, (file1,) + pats, opts)
2721 m = scmutil.match(ctx, (file1,) + pats, opts)
2720 for abs in ctx.walk(m):
2722 for abs in ctx.walk(m):
2721 fctx = ctx[abs]
2723 fctx = ctx[abs]
2722 o = fctx.filelog().renamed(fctx.filenode())
2724 o = fctx.filelog().renamed(fctx.filenode())
2723 rel = m.rel(abs)
2725 rel = m.rel(abs)
2724 if o:
2726 if o:
2725 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2727 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2726 else:
2728 else:
2727 ui.write(_("%s not renamed\n") % rel)
2729 ui.write(_("%s not renamed\n") % rel)
2728
2730
2729 @command('debugrevlog',
2731 @command('debugrevlog',
2730 [('c', 'changelog', False, _('open changelog')),
2732 [('c', 'changelog', False, _('open changelog')),
2731 ('m', 'manifest', False, _('open manifest')),
2733 ('m', 'manifest', False, _('open manifest')),
2734 ('', 'dir', False, _('open directory manifest')),
2732 ('d', 'dump', False, _('dump index data'))],
2735 ('d', 'dump', False, _('dump index data'))],
2733 _('-c|-m|FILE'),
2736 _('-c|-m|FILE'),
2734 optionalrepo=True)
2737 optionalrepo=True)
2735 def debugrevlog(ui, repo, file_=None, **opts):
2738 def debugrevlog(ui, repo, file_=None, **opts):
2736 """show data and statistics about a revlog"""
2739 """show data and statistics about a revlog"""
2737 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2740 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2738
2741
2739 if opts.get("dump"):
2742 if opts.get("dump"):
2740 numrevs = len(r)
2743 numrevs = len(r)
2741 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2744 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2742 " rawsize totalsize compression heads chainlen\n")
2745 " rawsize totalsize compression heads chainlen\n")
2743 ts = 0
2746 ts = 0
2744 heads = set()
2747 heads = set()
2745
2748
2746 for rev in xrange(numrevs):
2749 for rev in xrange(numrevs):
2747 dbase = r.deltaparent(rev)
2750 dbase = r.deltaparent(rev)
2748 if dbase == -1:
2751 if dbase == -1:
2749 dbase = rev
2752 dbase = rev
2750 cbase = r.chainbase(rev)
2753 cbase = r.chainbase(rev)
2751 clen = r.chainlen(rev)
2754 clen = r.chainlen(rev)
2752 p1, p2 = r.parentrevs(rev)
2755 p1, p2 = r.parentrevs(rev)
2753 rs = r.rawsize(rev)
2756 rs = r.rawsize(rev)
2754 ts = ts + rs
2757 ts = ts + rs
2755 heads -= set(r.parentrevs(rev))
2758 heads -= set(r.parentrevs(rev))
2756 heads.add(rev)
2759 heads.add(rev)
2757 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2760 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2758 "%11d %5d %8d\n" %
2761 "%11d %5d %8d\n" %
2759 (rev, p1, p2, r.start(rev), r.end(rev),
2762 (rev, p1, p2, r.start(rev), r.end(rev),
2760 r.start(dbase), r.start(cbase),
2763 r.start(dbase), r.start(cbase),
2761 r.start(p1), r.start(p2),
2764 r.start(p1), r.start(p2),
2762 rs, ts, ts / r.end(rev), len(heads), clen))
2765 rs, ts, ts / r.end(rev), len(heads), clen))
2763 return 0
2766 return 0
2764
2767
2765 v = r.version
2768 v = r.version
2766 format = v & 0xFFFF
2769 format = v & 0xFFFF
2767 flags = []
2770 flags = []
2768 gdelta = False
2771 gdelta = False
2769 if v & revlog.REVLOGNGINLINEDATA:
2772 if v & revlog.REVLOGNGINLINEDATA:
2770 flags.append('inline')
2773 flags.append('inline')
2771 if v & revlog.REVLOGGENERALDELTA:
2774 if v & revlog.REVLOGGENERALDELTA:
2772 gdelta = True
2775 gdelta = True
2773 flags.append('generaldelta')
2776 flags.append('generaldelta')
2774 if not flags:
2777 if not flags:
2775 flags = ['(none)']
2778 flags = ['(none)']
2776
2779
2777 nummerges = 0
2780 nummerges = 0
2778 numfull = 0
2781 numfull = 0
2779 numprev = 0
2782 numprev = 0
2780 nump1 = 0
2783 nump1 = 0
2781 nump2 = 0
2784 nump2 = 0
2782 numother = 0
2785 numother = 0
2783 nump1prev = 0
2786 nump1prev = 0
2784 nump2prev = 0
2787 nump2prev = 0
2785 chainlengths = []
2788 chainlengths = []
2786
2789
2787 datasize = [None, 0, 0L]
2790 datasize = [None, 0, 0L]
2788 fullsize = [None, 0, 0L]
2791 fullsize = [None, 0, 0L]
2789 deltasize = [None, 0, 0L]
2792 deltasize = [None, 0, 0L]
2790
2793
2791 def addsize(size, l):
2794 def addsize(size, l):
2792 if l[0] is None or size < l[0]:
2795 if l[0] is None or size < l[0]:
2793 l[0] = size
2796 l[0] = size
2794 if size > l[1]:
2797 if size > l[1]:
2795 l[1] = size
2798 l[1] = size
2796 l[2] += size
2799 l[2] += size
2797
2800
2798 numrevs = len(r)
2801 numrevs = len(r)
2799 for rev in xrange(numrevs):
2802 for rev in xrange(numrevs):
2800 p1, p2 = r.parentrevs(rev)
2803 p1, p2 = r.parentrevs(rev)
2801 delta = r.deltaparent(rev)
2804 delta = r.deltaparent(rev)
2802 if format > 0:
2805 if format > 0:
2803 addsize(r.rawsize(rev), datasize)
2806 addsize(r.rawsize(rev), datasize)
2804 if p2 != nullrev:
2807 if p2 != nullrev:
2805 nummerges += 1
2808 nummerges += 1
2806 size = r.length(rev)
2809 size = r.length(rev)
2807 if delta == nullrev:
2810 if delta == nullrev:
2808 chainlengths.append(0)
2811 chainlengths.append(0)
2809 numfull += 1
2812 numfull += 1
2810 addsize(size, fullsize)
2813 addsize(size, fullsize)
2811 else:
2814 else:
2812 chainlengths.append(chainlengths[delta] + 1)
2815 chainlengths.append(chainlengths[delta] + 1)
2813 addsize(size, deltasize)
2816 addsize(size, deltasize)
2814 if delta == rev - 1:
2817 if delta == rev - 1:
2815 numprev += 1
2818 numprev += 1
2816 if delta == p1:
2819 if delta == p1:
2817 nump1prev += 1
2820 nump1prev += 1
2818 elif delta == p2:
2821 elif delta == p2:
2819 nump2prev += 1
2822 nump2prev += 1
2820 elif delta == p1:
2823 elif delta == p1:
2821 nump1 += 1
2824 nump1 += 1
2822 elif delta == p2:
2825 elif delta == p2:
2823 nump2 += 1
2826 nump2 += 1
2824 elif delta != nullrev:
2827 elif delta != nullrev:
2825 numother += 1
2828 numother += 1
2826
2829
2827 # Adjust size min value for empty cases
2830 # Adjust size min value for empty cases
2828 for size in (datasize, fullsize, deltasize):
2831 for size in (datasize, fullsize, deltasize):
2829 if size[0] is None:
2832 if size[0] is None:
2830 size[0] = 0
2833 size[0] = 0
2831
2834
2832 numdeltas = numrevs - numfull
2835 numdeltas = numrevs - numfull
2833 numoprev = numprev - nump1prev - nump2prev
2836 numoprev = numprev - nump1prev - nump2prev
2834 totalrawsize = datasize[2]
2837 totalrawsize = datasize[2]
2835 datasize[2] /= numrevs
2838 datasize[2] /= numrevs
2836 fulltotal = fullsize[2]
2839 fulltotal = fullsize[2]
2837 fullsize[2] /= numfull
2840 fullsize[2] /= numfull
2838 deltatotal = deltasize[2]
2841 deltatotal = deltasize[2]
2839 if numrevs - numfull > 0:
2842 if numrevs - numfull > 0:
2840 deltasize[2] /= numrevs - numfull
2843 deltasize[2] /= numrevs - numfull
2841 totalsize = fulltotal + deltatotal
2844 totalsize = fulltotal + deltatotal
2842 avgchainlen = sum(chainlengths) / numrevs
2845 avgchainlen = sum(chainlengths) / numrevs
2843 maxchainlen = max(chainlengths)
2846 maxchainlen = max(chainlengths)
2844 compratio = totalrawsize / totalsize
2847 compratio = totalrawsize / totalsize
2845
2848
2846 basedfmtstr = '%%%dd\n'
2849 basedfmtstr = '%%%dd\n'
2847 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2850 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2848
2851
2849 def dfmtstr(max):
2852 def dfmtstr(max):
2850 return basedfmtstr % len(str(max))
2853 return basedfmtstr % len(str(max))
2851 def pcfmtstr(max, padding=0):
2854 def pcfmtstr(max, padding=0):
2852 return basepcfmtstr % (len(str(max)), ' ' * padding)
2855 return basepcfmtstr % (len(str(max)), ' ' * padding)
2853
2856
2854 def pcfmt(value, total):
2857 def pcfmt(value, total):
2855 return (value, 100 * float(value) / total)
2858 return (value, 100 * float(value) / total)
2856
2859
2857 ui.write(('format : %d\n') % format)
2860 ui.write(('format : %d\n') % format)
2858 ui.write(('flags : %s\n') % ', '.join(flags))
2861 ui.write(('flags : %s\n') % ', '.join(flags))
2859
2862
2860 ui.write('\n')
2863 ui.write('\n')
2861 fmt = pcfmtstr(totalsize)
2864 fmt = pcfmtstr(totalsize)
2862 fmt2 = dfmtstr(totalsize)
2865 fmt2 = dfmtstr(totalsize)
2863 ui.write(('revisions : ') + fmt2 % numrevs)
2866 ui.write(('revisions : ') + fmt2 % numrevs)
2864 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2867 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2865 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2868 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2866 ui.write(('revisions : ') + fmt2 % numrevs)
2869 ui.write(('revisions : ') + fmt2 % numrevs)
2867 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2870 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2868 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2871 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2869 ui.write(('revision size : ') + fmt2 % totalsize)
2872 ui.write(('revision size : ') + fmt2 % totalsize)
2870 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2873 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2871 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2874 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2872
2875
2873 ui.write('\n')
2876 ui.write('\n')
2874 fmt = dfmtstr(max(avgchainlen, compratio))
2877 fmt = dfmtstr(max(avgchainlen, compratio))
2875 ui.write(('avg chain length : ') + fmt % avgchainlen)
2878 ui.write(('avg chain length : ') + fmt % avgchainlen)
2876 ui.write(('max chain length : ') + fmt % maxchainlen)
2879 ui.write(('max chain length : ') + fmt % maxchainlen)
2877 ui.write(('compression ratio : ') + fmt % compratio)
2880 ui.write(('compression ratio : ') + fmt % compratio)
2878
2881
2879 if format > 0:
2882 if format > 0:
2880 ui.write('\n')
2883 ui.write('\n')
2881 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2884 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2882 % tuple(datasize))
2885 % tuple(datasize))
2883 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2886 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2884 % tuple(fullsize))
2887 % tuple(fullsize))
2885 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2888 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2886 % tuple(deltasize))
2889 % tuple(deltasize))
2887
2890
2888 if numdeltas > 0:
2891 if numdeltas > 0:
2889 ui.write('\n')
2892 ui.write('\n')
2890 fmt = pcfmtstr(numdeltas)
2893 fmt = pcfmtstr(numdeltas)
2891 fmt2 = pcfmtstr(numdeltas, 4)
2894 fmt2 = pcfmtstr(numdeltas, 4)
2892 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2895 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2893 if numprev > 0:
2896 if numprev > 0:
2894 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2897 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2895 numprev))
2898 numprev))
2896 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2899 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2897 numprev))
2900 numprev))
2898 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2901 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2899 numprev))
2902 numprev))
2900 if gdelta:
2903 if gdelta:
2901 ui.write(('deltas against p1 : ')
2904 ui.write(('deltas against p1 : ')
2902 + fmt % pcfmt(nump1, numdeltas))
2905 + fmt % pcfmt(nump1, numdeltas))
2903 ui.write(('deltas against p2 : ')
2906 ui.write(('deltas against p2 : ')
2904 + fmt % pcfmt(nump2, numdeltas))
2907 + fmt % pcfmt(nump2, numdeltas))
2905 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2908 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2906 numdeltas))
2909 numdeltas))
2907
2910
2908 @command('debugrevspec',
2911 @command('debugrevspec',
2909 [('', 'optimize', None, _('print parsed tree after optimizing'))],
2912 [('', 'optimize', None, _('print parsed tree after optimizing'))],
2910 ('REVSPEC'))
2913 ('REVSPEC'))
2911 def debugrevspec(ui, repo, expr, **opts):
2914 def debugrevspec(ui, repo, expr, **opts):
2912 """parse and apply a revision specification
2915 """parse and apply a revision specification
2913
2916
2914 Use --verbose to print the parsed tree before and after aliases
2917 Use --verbose to print the parsed tree before and after aliases
2915 expansion.
2918 expansion.
2916 """
2919 """
2917 if ui.verbose:
2920 if ui.verbose:
2918 tree = revset.parse(expr)[0]
2921 tree = revset.parse(expr)[0]
2919 ui.note(revset.prettyformat(tree), "\n")
2922 ui.note(revset.prettyformat(tree), "\n")
2920 newtree = revset.findaliases(ui, tree)
2923 newtree = revset.findaliases(ui, tree)
2921 if newtree != tree:
2924 if newtree != tree:
2922 ui.note(revset.prettyformat(newtree), "\n")
2925 ui.note(revset.prettyformat(newtree), "\n")
2923 tree = newtree
2926 tree = newtree
2924 newtree = revset.foldconcat(tree)
2927 newtree = revset.foldconcat(tree)
2925 if newtree != tree:
2928 if newtree != tree:
2926 ui.note(revset.prettyformat(newtree), "\n")
2929 ui.note(revset.prettyformat(newtree), "\n")
2927 if opts["optimize"]:
2930 if opts["optimize"]:
2928 weight, optimizedtree = revset.optimize(newtree, True)
2931 weight, optimizedtree = revset.optimize(newtree, True)
2929 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
2932 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
2930 func = revset.match(ui, expr)
2933 func = revset.match(ui, expr)
2931 revs = func(repo)
2934 revs = func(repo)
2932 if ui.verbose:
2935 if ui.verbose:
2933 ui.note("* set:\n", revset.prettyformatset(revs), "\n")
2936 ui.note("* set:\n", revset.prettyformatset(revs), "\n")
2934 for c in revs:
2937 for c in revs:
2935 ui.write("%s\n" % c)
2938 ui.write("%s\n" % c)
2936
2939
2937 @command('debugsetparents', [], _('REV1 [REV2]'))
2940 @command('debugsetparents', [], _('REV1 [REV2]'))
2938 def debugsetparents(ui, repo, rev1, rev2=None):
2941 def debugsetparents(ui, repo, rev1, rev2=None):
2939 """manually set the parents of the current working directory
2942 """manually set the parents of the current working directory
2940
2943
2941 This is useful for writing repository conversion tools, but should
2944 This is useful for writing repository conversion tools, but should
2942 be used with care. For example, neither the working directory nor the
2945 be used with care. For example, neither the working directory nor the
2943 dirstate is updated, so file status may be incorrect after running this
2946 dirstate is updated, so file status may be incorrect after running this
2944 command.
2947 command.
2945
2948
2946 Returns 0 on success.
2949 Returns 0 on success.
2947 """
2950 """
2948
2951
2949 r1 = scmutil.revsingle(repo, rev1).node()
2952 r1 = scmutil.revsingle(repo, rev1).node()
2950 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2953 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2951
2954
2952 wlock = repo.wlock()
2955 wlock = repo.wlock()
2953 try:
2956 try:
2954 repo.dirstate.beginparentchange()
2957 repo.dirstate.beginparentchange()
2955 repo.setparents(r1, r2)
2958 repo.setparents(r1, r2)
2956 repo.dirstate.endparentchange()
2959 repo.dirstate.endparentchange()
2957 finally:
2960 finally:
2958 wlock.release()
2961 wlock.release()
2959
2962
2960 @command('debugdirstate|debugstate',
2963 @command('debugdirstate|debugstate',
2961 [('', 'nodates', None, _('do not display the saved mtime')),
2964 [('', 'nodates', None, _('do not display the saved mtime')),
2962 ('', 'datesort', None, _('sort by saved mtime'))],
2965 ('', 'datesort', None, _('sort by saved mtime'))],
2963 _('[OPTION]...'))
2966 _('[OPTION]...'))
2964 def debugstate(ui, repo, nodates=None, datesort=None):
2967 def debugstate(ui, repo, nodates=None, datesort=None):
2965 """show the contents of the current dirstate"""
2968 """show the contents of the current dirstate"""
2966 timestr = ""
2969 timestr = ""
2967 if datesort:
2970 if datesort:
2968 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2971 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2969 else:
2972 else:
2970 keyfunc = None # sort by filename
2973 keyfunc = None # sort by filename
2971 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2974 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2972 if ent[3] == -1:
2975 if ent[3] == -1:
2973 timestr = 'unset '
2976 timestr = 'unset '
2974 elif nodates:
2977 elif nodates:
2975 timestr = 'set '
2978 timestr = 'set '
2976 else:
2979 else:
2977 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2980 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2978 time.localtime(ent[3]))
2981 time.localtime(ent[3]))
2979 if ent[1] & 020000:
2982 if ent[1] & 020000:
2980 mode = 'lnk'
2983 mode = 'lnk'
2981 else:
2984 else:
2982 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2985 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2983 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2986 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2984 for f in repo.dirstate.copies():
2987 for f in repo.dirstate.copies():
2985 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2988 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2986
2989
2987 @command('debugsub',
2990 @command('debugsub',
2988 [('r', 'rev', '',
2991 [('r', 'rev', '',
2989 _('revision to check'), _('REV'))],
2992 _('revision to check'), _('REV'))],
2990 _('[-r REV] [REV]'))
2993 _('[-r REV] [REV]'))
2991 def debugsub(ui, repo, rev=None):
2994 def debugsub(ui, repo, rev=None):
2992 ctx = scmutil.revsingle(repo, rev, None)
2995 ctx = scmutil.revsingle(repo, rev, None)
2993 for k, v in sorted(ctx.substate.items()):
2996 for k, v in sorted(ctx.substate.items()):
2994 ui.write(('path %s\n') % k)
2997 ui.write(('path %s\n') % k)
2995 ui.write((' source %s\n') % v[0])
2998 ui.write((' source %s\n') % v[0])
2996 ui.write((' revision %s\n') % v[1])
2999 ui.write((' revision %s\n') % v[1])
2997
3000
2998 @command('debugsuccessorssets',
3001 @command('debugsuccessorssets',
2999 [],
3002 [],
3000 _('[REV]'))
3003 _('[REV]'))
3001 def debugsuccessorssets(ui, repo, *revs):
3004 def debugsuccessorssets(ui, repo, *revs):
3002 """show set of successors for revision
3005 """show set of successors for revision
3003
3006
3004 A successors set of changeset A is a consistent group of revisions that
3007 A successors set of changeset A is a consistent group of revisions that
3005 succeed A. It contains non-obsolete changesets only.
3008 succeed A. It contains non-obsolete changesets only.
3006
3009
3007 In most cases a changeset A has a single successors set containing a single
3010 In most cases a changeset A has a single successors set containing a single
3008 successor (changeset A replaced by A').
3011 successor (changeset A replaced by A').
3009
3012
3010 A changeset that is made obsolete with no successors are called "pruned".
3013 A changeset that is made obsolete with no successors are called "pruned".
3011 Such changesets have no successors sets at all.
3014 Such changesets have no successors sets at all.
3012
3015
3013 A changeset that has been "split" will have a successors set containing
3016 A changeset that has been "split" will have a successors set containing
3014 more than one successor.
3017 more than one successor.
3015
3018
3016 A changeset that has been rewritten in multiple different ways is called
3019 A changeset that has been rewritten in multiple different ways is called
3017 "divergent". Such changesets have multiple successor sets (each of which
3020 "divergent". Such changesets have multiple successor sets (each of which
3018 may also be split, i.e. have multiple successors).
3021 may also be split, i.e. have multiple successors).
3019
3022
3020 Results are displayed as follows::
3023 Results are displayed as follows::
3021
3024
3022 <rev1>
3025 <rev1>
3023 <successors-1A>
3026 <successors-1A>
3024 <rev2>
3027 <rev2>
3025 <successors-2A>
3028 <successors-2A>
3026 <successors-2B1> <successors-2B2> <successors-2B3>
3029 <successors-2B1> <successors-2B2> <successors-2B3>
3027
3030
3028 Here rev2 has two possible (i.e. divergent) successors sets. The first
3031 Here rev2 has two possible (i.e. divergent) successors sets. The first
3029 holds one element, whereas the second holds three (i.e. the changeset has
3032 holds one element, whereas the second holds three (i.e. the changeset has
3030 been split).
3033 been split).
3031 """
3034 """
3032 # passed to successorssets caching computation from one call to another
3035 # passed to successorssets caching computation from one call to another
3033 cache = {}
3036 cache = {}
3034 ctx2str = str
3037 ctx2str = str
3035 node2str = short
3038 node2str = short
3036 if ui.debug():
3039 if ui.debug():
3037 def ctx2str(ctx):
3040 def ctx2str(ctx):
3038 return ctx.hex()
3041 return ctx.hex()
3039 node2str = hex
3042 node2str = hex
3040 for rev in scmutil.revrange(repo, revs):
3043 for rev in scmutil.revrange(repo, revs):
3041 ctx = repo[rev]
3044 ctx = repo[rev]
3042 ui.write('%s\n'% ctx2str(ctx))
3045 ui.write('%s\n'% ctx2str(ctx))
3043 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
3046 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
3044 if succsset:
3047 if succsset:
3045 ui.write(' ')
3048 ui.write(' ')
3046 ui.write(node2str(succsset[0]))
3049 ui.write(node2str(succsset[0]))
3047 for node in succsset[1:]:
3050 for node in succsset[1:]:
3048 ui.write(' ')
3051 ui.write(' ')
3049 ui.write(node2str(node))
3052 ui.write(node2str(node))
3050 ui.write('\n')
3053 ui.write('\n')
3051
3054
3052 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
3055 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
3053 def debugwalk(ui, repo, *pats, **opts):
3056 def debugwalk(ui, repo, *pats, **opts):
3054 """show how files match on given patterns"""
3057 """show how files match on given patterns"""
3055 m = scmutil.match(repo[None], pats, opts)
3058 m = scmutil.match(repo[None], pats, opts)
3056 items = list(repo.walk(m))
3059 items = list(repo.walk(m))
3057 if not items:
3060 if not items:
3058 return
3061 return
3059 f = lambda fn: fn
3062 f = lambda fn: fn
3060 if ui.configbool('ui', 'slash') and os.sep != '/':
3063 if ui.configbool('ui', 'slash') and os.sep != '/':
3061 f = lambda fn: util.normpath(fn)
3064 f = lambda fn: util.normpath(fn)
3062 fmt = 'f %%-%ds %%-%ds %%s' % (
3065 fmt = 'f %%-%ds %%-%ds %%s' % (
3063 max([len(abs) for abs in items]),
3066 max([len(abs) for abs in items]),
3064 max([len(m.rel(abs)) for abs in items]))
3067 max([len(m.rel(abs)) for abs in items]))
3065 for abs in items:
3068 for abs in items:
3066 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3069 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3067 ui.write("%s\n" % line.rstrip())
3070 ui.write("%s\n" % line.rstrip())
3068
3071
3069 @command('debugwireargs',
3072 @command('debugwireargs',
3070 [('', 'three', '', 'three'),
3073 [('', 'three', '', 'three'),
3071 ('', 'four', '', 'four'),
3074 ('', 'four', '', 'four'),
3072 ('', 'five', '', 'five'),
3075 ('', 'five', '', 'five'),
3073 ] + remoteopts,
3076 ] + remoteopts,
3074 _('REPO [OPTIONS]... [ONE [TWO]]'),
3077 _('REPO [OPTIONS]... [ONE [TWO]]'),
3075 norepo=True)
3078 norepo=True)
3076 def debugwireargs(ui, repopath, *vals, **opts):
3079 def debugwireargs(ui, repopath, *vals, **opts):
3077 repo = hg.peer(ui, opts, repopath)
3080 repo = hg.peer(ui, opts, repopath)
3078 for opt in remoteopts:
3081 for opt in remoteopts:
3079 del opts[opt[1]]
3082 del opts[opt[1]]
3080 args = {}
3083 args = {}
3081 for k, v in opts.iteritems():
3084 for k, v in opts.iteritems():
3082 if v:
3085 if v:
3083 args[k] = v
3086 args[k] = v
3084 # run twice to check that we don't mess up the stream for the next command
3087 # run twice to check that we don't mess up the stream for the next command
3085 res1 = repo.debugwireargs(*vals, **args)
3088 res1 = repo.debugwireargs(*vals, **args)
3086 res2 = repo.debugwireargs(*vals, **args)
3089 res2 = repo.debugwireargs(*vals, **args)
3087 ui.write("%s\n" % res1)
3090 ui.write("%s\n" % res1)
3088 if res1 != res2:
3091 if res1 != res2:
3089 ui.warn("%s\n" % res2)
3092 ui.warn("%s\n" % res2)
3090
3093
3091 @command('^diff',
3094 @command('^diff',
3092 [('r', 'rev', [], _('revision'), _('REV')),
3095 [('r', 'rev', [], _('revision'), _('REV')),
3093 ('c', 'change', '', _('change made by revision'), _('REV'))
3096 ('c', 'change', '', _('change made by revision'), _('REV'))
3094 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3097 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3095 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3098 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3096 inferrepo=True)
3099 inferrepo=True)
3097 def diff(ui, repo, *pats, **opts):
3100 def diff(ui, repo, *pats, **opts):
3098 """diff repository (or selected files)
3101 """diff repository (or selected files)
3099
3102
3100 Show differences between revisions for the specified files.
3103 Show differences between revisions for the specified files.
3101
3104
3102 Differences between files are shown using the unified diff format.
3105 Differences between files are shown using the unified diff format.
3103
3106
3104 .. note::
3107 .. note::
3105
3108
3106 diff may generate unexpected results for merges, as it will
3109 diff may generate unexpected results for merges, as it will
3107 default to comparing against the working directory's first
3110 default to comparing against the working directory's first
3108 parent changeset if no revisions are specified.
3111 parent changeset if no revisions are specified.
3109
3112
3110 When two revision arguments are given, then changes are shown
3113 When two revision arguments are given, then changes are shown
3111 between those revisions. If only one revision is specified then
3114 between those revisions. If only one revision is specified then
3112 that revision is compared to the working directory, and, when no
3115 that revision is compared to the working directory, and, when no
3113 revisions are specified, the working directory files are compared
3116 revisions are specified, the working directory files are compared
3114 to its parent.
3117 to its parent.
3115
3118
3116 Alternatively you can specify -c/--change with a revision to see
3119 Alternatively you can specify -c/--change with a revision to see
3117 the changes in that changeset relative to its first parent.
3120 the changes in that changeset relative to its first parent.
3118
3121
3119 Without the -a/--text option, diff will avoid generating diffs of
3122 Without the -a/--text option, diff will avoid generating diffs of
3120 files it detects as binary. With -a, diff will generate a diff
3123 files it detects as binary. With -a, diff will generate a diff
3121 anyway, probably with undesirable results.
3124 anyway, probably with undesirable results.
3122
3125
3123 Use the -g/--git option to generate diffs in the git extended diff
3126 Use the -g/--git option to generate diffs in the git extended diff
3124 format. For more information, read :hg:`help diffs`.
3127 format. For more information, read :hg:`help diffs`.
3125
3128
3126 .. container:: verbose
3129 .. container:: verbose
3127
3130
3128 Examples:
3131 Examples:
3129
3132
3130 - compare a file in the current working directory to its parent::
3133 - compare a file in the current working directory to its parent::
3131
3134
3132 hg diff foo.c
3135 hg diff foo.c
3133
3136
3134 - compare two historical versions of a directory, with rename info::
3137 - compare two historical versions of a directory, with rename info::
3135
3138
3136 hg diff --git -r 1.0:1.2 lib/
3139 hg diff --git -r 1.0:1.2 lib/
3137
3140
3138 - get change stats relative to the last change on some date::
3141 - get change stats relative to the last change on some date::
3139
3142
3140 hg diff --stat -r "date('may 2')"
3143 hg diff --stat -r "date('may 2')"
3141
3144
3142 - diff all newly-added files that contain a keyword::
3145 - diff all newly-added files that contain a keyword::
3143
3146
3144 hg diff "set:added() and grep(GNU)"
3147 hg diff "set:added() and grep(GNU)"
3145
3148
3146 - compare a revision and its parents::
3149 - compare a revision and its parents::
3147
3150
3148 hg diff -c 9353 # compare against first parent
3151 hg diff -c 9353 # compare against first parent
3149 hg diff -r 9353^:9353 # same using revset syntax
3152 hg diff -r 9353^:9353 # same using revset syntax
3150 hg diff -r 9353^2:9353 # compare against the second parent
3153 hg diff -r 9353^2:9353 # compare against the second parent
3151
3154
3152 Returns 0 on success.
3155 Returns 0 on success.
3153 """
3156 """
3154
3157
3155 revs = opts.get('rev')
3158 revs = opts.get('rev')
3156 change = opts.get('change')
3159 change = opts.get('change')
3157 stat = opts.get('stat')
3160 stat = opts.get('stat')
3158 reverse = opts.get('reverse')
3161 reverse = opts.get('reverse')
3159
3162
3160 if revs and change:
3163 if revs and change:
3161 msg = _('cannot specify --rev and --change at the same time')
3164 msg = _('cannot specify --rev and --change at the same time')
3162 raise util.Abort(msg)
3165 raise util.Abort(msg)
3163 elif change:
3166 elif change:
3164 node2 = scmutil.revsingle(repo, change, None).node()
3167 node2 = scmutil.revsingle(repo, change, None).node()
3165 node1 = repo[node2].p1().node()
3168 node1 = repo[node2].p1().node()
3166 else:
3169 else:
3167 node1, node2 = scmutil.revpair(repo, revs)
3170 node1, node2 = scmutil.revpair(repo, revs)
3168
3171
3169 if reverse:
3172 if reverse:
3170 node1, node2 = node2, node1
3173 node1, node2 = node2, node1
3171
3174
3172 diffopts = patch.diffallopts(ui, opts)
3175 diffopts = patch.diffallopts(ui, opts)
3173 m = scmutil.match(repo[node2], pats, opts)
3176 m = scmutil.match(repo[node2], pats, opts)
3174 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3177 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3175 listsubrepos=opts.get('subrepos'),
3178 listsubrepos=opts.get('subrepos'),
3176 root=opts.get('root'))
3179 root=opts.get('root'))
3177
3180
3178 @command('^export',
3181 @command('^export',
3179 [('o', 'output', '',
3182 [('o', 'output', '',
3180 _('print output to file with formatted name'), _('FORMAT')),
3183 _('print output to file with formatted name'), _('FORMAT')),
3181 ('', 'switch-parent', None, _('diff against the second parent')),
3184 ('', 'switch-parent', None, _('diff against the second parent')),
3182 ('r', 'rev', [], _('revisions to export'), _('REV')),
3185 ('r', 'rev', [], _('revisions to export'), _('REV')),
3183 ] + diffopts,
3186 ] + diffopts,
3184 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3187 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3185 def export(ui, repo, *changesets, **opts):
3188 def export(ui, repo, *changesets, **opts):
3186 """dump the header and diffs for one or more changesets
3189 """dump the header and diffs for one or more changesets
3187
3190
3188 Print the changeset header and diffs for one or more revisions.
3191 Print the changeset header and diffs for one or more revisions.
3189 If no revision is given, the parent of the working directory is used.
3192 If no revision is given, the parent of the working directory is used.
3190
3193
3191 The information shown in the changeset header is: author, date,
3194 The information shown in the changeset header is: author, date,
3192 branch name (if non-default), changeset hash, parent(s) and commit
3195 branch name (if non-default), changeset hash, parent(s) and commit
3193 comment.
3196 comment.
3194
3197
3195 .. note::
3198 .. note::
3196
3199
3197 export may generate unexpected diff output for merge
3200 export may generate unexpected diff output for merge
3198 changesets, as it will compare the merge changeset against its
3201 changesets, as it will compare the merge changeset against its
3199 first parent only.
3202 first parent only.
3200
3203
3201 Output may be to a file, in which case the name of the file is
3204 Output may be to a file, in which case the name of the file is
3202 given using a format string. The formatting rules are as follows:
3205 given using a format string. The formatting rules are as follows:
3203
3206
3204 :``%%``: literal "%" character
3207 :``%%``: literal "%" character
3205 :``%H``: changeset hash (40 hexadecimal digits)
3208 :``%H``: changeset hash (40 hexadecimal digits)
3206 :``%N``: number of patches being generated
3209 :``%N``: number of patches being generated
3207 :``%R``: changeset revision number
3210 :``%R``: changeset revision number
3208 :``%b``: basename of the exporting repository
3211 :``%b``: basename of the exporting repository
3209 :``%h``: short-form changeset hash (12 hexadecimal digits)
3212 :``%h``: short-form changeset hash (12 hexadecimal digits)
3210 :``%m``: first line of the commit message (only alphanumeric characters)
3213 :``%m``: first line of the commit message (only alphanumeric characters)
3211 :``%n``: zero-padded sequence number, starting at 1
3214 :``%n``: zero-padded sequence number, starting at 1
3212 :``%r``: zero-padded changeset revision number
3215 :``%r``: zero-padded changeset revision number
3213
3216
3214 Without the -a/--text option, export will avoid generating diffs
3217 Without the -a/--text option, export will avoid generating diffs
3215 of files it detects as binary. With -a, export will generate a
3218 of files it detects as binary. With -a, export will generate a
3216 diff anyway, probably with undesirable results.
3219 diff anyway, probably with undesirable results.
3217
3220
3218 Use the -g/--git option to generate diffs in the git extended diff
3221 Use the -g/--git option to generate diffs in the git extended diff
3219 format. See :hg:`help diffs` for more information.
3222 format. See :hg:`help diffs` for more information.
3220
3223
3221 With the --switch-parent option, the diff will be against the
3224 With the --switch-parent option, the diff will be against the
3222 second parent. It can be useful to review a merge.
3225 second parent. It can be useful to review a merge.
3223
3226
3224 .. container:: verbose
3227 .. container:: verbose
3225
3228
3226 Examples:
3229 Examples:
3227
3230
3228 - use export and import to transplant a bugfix to the current
3231 - use export and import to transplant a bugfix to the current
3229 branch::
3232 branch::
3230
3233
3231 hg export -r 9353 | hg import -
3234 hg export -r 9353 | hg import -
3232
3235
3233 - export all the changesets between two revisions to a file with
3236 - export all the changesets between two revisions to a file with
3234 rename information::
3237 rename information::
3235
3238
3236 hg export --git -r 123:150 > changes.txt
3239 hg export --git -r 123:150 > changes.txt
3237
3240
3238 - split outgoing changes into a series of patches with
3241 - split outgoing changes into a series of patches with
3239 descriptive names::
3242 descriptive names::
3240
3243
3241 hg export -r "outgoing()" -o "%n-%m.patch"
3244 hg export -r "outgoing()" -o "%n-%m.patch"
3242
3245
3243 Returns 0 on success.
3246 Returns 0 on success.
3244 """
3247 """
3245 changesets += tuple(opts.get('rev', []))
3248 changesets += tuple(opts.get('rev', []))
3246 if not changesets:
3249 if not changesets:
3247 changesets = ['.']
3250 changesets = ['.']
3248 revs = scmutil.revrange(repo, changesets)
3251 revs = scmutil.revrange(repo, changesets)
3249 if not revs:
3252 if not revs:
3250 raise util.Abort(_("export requires at least one changeset"))
3253 raise util.Abort(_("export requires at least one changeset"))
3251 if len(revs) > 1:
3254 if len(revs) > 1:
3252 ui.note(_('exporting patches:\n'))
3255 ui.note(_('exporting patches:\n'))
3253 else:
3256 else:
3254 ui.note(_('exporting patch:\n'))
3257 ui.note(_('exporting patch:\n'))
3255 cmdutil.export(repo, revs, template=opts.get('output'),
3258 cmdutil.export(repo, revs, template=opts.get('output'),
3256 switch_parent=opts.get('switch_parent'),
3259 switch_parent=opts.get('switch_parent'),
3257 opts=patch.diffallopts(ui, opts))
3260 opts=patch.diffallopts(ui, opts))
3258
3261
3259 @command('files',
3262 @command('files',
3260 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3263 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3261 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3264 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3262 ] + walkopts + formatteropts + subrepoopts,
3265 ] + walkopts + formatteropts + subrepoopts,
3263 _('[OPTION]... [PATTERN]...'))
3266 _('[OPTION]... [PATTERN]...'))
3264 def files(ui, repo, *pats, **opts):
3267 def files(ui, repo, *pats, **opts):
3265 """list tracked files
3268 """list tracked files
3266
3269
3267 Print files under Mercurial control in the working directory or
3270 Print files under Mercurial control in the working directory or
3268 specified revision whose names match the given patterns (excluding
3271 specified revision whose names match the given patterns (excluding
3269 removed files).
3272 removed files).
3270
3273
3271 If no patterns are given to match, this command prints the names
3274 If no patterns are given to match, this command prints the names
3272 of all files under Mercurial control in the working directory.
3275 of all files under Mercurial control in the working directory.
3273
3276
3274 .. container:: verbose
3277 .. container:: verbose
3275
3278
3276 Examples:
3279 Examples:
3277
3280
3278 - list all files under the current directory::
3281 - list all files under the current directory::
3279
3282
3280 hg files .
3283 hg files .
3281
3284
3282 - shows sizes and flags for current revision::
3285 - shows sizes and flags for current revision::
3283
3286
3284 hg files -vr .
3287 hg files -vr .
3285
3288
3286 - list all files named README::
3289 - list all files named README::
3287
3290
3288 hg files -I "**/README"
3291 hg files -I "**/README"
3289
3292
3290 - list all binary files::
3293 - list all binary files::
3291
3294
3292 hg files "set:binary()"
3295 hg files "set:binary()"
3293
3296
3294 - find files containing a regular expression::
3297 - find files containing a regular expression::
3295
3298
3296 hg files "set:grep('bob')"
3299 hg files "set:grep('bob')"
3297
3300
3298 - search tracked file contents with xargs and grep::
3301 - search tracked file contents with xargs and grep::
3299
3302
3300 hg files -0 | xargs -0 grep foo
3303 hg files -0 | xargs -0 grep foo
3301
3304
3302 See :hg:`help patterns` and :hg:`help filesets` for more information
3305 See :hg:`help patterns` and :hg:`help filesets` for more information
3303 on specifying file patterns.
3306 on specifying file patterns.
3304
3307
3305 Returns 0 if a match is found, 1 otherwise.
3308 Returns 0 if a match is found, 1 otherwise.
3306
3309
3307 """
3310 """
3308 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3311 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3309
3312
3310 end = '\n'
3313 end = '\n'
3311 if opts.get('print0'):
3314 if opts.get('print0'):
3312 end = '\0'
3315 end = '\0'
3313 fm = ui.formatter('files', opts)
3316 fm = ui.formatter('files', opts)
3314 fmt = '%s' + end
3317 fmt = '%s' + end
3315
3318
3316 m = scmutil.match(ctx, pats, opts)
3319 m = scmutil.match(ctx, pats, opts)
3317 ret = cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
3320 ret = cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
3318
3321
3319 fm.end()
3322 fm.end()
3320
3323
3321 return ret
3324 return ret
3322
3325
3323 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3326 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3324 def forget(ui, repo, *pats, **opts):
3327 def forget(ui, repo, *pats, **opts):
3325 """forget the specified files on the next commit
3328 """forget the specified files on the next commit
3326
3329
3327 Mark the specified files so they will no longer be tracked
3330 Mark the specified files so they will no longer be tracked
3328 after the next commit.
3331 after the next commit.
3329
3332
3330 This only removes files from the current branch, not from the
3333 This only removes files from the current branch, not from the
3331 entire project history, and it does not delete them from the
3334 entire project history, and it does not delete them from the
3332 working directory.
3335 working directory.
3333
3336
3334 To undo a forget before the next commit, see :hg:`add`.
3337 To undo a forget before the next commit, see :hg:`add`.
3335
3338
3336 .. container:: verbose
3339 .. container:: verbose
3337
3340
3338 Examples:
3341 Examples:
3339
3342
3340 - forget newly-added binary files::
3343 - forget newly-added binary files::
3341
3344
3342 hg forget "set:added() and binary()"
3345 hg forget "set:added() and binary()"
3343
3346
3344 - forget files that would be excluded by .hgignore::
3347 - forget files that would be excluded by .hgignore::
3345
3348
3346 hg forget "set:hgignore()"
3349 hg forget "set:hgignore()"
3347
3350
3348 Returns 0 on success.
3351 Returns 0 on success.
3349 """
3352 """
3350
3353
3351 if not pats:
3354 if not pats:
3352 raise util.Abort(_('no files specified'))
3355 raise util.Abort(_('no files specified'))
3353
3356
3354 m = scmutil.match(repo[None], pats, opts)
3357 m = scmutil.match(repo[None], pats, opts)
3355 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3358 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3356 return rejected and 1 or 0
3359 return rejected and 1 or 0
3357
3360
3358 @command(
3361 @command(
3359 'graft',
3362 'graft',
3360 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3363 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3361 ('c', 'continue', False, _('resume interrupted graft')),
3364 ('c', 'continue', False, _('resume interrupted graft')),
3362 ('e', 'edit', False, _('invoke editor on commit messages')),
3365 ('e', 'edit', False, _('invoke editor on commit messages')),
3363 ('', 'log', None, _('append graft info to log message')),
3366 ('', 'log', None, _('append graft info to log message')),
3364 ('f', 'force', False, _('force graft')),
3367 ('f', 'force', False, _('force graft')),
3365 ('D', 'currentdate', False,
3368 ('D', 'currentdate', False,
3366 _('record the current date as commit date')),
3369 _('record the current date as commit date')),
3367 ('U', 'currentuser', False,
3370 ('U', 'currentuser', False,
3368 _('record the current user as committer'), _('DATE'))]
3371 _('record the current user as committer'), _('DATE'))]
3369 + commitopts2 + mergetoolopts + dryrunopts,
3372 + commitopts2 + mergetoolopts + dryrunopts,
3370 _('[OPTION]... [-r] REV...'))
3373 _('[OPTION]... [-r] REV...'))
3371 def graft(ui, repo, *revs, **opts):
3374 def graft(ui, repo, *revs, **opts):
3372 '''copy changes from other branches onto the current branch
3375 '''copy changes from other branches onto the current branch
3373
3376
3374 This command uses Mercurial's merge logic to copy individual
3377 This command uses Mercurial's merge logic to copy individual
3375 changes from other branches without merging branches in the
3378 changes from other branches without merging branches in the
3376 history graph. This is sometimes known as 'backporting' or
3379 history graph. This is sometimes known as 'backporting' or
3377 'cherry-picking'. By default, graft will copy user, date, and
3380 'cherry-picking'. By default, graft will copy user, date, and
3378 description from the source changesets.
3381 description from the source changesets.
3379
3382
3380 Changesets that are ancestors of the current revision, that have
3383 Changesets that are ancestors of the current revision, that have
3381 already been grafted, or that are merges will be skipped.
3384 already been grafted, or that are merges will be skipped.
3382
3385
3383 If --log is specified, log messages will have a comment appended
3386 If --log is specified, log messages will have a comment appended
3384 of the form::
3387 of the form::
3385
3388
3386 (grafted from CHANGESETHASH)
3389 (grafted from CHANGESETHASH)
3387
3390
3388 If --force is specified, revisions will be grafted even if they
3391 If --force is specified, revisions will be grafted even if they
3389 are already ancestors of or have been grafted to the destination.
3392 are already ancestors of or have been grafted to the destination.
3390 This is useful when the revisions have since been backed out.
3393 This is useful when the revisions have since been backed out.
3391
3394
3392 If a graft merge results in conflicts, the graft process is
3395 If a graft merge results in conflicts, the graft process is
3393 interrupted so that the current merge can be manually resolved.
3396 interrupted so that the current merge can be manually resolved.
3394 Once all conflicts are addressed, the graft process can be
3397 Once all conflicts are addressed, the graft process can be
3395 continued with the -c/--continue option.
3398 continued with the -c/--continue option.
3396
3399
3397 .. note::
3400 .. note::
3398
3401
3399 The -c/--continue option does not reapply earlier options, except
3402 The -c/--continue option does not reapply earlier options, except
3400 for --force.
3403 for --force.
3401
3404
3402 .. container:: verbose
3405 .. container:: verbose
3403
3406
3404 Examples:
3407 Examples:
3405
3408
3406 - copy a single change to the stable branch and edit its description::
3409 - copy a single change to the stable branch and edit its description::
3407
3410
3408 hg update stable
3411 hg update stable
3409 hg graft --edit 9393
3412 hg graft --edit 9393
3410
3413
3411 - graft a range of changesets with one exception, updating dates::
3414 - graft a range of changesets with one exception, updating dates::
3412
3415
3413 hg graft -D "2085::2093 and not 2091"
3416 hg graft -D "2085::2093 and not 2091"
3414
3417
3415 - continue a graft after resolving conflicts::
3418 - continue a graft after resolving conflicts::
3416
3419
3417 hg graft -c
3420 hg graft -c
3418
3421
3419 - show the source of a grafted changeset::
3422 - show the source of a grafted changeset::
3420
3423
3421 hg log --debug -r .
3424 hg log --debug -r .
3422
3425
3423 See :hg:`help revisions` and :hg:`help revsets` for more about
3426 See :hg:`help revisions` and :hg:`help revsets` for more about
3424 specifying revisions.
3427 specifying revisions.
3425
3428
3426 Returns 0 on successful completion.
3429 Returns 0 on successful completion.
3427 '''
3430 '''
3428
3431
3429 revs = list(revs)
3432 revs = list(revs)
3430 revs.extend(opts['rev'])
3433 revs.extend(opts['rev'])
3431
3434
3432 if not opts.get('user') and opts.get('currentuser'):
3435 if not opts.get('user') and opts.get('currentuser'):
3433 opts['user'] = ui.username()
3436 opts['user'] = ui.username()
3434 if not opts.get('date') and opts.get('currentdate'):
3437 if not opts.get('date') and opts.get('currentdate'):
3435 opts['date'] = "%d %d" % util.makedate()
3438 opts['date'] = "%d %d" % util.makedate()
3436
3439
3437 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3440 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3438
3441
3439 cont = False
3442 cont = False
3440 if opts['continue']:
3443 if opts['continue']:
3441 cont = True
3444 cont = True
3442 if revs:
3445 if revs:
3443 raise util.Abort(_("can't specify --continue and revisions"))
3446 raise util.Abort(_("can't specify --continue and revisions"))
3444 # read in unfinished revisions
3447 # read in unfinished revisions
3445 try:
3448 try:
3446 nodes = repo.vfs.read('graftstate').splitlines()
3449 nodes = repo.vfs.read('graftstate').splitlines()
3447 revs = [repo[node].rev() for node in nodes]
3450 revs = [repo[node].rev() for node in nodes]
3448 except IOError, inst:
3451 except IOError, inst:
3449 if inst.errno != errno.ENOENT:
3452 if inst.errno != errno.ENOENT:
3450 raise
3453 raise
3451 raise util.Abort(_("no graft state found, can't continue"))
3454 raise util.Abort(_("no graft state found, can't continue"))
3452 else:
3455 else:
3453 cmdutil.checkunfinished(repo)
3456 cmdutil.checkunfinished(repo)
3454 cmdutil.bailifchanged(repo)
3457 cmdutil.bailifchanged(repo)
3455 if not revs:
3458 if not revs:
3456 raise util.Abort(_('no revisions specified'))
3459 raise util.Abort(_('no revisions specified'))
3457 revs = scmutil.revrange(repo, revs)
3460 revs = scmutil.revrange(repo, revs)
3458
3461
3459 skipped = set()
3462 skipped = set()
3460 # check for merges
3463 # check for merges
3461 for rev in repo.revs('%ld and merge()', revs):
3464 for rev in repo.revs('%ld and merge()', revs):
3462 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3465 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3463 skipped.add(rev)
3466 skipped.add(rev)
3464 revs = [r for r in revs if r not in skipped]
3467 revs = [r for r in revs if r not in skipped]
3465 if not revs:
3468 if not revs:
3466 return -1
3469 return -1
3467
3470
3468 # Don't check in the --continue case, in effect retaining --force across
3471 # Don't check in the --continue case, in effect retaining --force across
3469 # --continues. That's because without --force, any revisions we decided to
3472 # --continues. That's because without --force, any revisions we decided to
3470 # skip would have been filtered out here, so they wouldn't have made their
3473 # skip would have been filtered out here, so they wouldn't have made their
3471 # way to the graftstate. With --force, any revisions we would have otherwise
3474 # way to the graftstate. With --force, any revisions we would have otherwise
3472 # skipped would not have been filtered out, and if they hadn't been applied
3475 # skipped would not have been filtered out, and if they hadn't been applied
3473 # already, they'd have been in the graftstate.
3476 # already, they'd have been in the graftstate.
3474 if not (cont or opts.get('force')):
3477 if not (cont or opts.get('force')):
3475 # check for ancestors of dest branch
3478 # check for ancestors of dest branch
3476 crev = repo['.'].rev()
3479 crev = repo['.'].rev()
3477 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3480 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3478 # Cannot use x.remove(y) on smart set, this has to be a list.
3481 # Cannot use x.remove(y) on smart set, this has to be a list.
3479 # XXX make this lazy in the future
3482 # XXX make this lazy in the future
3480 revs = list(revs)
3483 revs = list(revs)
3481 # don't mutate while iterating, create a copy
3484 # don't mutate while iterating, create a copy
3482 for rev in list(revs):
3485 for rev in list(revs):
3483 if rev in ancestors:
3486 if rev in ancestors:
3484 ui.warn(_('skipping ancestor revision %d:%s\n') %
3487 ui.warn(_('skipping ancestor revision %d:%s\n') %
3485 (rev, repo[rev]))
3488 (rev, repo[rev]))
3486 # XXX remove on list is slow
3489 # XXX remove on list is slow
3487 revs.remove(rev)
3490 revs.remove(rev)
3488 if not revs:
3491 if not revs:
3489 return -1
3492 return -1
3490
3493
3491 # analyze revs for earlier grafts
3494 # analyze revs for earlier grafts
3492 ids = {}
3495 ids = {}
3493 for ctx in repo.set("%ld", revs):
3496 for ctx in repo.set("%ld", revs):
3494 ids[ctx.hex()] = ctx.rev()
3497 ids[ctx.hex()] = ctx.rev()
3495 n = ctx.extra().get('source')
3498 n = ctx.extra().get('source')
3496 if n:
3499 if n:
3497 ids[n] = ctx.rev()
3500 ids[n] = ctx.rev()
3498
3501
3499 # check ancestors for earlier grafts
3502 # check ancestors for earlier grafts
3500 ui.debug('scanning for duplicate grafts\n')
3503 ui.debug('scanning for duplicate grafts\n')
3501
3504
3502 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3505 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3503 ctx = repo[rev]
3506 ctx = repo[rev]
3504 n = ctx.extra().get('source')
3507 n = ctx.extra().get('source')
3505 if n in ids:
3508 if n in ids:
3506 try:
3509 try:
3507 r = repo[n].rev()
3510 r = repo[n].rev()
3508 except error.RepoLookupError:
3511 except error.RepoLookupError:
3509 r = None
3512 r = None
3510 if r in revs:
3513 if r in revs:
3511 ui.warn(_('skipping revision %d:%s '
3514 ui.warn(_('skipping revision %d:%s '
3512 '(already grafted to %d:%s)\n')
3515 '(already grafted to %d:%s)\n')
3513 % (r, repo[r], rev, ctx))
3516 % (r, repo[r], rev, ctx))
3514 revs.remove(r)
3517 revs.remove(r)
3515 elif ids[n] in revs:
3518 elif ids[n] in revs:
3516 if r is None:
3519 if r is None:
3517 ui.warn(_('skipping already grafted revision %d:%s '
3520 ui.warn(_('skipping already grafted revision %d:%s '
3518 '(%d:%s also has unknown origin %s)\n')
3521 '(%d:%s also has unknown origin %s)\n')
3519 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3522 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3520 else:
3523 else:
3521 ui.warn(_('skipping already grafted revision %d:%s '
3524 ui.warn(_('skipping already grafted revision %d:%s '
3522 '(%d:%s also has origin %d:%s)\n')
3525 '(%d:%s also has origin %d:%s)\n')
3523 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3526 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3524 revs.remove(ids[n])
3527 revs.remove(ids[n])
3525 elif ctx.hex() in ids:
3528 elif ctx.hex() in ids:
3526 r = ids[ctx.hex()]
3529 r = ids[ctx.hex()]
3527 ui.warn(_('skipping already grafted revision %d:%s '
3530 ui.warn(_('skipping already grafted revision %d:%s '
3528 '(was grafted from %d:%s)\n') %
3531 '(was grafted from %d:%s)\n') %
3529 (r, repo[r], rev, ctx))
3532 (r, repo[r], rev, ctx))
3530 revs.remove(r)
3533 revs.remove(r)
3531 if not revs:
3534 if not revs:
3532 return -1
3535 return -1
3533
3536
3534 wlock = repo.wlock()
3537 wlock = repo.wlock()
3535 try:
3538 try:
3536 for pos, ctx in enumerate(repo.set("%ld", revs)):
3539 for pos, ctx in enumerate(repo.set("%ld", revs)):
3537 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3540 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3538 ctx.description().split('\n', 1)[0])
3541 ctx.description().split('\n', 1)[0])
3539 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3542 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3540 if names:
3543 if names:
3541 desc += ' (%s)' % ' '.join(names)
3544 desc += ' (%s)' % ' '.join(names)
3542 ui.status(_('grafting %s\n') % desc)
3545 ui.status(_('grafting %s\n') % desc)
3543 if opts.get('dry_run'):
3546 if opts.get('dry_run'):
3544 continue
3547 continue
3545
3548
3546 source = ctx.extra().get('source')
3549 source = ctx.extra().get('source')
3547 extra = {}
3550 extra = {}
3548 if source:
3551 if source:
3549 extra['source'] = source
3552 extra['source'] = source
3550 extra['intermediate-source'] = ctx.hex()
3553 extra['intermediate-source'] = ctx.hex()
3551 else:
3554 else:
3552 extra['source'] = ctx.hex()
3555 extra['source'] = ctx.hex()
3553 user = ctx.user()
3556 user = ctx.user()
3554 if opts.get('user'):
3557 if opts.get('user'):
3555 user = opts['user']
3558 user = opts['user']
3556 date = ctx.date()
3559 date = ctx.date()
3557 if opts.get('date'):
3560 if opts.get('date'):
3558 date = opts['date']
3561 date = opts['date']
3559 message = ctx.description()
3562 message = ctx.description()
3560 if opts.get('log'):
3563 if opts.get('log'):
3561 message += '\n(grafted from %s)' % ctx.hex()
3564 message += '\n(grafted from %s)' % ctx.hex()
3562
3565
3563 # we don't merge the first commit when continuing
3566 # we don't merge the first commit when continuing
3564 if not cont:
3567 if not cont:
3565 # perform the graft merge with p1(rev) as 'ancestor'
3568 # perform the graft merge with p1(rev) as 'ancestor'
3566 try:
3569 try:
3567 # ui.forcemerge is an internal variable, do not document
3570 # ui.forcemerge is an internal variable, do not document
3568 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3571 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3569 'graft')
3572 'graft')
3570 stats = mergemod.graft(repo, ctx, ctx.p1(),
3573 stats = mergemod.graft(repo, ctx, ctx.p1(),
3571 ['local', 'graft'])
3574 ['local', 'graft'])
3572 finally:
3575 finally:
3573 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3576 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3574 # report any conflicts
3577 # report any conflicts
3575 if stats and stats[3] > 0:
3578 if stats and stats[3] > 0:
3576 # write out state for --continue
3579 # write out state for --continue
3577 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3580 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3578 repo.vfs.write('graftstate', ''.join(nodelines))
3581 repo.vfs.write('graftstate', ''.join(nodelines))
3579 raise util.Abort(
3582 raise util.Abort(
3580 _("unresolved conflicts, can't continue"),
3583 _("unresolved conflicts, can't continue"),
3581 hint=_('use hg resolve and hg graft --continue'))
3584 hint=_('use hg resolve and hg graft --continue'))
3582 else:
3585 else:
3583 cont = False
3586 cont = False
3584
3587
3585 # commit
3588 # commit
3586 node = repo.commit(text=message, user=user,
3589 node = repo.commit(text=message, user=user,
3587 date=date, extra=extra, editor=editor)
3590 date=date, extra=extra, editor=editor)
3588 if node is None:
3591 if node is None:
3589 ui.warn(
3592 ui.warn(
3590 _('note: graft of %d:%s created no changes to commit\n') %
3593 _('note: graft of %d:%s created no changes to commit\n') %
3591 (ctx.rev(), ctx))
3594 (ctx.rev(), ctx))
3592 finally:
3595 finally:
3593 wlock.release()
3596 wlock.release()
3594
3597
3595 # remove state when we complete successfully
3598 # remove state when we complete successfully
3596 if not opts.get('dry_run'):
3599 if not opts.get('dry_run'):
3597 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3600 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3598
3601
3599 return 0
3602 return 0
3600
3603
3601 @command('grep',
3604 @command('grep',
3602 [('0', 'print0', None, _('end fields with NUL')),
3605 [('0', 'print0', None, _('end fields with NUL')),
3603 ('', 'all', None, _('print all revisions that match')),
3606 ('', 'all', None, _('print all revisions that match')),
3604 ('a', 'text', None, _('treat all files as text')),
3607 ('a', 'text', None, _('treat all files as text')),
3605 ('f', 'follow', None,
3608 ('f', 'follow', None,
3606 _('follow changeset history,'
3609 _('follow changeset history,'
3607 ' or file history across copies and renames')),
3610 ' or file history across copies and renames')),
3608 ('i', 'ignore-case', None, _('ignore case when matching')),
3611 ('i', 'ignore-case', None, _('ignore case when matching')),
3609 ('l', 'files-with-matches', None,
3612 ('l', 'files-with-matches', None,
3610 _('print only filenames and revisions that match')),
3613 _('print only filenames and revisions that match')),
3611 ('n', 'line-number', None, _('print matching line numbers')),
3614 ('n', 'line-number', None, _('print matching line numbers')),
3612 ('r', 'rev', [],
3615 ('r', 'rev', [],
3613 _('only search files changed within revision range'), _('REV')),
3616 _('only search files changed within revision range'), _('REV')),
3614 ('u', 'user', None, _('list the author (long with -v)')),
3617 ('u', 'user', None, _('list the author (long with -v)')),
3615 ('d', 'date', None, _('list the date (short with -q)')),
3618 ('d', 'date', None, _('list the date (short with -q)')),
3616 ] + walkopts,
3619 ] + walkopts,
3617 _('[OPTION]... PATTERN [FILE]...'),
3620 _('[OPTION]... PATTERN [FILE]...'),
3618 inferrepo=True)
3621 inferrepo=True)
3619 def grep(ui, repo, pattern, *pats, **opts):
3622 def grep(ui, repo, pattern, *pats, **opts):
3620 """search for a pattern in specified files and revisions
3623 """search for a pattern in specified files and revisions
3621
3624
3622 Search revisions of files for a regular expression.
3625 Search revisions of files for a regular expression.
3623
3626
3624 This command behaves differently than Unix grep. It only accepts
3627 This command behaves differently than Unix grep. It only accepts
3625 Python/Perl regexps. It searches repository history, not the
3628 Python/Perl regexps. It searches repository history, not the
3626 working directory. It always prints the revision number in which a
3629 working directory. It always prints the revision number in which a
3627 match appears.
3630 match appears.
3628
3631
3629 By default, grep only prints output for the first revision of a
3632 By default, grep only prints output for the first revision of a
3630 file in which it finds a match. To get it to print every revision
3633 file in which it finds a match. To get it to print every revision
3631 that contains a change in match status ("-" for a match that
3634 that contains a change in match status ("-" for a match that
3632 becomes a non-match, or "+" for a non-match that becomes a match),
3635 becomes a non-match, or "+" for a non-match that becomes a match),
3633 use the --all flag.
3636 use the --all flag.
3634
3637
3635 Returns 0 if a match is found, 1 otherwise.
3638 Returns 0 if a match is found, 1 otherwise.
3636 """
3639 """
3637 reflags = re.M
3640 reflags = re.M
3638 if opts.get('ignore_case'):
3641 if opts.get('ignore_case'):
3639 reflags |= re.I
3642 reflags |= re.I
3640 try:
3643 try:
3641 regexp = util.re.compile(pattern, reflags)
3644 regexp = util.re.compile(pattern, reflags)
3642 except re.error, inst:
3645 except re.error, inst:
3643 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3646 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3644 return 1
3647 return 1
3645 sep, eol = ':', '\n'
3648 sep, eol = ':', '\n'
3646 if opts.get('print0'):
3649 if opts.get('print0'):
3647 sep = eol = '\0'
3650 sep = eol = '\0'
3648
3651
3649 getfile = util.lrucachefunc(repo.file)
3652 getfile = util.lrucachefunc(repo.file)
3650
3653
3651 def matchlines(body):
3654 def matchlines(body):
3652 begin = 0
3655 begin = 0
3653 linenum = 0
3656 linenum = 0
3654 while begin < len(body):
3657 while begin < len(body):
3655 match = regexp.search(body, begin)
3658 match = regexp.search(body, begin)
3656 if not match:
3659 if not match:
3657 break
3660 break
3658 mstart, mend = match.span()
3661 mstart, mend = match.span()
3659 linenum += body.count('\n', begin, mstart) + 1
3662 linenum += body.count('\n', begin, mstart) + 1
3660 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3663 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3661 begin = body.find('\n', mend) + 1 or len(body) + 1
3664 begin = body.find('\n', mend) + 1 or len(body) + 1
3662 lend = begin - 1
3665 lend = begin - 1
3663 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3666 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3664
3667
3665 class linestate(object):
3668 class linestate(object):
3666 def __init__(self, line, linenum, colstart, colend):
3669 def __init__(self, line, linenum, colstart, colend):
3667 self.line = line
3670 self.line = line
3668 self.linenum = linenum
3671 self.linenum = linenum
3669 self.colstart = colstart
3672 self.colstart = colstart
3670 self.colend = colend
3673 self.colend = colend
3671
3674
3672 def __hash__(self):
3675 def __hash__(self):
3673 return hash((self.linenum, self.line))
3676 return hash((self.linenum, self.line))
3674
3677
3675 def __eq__(self, other):
3678 def __eq__(self, other):
3676 return self.line == other.line
3679 return self.line == other.line
3677
3680
3678 def __iter__(self):
3681 def __iter__(self):
3679 yield (self.line[:self.colstart], '')
3682 yield (self.line[:self.colstart], '')
3680 yield (self.line[self.colstart:self.colend], 'grep.match')
3683 yield (self.line[self.colstart:self.colend], 'grep.match')
3681 rest = self.line[self.colend:]
3684 rest = self.line[self.colend:]
3682 while rest != '':
3685 while rest != '':
3683 match = regexp.search(rest)
3686 match = regexp.search(rest)
3684 if not match:
3687 if not match:
3685 yield (rest, '')
3688 yield (rest, '')
3686 break
3689 break
3687 mstart, mend = match.span()
3690 mstart, mend = match.span()
3688 yield (rest[:mstart], '')
3691 yield (rest[:mstart], '')
3689 yield (rest[mstart:mend], 'grep.match')
3692 yield (rest[mstart:mend], 'grep.match')
3690 rest = rest[mend:]
3693 rest = rest[mend:]
3691
3694
3692 matches = {}
3695 matches = {}
3693 copies = {}
3696 copies = {}
3694 def grepbody(fn, rev, body):
3697 def grepbody(fn, rev, body):
3695 matches[rev].setdefault(fn, [])
3698 matches[rev].setdefault(fn, [])
3696 m = matches[rev][fn]
3699 m = matches[rev][fn]
3697 for lnum, cstart, cend, line in matchlines(body):
3700 for lnum, cstart, cend, line in matchlines(body):
3698 s = linestate(line, lnum, cstart, cend)
3701 s = linestate(line, lnum, cstart, cend)
3699 m.append(s)
3702 m.append(s)
3700
3703
3701 def difflinestates(a, b):
3704 def difflinestates(a, b):
3702 sm = difflib.SequenceMatcher(None, a, b)
3705 sm = difflib.SequenceMatcher(None, a, b)
3703 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3706 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3704 if tag == 'insert':
3707 if tag == 'insert':
3705 for i in xrange(blo, bhi):
3708 for i in xrange(blo, bhi):
3706 yield ('+', b[i])
3709 yield ('+', b[i])
3707 elif tag == 'delete':
3710 elif tag == 'delete':
3708 for i in xrange(alo, ahi):
3711 for i in xrange(alo, ahi):
3709 yield ('-', a[i])
3712 yield ('-', a[i])
3710 elif tag == 'replace':
3713 elif tag == 'replace':
3711 for i in xrange(alo, ahi):
3714 for i in xrange(alo, ahi):
3712 yield ('-', a[i])
3715 yield ('-', a[i])
3713 for i in xrange(blo, bhi):
3716 for i in xrange(blo, bhi):
3714 yield ('+', b[i])
3717 yield ('+', b[i])
3715
3718
3716 def display(fn, ctx, pstates, states):
3719 def display(fn, ctx, pstates, states):
3717 rev = ctx.rev()
3720 rev = ctx.rev()
3718 if ui.quiet:
3721 if ui.quiet:
3719 datefunc = util.shortdate
3722 datefunc = util.shortdate
3720 else:
3723 else:
3721 datefunc = util.datestr
3724 datefunc = util.datestr
3722 found = False
3725 found = False
3723 @util.cachefunc
3726 @util.cachefunc
3724 def binary():
3727 def binary():
3725 flog = getfile(fn)
3728 flog = getfile(fn)
3726 return util.binary(flog.read(ctx.filenode(fn)))
3729 return util.binary(flog.read(ctx.filenode(fn)))
3727
3730
3728 if opts.get('all'):
3731 if opts.get('all'):
3729 iter = difflinestates(pstates, states)
3732 iter = difflinestates(pstates, states)
3730 else:
3733 else:
3731 iter = [('', l) for l in states]
3734 iter = [('', l) for l in states]
3732 for change, l in iter:
3735 for change, l in iter:
3733 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3736 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3734
3737
3735 if opts.get('line_number'):
3738 if opts.get('line_number'):
3736 cols.append((str(l.linenum), 'grep.linenumber'))
3739 cols.append((str(l.linenum), 'grep.linenumber'))
3737 if opts.get('all'):
3740 if opts.get('all'):
3738 cols.append((change, 'grep.change'))
3741 cols.append((change, 'grep.change'))
3739 if opts.get('user'):
3742 if opts.get('user'):
3740 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3743 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3741 if opts.get('date'):
3744 if opts.get('date'):
3742 cols.append((datefunc(ctx.date()), 'grep.date'))
3745 cols.append((datefunc(ctx.date()), 'grep.date'))
3743 for col, label in cols[:-1]:
3746 for col, label in cols[:-1]:
3744 ui.write(col, label=label)
3747 ui.write(col, label=label)
3745 ui.write(sep, label='grep.sep')
3748 ui.write(sep, label='grep.sep')
3746 ui.write(cols[-1][0], label=cols[-1][1])
3749 ui.write(cols[-1][0], label=cols[-1][1])
3747 if not opts.get('files_with_matches'):
3750 if not opts.get('files_with_matches'):
3748 ui.write(sep, label='grep.sep')
3751 ui.write(sep, label='grep.sep')
3749 if not opts.get('text') and binary():
3752 if not opts.get('text') and binary():
3750 ui.write(" Binary file matches")
3753 ui.write(" Binary file matches")
3751 else:
3754 else:
3752 for s, label in l:
3755 for s, label in l:
3753 ui.write(s, label=label)
3756 ui.write(s, label=label)
3754 ui.write(eol)
3757 ui.write(eol)
3755 found = True
3758 found = True
3756 if opts.get('files_with_matches'):
3759 if opts.get('files_with_matches'):
3757 break
3760 break
3758 return found
3761 return found
3759
3762
3760 skip = {}
3763 skip = {}
3761 revfiles = {}
3764 revfiles = {}
3762 matchfn = scmutil.match(repo[None], pats, opts)
3765 matchfn = scmutil.match(repo[None], pats, opts)
3763 found = False
3766 found = False
3764 follow = opts.get('follow')
3767 follow = opts.get('follow')
3765
3768
3766 def prep(ctx, fns):
3769 def prep(ctx, fns):
3767 rev = ctx.rev()
3770 rev = ctx.rev()
3768 pctx = ctx.p1()
3771 pctx = ctx.p1()
3769 parent = pctx.rev()
3772 parent = pctx.rev()
3770 matches.setdefault(rev, {})
3773 matches.setdefault(rev, {})
3771 matches.setdefault(parent, {})
3774 matches.setdefault(parent, {})
3772 files = revfiles.setdefault(rev, [])
3775 files = revfiles.setdefault(rev, [])
3773 for fn in fns:
3776 for fn in fns:
3774 flog = getfile(fn)
3777 flog = getfile(fn)
3775 try:
3778 try:
3776 fnode = ctx.filenode(fn)
3779 fnode = ctx.filenode(fn)
3777 except error.LookupError:
3780 except error.LookupError:
3778 continue
3781 continue
3779
3782
3780 copied = flog.renamed(fnode)
3783 copied = flog.renamed(fnode)
3781 copy = follow and copied and copied[0]
3784 copy = follow and copied and copied[0]
3782 if copy:
3785 if copy:
3783 copies.setdefault(rev, {})[fn] = copy
3786 copies.setdefault(rev, {})[fn] = copy
3784 if fn in skip:
3787 if fn in skip:
3785 if copy:
3788 if copy:
3786 skip[copy] = True
3789 skip[copy] = True
3787 continue
3790 continue
3788 files.append(fn)
3791 files.append(fn)
3789
3792
3790 if fn not in matches[rev]:
3793 if fn not in matches[rev]:
3791 grepbody(fn, rev, flog.read(fnode))
3794 grepbody(fn, rev, flog.read(fnode))
3792
3795
3793 pfn = copy or fn
3796 pfn = copy or fn
3794 if pfn not in matches[parent]:
3797 if pfn not in matches[parent]:
3795 try:
3798 try:
3796 fnode = pctx.filenode(pfn)
3799 fnode = pctx.filenode(pfn)
3797 grepbody(pfn, parent, flog.read(fnode))
3800 grepbody(pfn, parent, flog.read(fnode))
3798 except error.LookupError:
3801 except error.LookupError:
3799 pass
3802 pass
3800
3803
3801 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3804 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3802 rev = ctx.rev()
3805 rev = ctx.rev()
3803 parent = ctx.p1().rev()
3806 parent = ctx.p1().rev()
3804 for fn in sorted(revfiles.get(rev, [])):
3807 for fn in sorted(revfiles.get(rev, [])):
3805 states = matches[rev][fn]
3808 states = matches[rev][fn]
3806 copy = copies.get(rev, {}).get(fn)
3809 copy = copies.get(rev, {}).get(fn)
3807 if fn in skip:
3810 if fn in skip:
3808 if copy:
3811 if copy:
3809 skip[copy] = True
3812 skip[copy] = True
3810 continue
3813 continue
3811 pstates = matches.get(parent, {}).get(copy or fn, [])
3814 pstates = matches.get(parent, {}).get(copy or fn, [])
3812 if pstates or states:
3815 if pstates or states:
3813 r = display(fn, ctx, pstates, states)
3816 r = display(fn, ctx, pstates, states)
3814 found = found or r
3817 found = found or r
3815 if r and not opts.get('all'):
3818 if r and not opts.get('all'):
3816 skip[fn] = True
3819 skip[fn] = True
3817 if copy:
3820 if copy:
3818 skip[copy] = True
3821 skip[copy] = True
3819 del matches[rev]
3822 del matches[rev]
3820 del revfiles[rev]
3823 del revfiles[rev]
3821
3824
3822 return not found
3825 return not found
3823
3826
3824 @command('heads',
3827 @command('heads',
3825 [('r', 'rev', '',
3828 [('r', 'rev', '',
3826 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3829 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3827 ('t', 'topo', False, _('show topological heads only')),
3830 ('t', 'topo', False, _('show topological heads only')),
3828 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3831 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3829 ('c', 'closed', False, _('show normal and closed branch heads')),
3832 ('c', 'closed', False, _('show normal and closed branch heads')),
3830 ] + templateopts,
3833 ] + templateopts,
3831 _('[-ct] [-r STARTREV] [REV]...'))
3834 _('[-ct] [-r STARTREV] [REV]...'))
3832 def heads(ui, repo, *branchrevs, **opts):
3835 def heads(ui, repo, *branchrevs, **opts):
3833 """show branch heads
3836 """show branch heads
3834
3837
3835 With no arguments, show all open branch heads in the repository.
3838 With no arguments, show all open branch heads in the repository.
3836 Branch heads are changesets that have no descendants on the
3839 Branch heads are changesets that have no descendants on the
3837 same branch. They are where development generally takes place and
3840 same branch. They are where development generally takes place and
3838 are the usual targets for update and merge operations.
3841 are the usual targets for update and merge operations.
3839
3842
3840 If one or more REVs are given, only open branch heads on the
3843 If one or more REVs are given, only open branch heads on the
3841 branches associated with the specified changesets are shown. This
3844 branches associated with the specified changesets are shown. This
3842 means that you can use :hg:`heads .` to see the heads on the
3845 means that you can use :hg:`heads .` to see the heads on the
3843 currently checked-out branch.
3846 currently checked-out branch.
3844
3847
3845 If -c/--closed is specified, also show branch heads marked closed
3848 If -c/--closed is specified, also show branch heads marked closed
3846 (see :hg:`commit --close-branch`).
3849 (see :hg:`commit --close-branch`).
3847
3850
3848 If STARTREV is specified, only those heads that are descendants of
3851 If STARTREV is specified, only those heads that are descendants of
3849 STARTREV will be displayed.
3852 STARTREV will be displayed.
3850
3853
3851 If -t/--topo is specified, named branch mechanics will be ignored and only
3854 If -t/--topo is specified, named branch mechanics will be ignored and only
3852 topological heads (changesets with no children) will be shown.
3855 topological heads (changesets with no children) will be shown.
3853
3856
3854 Returns 0 if matching heads are found, 1 if not.
3857 Returns 0 if matching heads are found, 1 if not.
3855 """
3858 """
3856
3859
3857 start = None
3860 start = None
3858 if 'rev' in opts:
3861 if 'rev' in opts:
3859 start = scmutil.revsingle(repo, opts['rev'], None).node()
3862 start = scmutil.revsingle(repo, opts['rev'], None).node()
3860
3863
3861 if opts.get('topo'):
3864 if opts.get('topo'):
3862 heads = [repo[h] for h in repo.heads(start)]
3865 heads = [repo[h] for h in repo.heads(start)]
3863 else:
3866 else:
3864 heads = []
3867 heads = []
3865 for branch in repo.branchmap():
3868 for branch in repo.branchmap():
3866 heads += repo.branchheads(branch, start, opts.get('closed'))
3869 heads += repo.branchheads(branch, start, opts.get('closed'))
3867 heads = [repo[h] for h in heads]
3870 heads = [repo[h] for h in heads]
3868
3871
3869 if branchrevs:
3872 if branchrevs:
3870 branches = set(repo[br].branch() for br in branchrevs)
3873 branches = set(repo[br].branch() for br in branchrevs)
3871 heads = [h for h in heads if h.branch() in branches]
3874 heads = [h for h in heads if h.branch() in branches]
3872
3875
3873 if opts.get('active') and branchrevs:
3876 if opts.get('active') and branchrevs:
3874 dagheads = repo.heads(start)
3877 dagheads = repo.heads(start)
3875 heads = [h for h in heads if h.node() in dagheads]
3878 heads = [h for h in heads if h.node() in dagheads]
3876
3879
3877 if branchrevs:
3880 if branchrevs:
3878 haveheads = set(h.branch() for h in heads)
3881 haveheads = set(h.branch() for h in heads)
3879 if branches - haveheads:
3882 if branches - haveheads:
3880 headless = ', '.join(b for b in branches - haveheads)
3883 headless = ', '.join(b for b in branches - haveheads)
3881 msg = _('no open branch heads found on branches %s')
3884 msg = _('no open branch heads found on branches %s')
3882 if opts.get('rev'):
3885 if opts.get('rev'):
3883 msg += _(' (started at %s)') % opts['rev']
3886 msg += _(' (started at %s)') % opts['rev']
3884 ui.warn((msg + '\n') % headless)
3887 ui.warn((msg + '\n') % headless)
3885
3888
3886 if not heads:
3889 if not heads:
3887 return 1
3890 return 1
3888
3891
3889 heads = sorted(heads, key=lambda x: -x.rev())
3892 heads = sorted(heads, key=lambda x: -x.rev())
3890 displayer = cmdutil.show_changeset(ui, repo, opts)
3893 displayer = cmdutil.show_changeset(ui, repo, opts)
3891 for ctx in heads:
3894 for ctx in heads:
3892 displayer.show(ctx)
3895 displayer.show(ctx)
3893 displayer.close()
3896 displayer.close()
3894
3897
3895 @command('help',
3898 @command('help',
3896 [('e', 'extension', None, _('show only help for extensions')),
3899 [('e', 'extension', None, _('show only help for extensions')),
3897 ('c', 'command', None, _('show only help for commands')),
3900 ('c', 'command', None, _('show only help for commands')),
3898 ('k', 'keyword', '', _('show topics matching keyword')),
3901 ('k', 'keyword', '', _('show topics matching keyword')),
3899 ],
3902 ],
3900 _('[-ec] [TOPIC]'),
3903 _('[-ec] [TOPIC]'),
3901 norepo=True)
3904 norepo=True)
3902 def help_(ui, name=None, **opts):
3905 def help_(ui, name=None, **opts):
3903 """show help for a given topic or a help overview
3906 """show help for a given topic or a help overview
3904
3907
3905 With no arguments, print a list of commands with short help messages.
3908 With no arguments, print a list of commands with short help messages.
3906
3909
3907 Given a topic, extension, or command name, print help for that
3910 Given a topic, extension, or command name, print help for that
3908 topic.
3911 topic.
3909
3912
3910 Returns 0 if successful.
3913 Returns 0 if successful.
3911 """
3914 """
3912
3915
3913 textwidth = min(ui.termwidth(), 80) - 2
3916 textwidth = min(ui.termwidth(), 80) - 2
3914
3917
3915 keep = []
3918 keep = []
3916 if ui.verbose:
3919 if ui.verbose:
3917 keep.append('verbose')
3920 keep.append('verbose')
3918 if sys.platform.startswith('win'):
3921 if sys.platform.startswith('win'):
3919 keep.append('windows')
3922 keep.append('windows')
3920 elif sys.platform == 'OpenVMS':
3923 elif sys.platform == 'OpenVMS':
3921 keep.append('vms')
3924 keep.append('vms')
3922 elif sys.platform == 'plan9':
3925 elif sys.platform == 'plan9':
3923 keep.append('plan9')
3926 keep.append('plan9')
3924 else:
3927 else:
3925 keep.append('unix')
3928 keep.append('unix')
3926 keep.append(sys.platform.lower())
3929 keep.append(sys.platform.lower())
3927
3930
3928 section = None
3931 section = None
3929 if name and '.' in name:
3932 if name and '.' in name:
3930 name, section = name.split('.', 1)
3933 name, section = name.split('.', 1)
3931
3934
3932 text = help.help_(ui, name, **opts)
3935 text = help.help_(ui, name, **opts)
3933
3936
3934 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3937 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3935 section=section)
3938 section=section)
3936 if section and not formatted:
3939 if section and not formatted:
3937 raise util.Abort(_("help section not found"))
3940 raise util.Abort(_("help section not found"))
3938
3941
3939 if 'verbose' in pruned:
3942 if 'verbose' in pruned:
3940 keep.append('omitted')
3943 keep.append('omitted')
3941 else:
3944 else:
3942 keep.append('notomitted')
3945 keep.append('notomitted')
3943 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3946 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3944 section=section)
3947 section=section)
3945 ui.write(formatted)
3948 ui.write(formatted)
3946
3949
3947
3950
3948 @command('identify|id',
3951 @command('identify|id',
3949 [('r', 'rev', '',
3952 [('r', 'rev', '',
3950 _('identify the specified revision'), _('REV')),
3953 _('identify the specified revision'), _('REV')),
3951 ('n', 'num', None, _('show local revision number')),
3954 ('n', 'num', None, _('show local revision number')),
3952 ('i', 'id', None, _('show global revision id')),
3955 ('i', 'id', None, _('show global revision id')),
3953 ('b', 'branch', None, _('show branch')),
3956 ('b', 'branch', None, _('show branch')),
3954 ('t', 'tags', None, _('show tags')),
3957 ('t', 'tags', None, _('show tags')),
3955 ('B', 'bookmarks', None, _('show bookmarks')),
3958 ('B', 'bookmarks', None, _('show bookmarks')),
3956 ] + remoteopts,
3959 ] + remoteopts,
3957 _('[-nibtB] [-r REV] [SOURCE]'),
3960 _('[-nibtB] [-r REV] [SOURCE]'),
3958 optionalrepo=True)
3961 optionalrepo=True)
3959 def identify(ui, repo, source=None, rev=None,
3962 def identify(ui, repo, source=None, rev=None,
3960 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3963 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3961 """identify the working directory or specified revision
3964 """identify the working directory or specified revision
3962
3965
3963 Print a summary identifying the repository state at REV using one or
3966 Print a summary identifying the repository state at REV using one or
3964 two parent hash identifiers, followed by a "+" if the working
3967 two parent hash identifiers, followed by a "+" if the working
3965 directory has uncommitted changes, the branch name (if not default),
3968 directory has uncommitted changes, the branch name (if not default),
3966 a list of tags, and a list of bookmarks.
3969 a list of tags, and a list of bookmarks.
3967
3970
3968 When REV is not given, print a summary of the current state of the
3971 When REV is not given, print a summary of the current state of the
3969 repository.
3972 repository.
3970
3973
3971 Specifying a path to a repository root or Mercurial bundle will
3974 Specifying a path to a repository root or Mercurial bundle will
3972 cause lookup to operate on that repository/bundle.
3975 cause lookup to operate on that repository/bundle.
3973
3976
3974 .. container:: verbose
3977 .. container:: verbose
3975
3978
3976 Examples:
3979 Examples:
3977
3980
3978 - generate a build identifier for the working directory::
3981 - generate a build identifier for the working directory::
3979
3982
3980 hg id --id > build-id.dat
3983 hg id --id > build-id.dat
3981
3984
3982 - find the revision corresponding to a tag::
3985 - find the revision corresponding to a tag::
3983
3986
3984 hg id -n -r 1.3
3987 hg id -n -r 1.3
3985
3988
3986 - check the most recent revision of a remote repository::
3989 - check the most recent revision of a remote repository::
3987
3990
3988 hg id -r tip http://selenic.com/hg/
3991 hg id -r tip http://selenic.com/hg/
3989
3992
3990 Returns 0 if successful.
3993 Returns 0 if successful.
3991 """
3994 """
3992
3995
3993 if not repo and not source:
3996 if not repo and not source:
3994 raise util.Abort(_("there is no Mercurial repository here "
3997 raise util.Abort(_("there is no Mercurial repository here "
3995 "(.hg not found)"))
3998 "(.hg not found)"))
3996
3999
3997 if ui.debugflag:
4000 if ui.debugflag:
3998 hexfunc = hex
4001 hexfunc = hex
3999 else:
4002 else:
4000 hexfunc = short
4003 hexfunc = short
4001 default = not (num or id or branch or tags or bookmarks)
4004 default = not (num or id or branch or tags or bookmarks)
4002 output = []
4005 output = []
4003 revs = []
4006 revs = []
4004
4007
4005 if source:
4008 if source:
4006 source, branches = hg.parseurl(ui.expandpath(source))
4009 source, branches = hg.parseurl(ui.expandpath(source))
4007 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
4010 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
4008 repo = peer.local()
4011 repo = peer.local()
4009 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
4012 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
4010
4013
4011 if not repo:
4014 if not repo:
4012 if num or branch or tags:
4015 if num or branch or tags:
4013 raise util.Abort(
4016 raise util.Abort(
4014 _("can't query remote revision number, branch, or tags"))
4017 _("can't query remote revision number, branch, or tags"))
4015 if not rev and revs:
4018 if not rev and revs:
4016 rev = revs[0]
4019 rev = revs[0]
4017 if not rev:
4020 if not rev:
4018 rev = "tip"
4021 rev = "tip"
4019
4022
4020 remoterev = peer.lookup(rev)
4023 remoterev = peer.lookup(rev)
4021 if default or id:
4024 if default or id:
4022 output = [hexfunc(remoterev)]
4025 output = [hexfunc(remoterev)]
4023
4026
4024 def getbms():
4027 def getbms():
4025 bms = []
4028 bms = []
4026
4029
4027 if 'bookmarks' in peer.listkeys('namespaces'):
4030 if 'bookmarks' in peer.listkeys('namespaces'):
4028 hexremoterev = hex(remoterev)
4031 hexremoterev = hex(remoterev)
4029 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
4032 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
4030 if bmr == hexremoterev]
4033 if bmr == hexremoterev]
4031
4034
4032 return sorted(bms)
4035 return sorted(bms)
4033
4036
4034 if bookmarks:
4037 if bookmarks:
4035 output.extend(getbms())
4038 output.extend(getbms())
4036 elif default and not ui.quiet:
4039 elif default and not ui.quiet:
4037 # multiple bookmarks for a single parent separated by '/'
4040 # multiple bookmarks for a single parent separated by '/'
4038 bm = '/'.join(getbms())
4041 bm = '/'.join(getbms())
4039 if bm:
4042 if bm:
4040 output.append(bm)
4043 output.append(bm)
4041 else:
4044 else:
4042 if not rev:
4045 if not rev:
4043 ctx = repo[None]
4046 ctx = repo[None]
4044 parents = ctx.parents()
4047 parents = ctx.parents()
4045 changed = ""
4048 changed = ""
4046 if default or id or num:
4049 if default or id or num:
4047 if (util.any(repo.status())
4050 if (util.any(repo.status())
4048 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
4051 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
4049 changed = '+'
4052 changed = '+'
4050 if default or id:
4053 if default or id:
4051 output = ["%s%s" %
4054 output = ["%s%s" %
4052 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4055 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4053 if num:
4056 if num:
4054 output.append("%s%s" %
4057 output.append("%s%s" %
4055 ('+'.join([str(p.rev()) for p in parents]), changed))
4058 ('+'.join([str(p.rev()) for p in parents]), changed))
4056 else:
4059 else:
4057 ctx = scmutil.revsingle(repo, rev)
4060 ctx = scmutil.revsingle(repo, rev)
4058 if default or id:
4061 if default or id:
4059 output = [hexfunc(ctx.node())]
4062 output = [hexfunc(ctx.node())]
4060 if num:
4063 if num:
4061 output.append(str(ctx.rev()))
4064 output.append(str(ctx.rev()))
4062
4065
4063 if default and not ui.quiet:
4066 if default and not ui.quiet:
4064 b = ctx.branch()
4067 b = ctx.branch()
4065 if b != 'default':
4068 if b != 'default':
4066 output.append("(%s)" % b)
4069 output.append("(%s)" % b)
4067
4070
4068 # multiple tags for a single parent separated by '/'
4071 # multiple tags for a single parent separated by '/'
4069 t = '/'.join(ctx.tags())
4072 t = '/'.join(ctx.tags())
4070 if t:
4073 if t:
4071 output.append(t)
4074 output.append(t)
4072
4075
4073 # multiple bookmarks for a single parent separated by '/'
4076 # multiple bookmarks for a single parent separated by '/'
4074 bm = '/'.join(ctx.bookmarks())
4077 bm = '/'.join(ctx.bookmarks())
4075 if bm:
4078 if bm:
4076 output.append(bm)
4079 output.append(bm)
4077 else:
4080 else:
4078 if branch:
4081 if branch:
4079 output.append(ctx.branch())
4082 output.append(ctx.branch())
4080
4083
4081 if tags:
4084 if tags:
4082 output.extend(ctx.tags())
4085 output.extend(ctx.tags())
4083
4086
4084 if bookmarks:
4087 if bookmarks:
4085 output.extend(ctx.bookmarks())
4088 output.extend(ctx.bookmarks())
4086
4089
4087 ui.write("%s\n" % ' '.join(output))
4090 ui.write("%s\n" % ' '.join(output))
4088
4091
4089 @command('import|patch',
4092 @command('import|patch',
4090 [('p', 'strip', 1,
4093 [('p', 'strip', 1,
4091 _('directory strip option for patch. This has the same '
4094 _('directory strip option for patch. This has the same '
4092 'meaning as the corresponding patch option'), _('NUM')),
4095 'meaning as the corresponding patch option'), _('NUM')),
4093 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4096 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4094 ('e', 'edit', False, _('invoke editor on commit messages')),
4097 ('e', 'edit', False, _('invoke editor on commit messages')),
4095 ('f', 'force', None,
4098 ('f', 'force', None,
4096 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4099 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4097 ('', 'no-commit', None,
4100 ('', 'no-commit', None,
4098 _("don't commit, just update the working directory")),
4101 _("don't commit, just update the working directory")),
4099 ('', 'bypass', None,
4102 ('', 'bypass', None,
4100 _("apply patch without touching the working directory")),
4103 _("apply patch without touching the working directory")),
4101 ('', 'partial', None,
4104 ('', 'partial', None,
4102 _('commit even if some hunks fail')),
4105 _('commit even if some hunks fail')),
4103 ('', 'exact', None,
4106 ('', 'exact', None,
4104 _('apply patch to the nodes from which it was generated')),
4107 _('apply patch to the nodes from which it was generated')),
4105 ('', 'prefix', '',
4108 ('', 'prefix', '',
4106 _('apply patch to subdirectory'), _('DIR')),
4109 _('apply patch to subdirectory'), _('DIR')),
4107 ('', 'import-branch', None,
4110 ('', 'import-branch', None,
4108 _('use any branch information in patch (implied by --exact)'))] +
4111 _('use any branch information in patch (implied by --exact)'))] +
4109 commitopts + commitopts2 + similarityopts,
4112 commitopts + commitopts2 + similarityopts,
4110 _('[OPTION]... PATCH...'))
4113 _('[OPTION]... PATCH...'))
4111 def import_(ui, repo, patch1=None, *patches, **opts):
4114 def import_(ui, repo, patch1=None, *patches, **opts):
4112 """import an ordered set of patches
4115 """import an ordered set of patches
4113
4116
4114 Import a list of patches and commit them individually (unless
4117 Import a list of patches and commit them individually (unless
4115 --no-commit is specified).
4118 --no-commit is specified).
4116
4119
4117 Because import first applies changes to the working directory,
4120 Because import first applies changes to the working directory,
4118 import will abort if there are outstanding changes.
4121 import will abort if there are outstanding changes.
4119
4122
4120 You can import a patch straight from a mail message. Even patches
4123 You can import a patch straight from a mail message. Even patches
4121 as attachments work (to use the body part, it must have type
4124 as attachments work (to use the body part, it must have type
4122 text/plain or text/x-patch). From and Subject headers of email
4125 text/plain or text/x-patch). From and Subject headers of email
4123 message are used as default committer and commit message. All
4126 message are used as default committer and commit message. All
4124 text/plain body parts before first diff are added to commit
4127 text/plain body parts before first diff are added to commit
4125 message.
4128 message.
4126
4129
4127 If the imported patch was generated by :hg:`export`, user and
4130 If the imported patch was generated by :hg:`export`, user and
4128 description from patch override values from message headers and
4131 description from patch override values from message headers and
4129 body. Values given on command line with -m/--message and -u/--user
4132 body. Values given on command line with -m/--message and -u/--user
4130 override these.
4133 override these.
4131
4134
4132 If --exact is specified, import will set the working directory to
4135 If --exact is specified, import will set the working directory to
4133 the parent of each patch before applying it, and will abort if the
4136 the parent of each patch before applying it, and will abort if the
4134 resulting changeset has a different ID than the one recorded in
4137 resulting changeset has a different ID than the one recorded in
4135 the patch. This may happen due to character set problems or other
4138 the patch. This may happen due to character set problems or other
4136 deficiencies in the text patch format.
4139 deficiencies in the text patch format.
4137
4140
4138 Use --bypass to apply and commit patches directly to the
4141 Use --bypass to apply and commit patches directly to the
4139 repository, not touching the working directory. Without --exact,
4142 repository, not touching the working directory. Without --exact,
4140 patches will be applied on top of the working directory parent
4143 patches will be applied on top of the working directory parent
4141 revision.
4144 revision.
4142
4145
4143 With -s/--similarity, hg will attempt to discover renames and
4146 With -s/--similarity, hg will attempt to discover renames and
4144 copies in the patch in the same way as :hg:`addremove`.
4147 copies in the patch in the same way as :hg:`addremove`.
4145
4148
4146 Use --partial to ensure a changeset will be created from the patch
4149 Use --partial to ensure a changeset will be created from the patch
4147 even if some hunks fail to apply. Hunks that fail to apply will be
4150 even if some hunks fail to apply. Hunks that fail to apply will be
4148 written to a <target-file>.rej file. Conflicts can then be resolved
4151 written to a <target-file>.rej file. Conflicts can then be resolved
4149 by hand before :hg:`commit --amend` is run to update the created
4152 by hand before :hg:`commit --amend` is run to update the created
4150 changeset. This flag exists to let people import patches that
4153 changeset. This flag exists to let people import patches that
4151 partially apply without losing the associated metadata (author,
4154 partially apply without losing the associated metadata (author,
4152 date, description, ...). Note that when none of the hunk applies
4155 date, description, ...). Note that when none of the hunk applies
4153 cleanly, :hg:`import --partial` will create an empty changeset,
4156 cleanly, :hg:`import --partial` will create an empty changeset,
4154 importing only the patch metadata.
4157 importing only the patch metadata.
4155
4158
4156 To read a patch from standard input, use "-" as the patch name. If
4159 To read a patch from standard input, use "-" as the patch name. If
4157 a URL is specified, the patch will be downloaded from it.
4160 a URL is specified, the patch will be downloaded from it.
4158 See :hg:`help dates` for a list of formats valid for -d/--date.
4161 See :hg:`help dates` for a list of formats valid for -d/--date.
4159
4162
4160 .. container:: verbose
4163 .. container:: verbose
4161
4164
4162 Examples:
4165 Examples:
4163
4166
4164 - import a traditional patch from a website and detect renames::
4167 - import a traditional patch from a website and detect renames::
4165
4168
4166 hg import -s 80 http://example.com/bugfix.patch
4169 hg import -s 80 http://example.com/bugfix.patch
4167
4170
4168 - import a changeset from an hgweb server::
4171 - import a changeset from an hgweb server::
4169
4172
4170 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
4173 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
4171
4174
4172 - import all the patches in an Unix-style mbox::
4175 - import all the patches in an Unix-style mbox::
4173
4176
4174 hg import incoming-patches.mbox
4177 hg import incoming-patches.mbox
4175
4178
4176 - attempt to exactly restore an exported changeset (not always
4179 - attempt to exactly restore an exported changeset (not always
4177 possible)::
4180 possible)::
4178
4181
4179 hg import --exact proposed-fix.patch
4182 hg import --exact proposed-fix.patch
4180
4183
4181 Returns 0 on success, 1 on partial success (see --partial).
4184 Returns 0 on success, 1 on partial success (see --partial).
4182 """
4185 """
4183
4186
4184 if not patch1:
4187 if not patch1:
4185 raise util.Abort(_('need at least one patch to import'))
4188 raise util.Abort(_('need at least one patch to import'))
4186
4189
4187 patches = (patch1,) + patches
4190 patches = (patch1,) + patches
4188
4191
4189 date = opts.get('date')
4192 date = opts.get('date')
4190 if date:
4193 if date:
4191 opts['date'] = util.parsedate(date)
4194 opts['date'] = util.parsedate(date)
4192
4195
4193 update = not opts.get('bypass')
4196 update = not opts.get('bypass')
4194 if not update and opts.get('no_commit'):
4197 if not update and opts.get('no_commit'):
4195 raise util.Abort(_('cannot use --no-commit with --bypass'))
4198 raise util.Abort(_('cannot use --no-commit with --bypass'))
4196 try:
4199 try:
4197 sim = float(opts.get('similarity') or 0)
4200 sim = float(opts.get('similarity') or 0)
4198 except ValueError:
4201 except ValueError:
4199 raise util.Abort(_('similarity must be a number'))
4202 raise util.Abort(_('similarity must be a number'))
4200 if sim < 0 or sim > 100:
4203 if sim < 0 or sim > 100:
4201 raise util.Abort(_('similarity must be between 0 and 100'))
4204 raise util.Abort(_('similarity must be between 0 and 100'))
4202 if sim and not update:
4205 if sim and not update:
4203 raise util.Abort(_('cannot use --similarity with --bypass'))
4206 raise util.Abort(_('cannot use --similarity with --bypass'))
4204 if opts.get('exact') and opts.get('edit'):
4207 if opts.get('exact') and opts.get('edit'):
4205 raise util.Abort(_('cannot use --exact with --edit'))
4208 raise util.Abort(_('cannot use --exact with --edit'))
4206 if opts.get('exact') and opts.get('prefix'):
4209 if opts.get('exact') and opts.get('prefix'):
4207 raise util.Abort(_('cannot use --exact with --prefix'))
4210 raise util.Abort(_('cannot use --exact with --prefix'))
4208
4211
4209 if update:
4212 if update:
4210 cmdutil.checkunfinished(repo)
4213 cmdutil.checkunfinished(repo)
4211 if (opts.get('exact') or not opts.get('force')) and update:
4214 if (opts.get('exact') or not opts.get('force')) and update:
4212 cmdutil.bailifchanged(repo)
4215 cmdutil.bailifchanged(repo)
4213
4216
4214 base = opts["base"]
4217 base = opts["base"]
4215 wlock = dsguard = lock = tr = None
4218 wlock = dsguard = lock = tr = None
4216 msgs = []
4219 msgs = []
4217 ret = 0
4220 ret = 0
4218
4221
4219
4222
4220 try:
4223 try:
4221 try:
4224 try:
4222 wlock = repo.wlock()
4225 wlock = repo.wlock()
4223 dsguard = cmdutil.dirstateguard(repo, 'import')
4226 dsguard = cmdutil.dirstateguard(repo, 'import')
4224 if not opts.get('no_commit'):
4227 if not opts.get('no_commit'):
4225 lock = repo.lock()
4228 lock = repo.lock()
4226 tr = repo.transaction('import')
4229 tr = repo.transaction('import')
4227 parents = repo.parents()
4230 parents = repo.parents()
4228 for patchurl in patches:
4231 for patchurl in patches:
4229 if patchurl == '-':
4232 if patchurl == '-':
4230 ui.status(_('applying patch from stdin\n'))
4233 ui.status(_('applying patch from stdin\n'))
4231 patchfile = ui.fin
4234 patchfile = ui.fin
4232 patchurl = 'stdin' # for error message
4235 patchurl = 'stdin' # for error message
4233 else:
4236 else:
4234 patchurl = os.path.join(base, patchurl)
4237 patchurl = os.path.join(base, patchurl)
4235 ui.status(_('applying %s\n') % patchurl)
4238 ui.status(_('applying %s\n') % patchurl)
4236 patchfile = hg.openpath(ui, patchurl)
4239 patchfile = hg.openpath(ui, patchurl)
4237
4240
4238 haspatch = False
4241 haspatch = False
4239 for hunk in patch.split(patchfile):
4242 for hunk in patch.split(patchfile):
4240 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4243 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4241 parents, opts,
4244 parents, opts,
4242 msgs, hg.clean)
4245 msgs, hg.clean)
4243 if msg:
4246 if msg:
4244 haspatch = True
4247 haspatch = True
4245 ui.note(msg + '\n')
4248 ui.note(msg + '\n')
4246 if update or opts.get('exact'):
4249 if update or opts.get('exact'):
4247 parents = repo.parents()
4250 parents = repo.parents()
4248 else:
4251 else:
4249 parents = [repo[node]]
4252 parents = [repo[node]]
4250 if rej:
4253 if rej:
4251 ui.write_err(_("patch applied partially\n"))
4254 ui.write_err(_("patch applied partially\n"))
4252 ui.write_err(_("(fix the .rej files and run "
4255 ui.write_err(_("(fix the .rej files and run "
4253 "`hg commit --amend`)\n"))
4256 "`hg commit --amend`)\n"))
4254 ret = 1
4257 ret = 1
4255 break
4258 break
4256
4259
4257 if not haspatch:
4260 if not haspatch:
4258 raise util.Abort(_('%s: no diffs found') % patchurl)
4261 raise util.Abort(_('%s: no diffs found') % patchurl)
4259
4262
4260 if tr:
4263 if tr:
4261 tr.close()
4264 tr.close()
4262 if msgs:
4265 if msgs:
4263 repo.savecommitmessage('\n* * *\n'.join(msgs))
4266 repo.savecommitmessage('\n* * *\n'.join(msgs))
4264 dsguard.close()
4267 dsguard.close()
4265 return ret
4268 return ret
4266 finally:
4269 finally:
4267 # TODO: get rid of this meaningless try/finally enclosing.
4270 # TODO: get rid of this meaningless try/finally enclosing.
4268 # this is kept only to reduce changes in a patch.
4271 # this is kept only to reduce changes in a patch.
4269 pass
4272 pass
4270 finally:
4273 finally:
4271 if tr:
4274 if tr:
4272 tr.release()
4275 tr.release()
4273 release(lock, dsguard, wlock)
4276 release(lock, dsguard, wlock)
4274
4277
4275 @command('incoming|in',
4278 @command('incoming|in',
4276 [('f', 'force', None,
4279 [('f', 'force', None,
4277 _('run even if remote repository is unrelated')),
4280 _('run even if remote repository is unrelated')),
4278 ('n', 'newest-first', None, _('show newest record first')),
4281 ('n', 'newest-first', None, _('show newest record first')),
4279 ('', 'bundle', '',
4282 ('', 'bundle', '',
4280 _('file to store the bundles into'), _('FILE')),
4283 _('file to store the bundles into'), _('FILE')),
4281 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4284 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4282 ('B', 'bookmarks', False, _("compare bookmarks")),
4285 ('B', 'bookmarks', False, _("compare bookmarks")),
4283 ('b', 'branch', [],
4286 ('b', 'branch', [],
4284 _('a specific branch you would like to pull'), _('BRANCH')),
4287 _('a specific branch you would like to pull'), _('BRANCH')),
4285 ] + logopts + remoteopts + subrepoopts,
4288 ] + logopts + remoteopts + subrepoopts,
4286 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4289 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4287 def incoming(ui, repo, source="default", **opts):
4290 def incoming(ui, repo, source="default", **opts):
4288 """show new changesets found in source
4291 """show new changesets found in source
4289
4292
4290 Show new changesets found in the specified path/URL or the default
4293 Show new changesets found in the specified path/URL or the default
4291 pull location. These are the changesets that would have been pulled
4294 pull location. These are the changesets that would have been pulled
4292 if a pull at the time you issued this command.
4295 if a pull at the time you issued this command.
4293
4296
4294 See pull for valid source format details.
4297 See pull for valid source format details.
4295
4298
4296 .. container:: verbose
4299 .. container:: verbose
4297
4300
4298 With -B/--bookmarks, the result of bookmark comparison between
4301 With -B/--bookmarks, the result of bookmark comparison between
4299 local and remote repositories is displayed. With -v/--verbose,
4302 local and remote repositories is displayed. With -v/--verbose,
4300 status is also displayed for each bookmark like below::
4303 status is also displayed for each bookmark like below::
4301
4304
4302 BM1 01234567890a added
4305 BM1 01234567890a added
4303 BM2 1234567890ab advanced
4306 BM2 1234567890ab advanced
4304 BM3 234567890abc diverged
4307 BM3 234567890abc diverged
4305 BM4 34567890abcd changed
4308 BM4 34567890abcd changed
4306
4309
4307 The action taken locally when pulling depends on the
4310 The action taken locally when pulling depends on the
4308 status of each bookmark:
4311 status of each bookmark:
4309
4312
4310 :``added``: pull will create it
4313 :``added``: pull will create it
4311 :``advanced``: pull will update it
4314 :``advanced``: pull will update it
4312 :``diverged``: pull will create a divergent bookmark
4315 :``diverged``: pull will create a divergent bookmark
4313 :``changed``: result depends on remote changesets
4316 :``changed``: result depends on remote changesets
4314
4317
4315 From the point of view of pulling behavior, bookmark
4318 From the point of view of pulling behavior, bookmark
4316 existing only in the remote repository are treated as ``added``,
4319 existing only in the remote repository are treated as ``added``,
4317 even if it is in fact locally deleted.
4320 even if it is in fact locally deleted.
4318
4321
4319 .. container:: verbose
4322 .. container:: verbose
4320
4323
4321 For remote repository, using --bundle avoids downloading the
4324 For remote repository, using --bundle avoids downloading the
4322 changesets twice if the incoming is followed by a pull.
4325 changesets twice if the incoming is followed by a pull.
4323
4326
4324 Examples:
4327 Examples:
4325
4328
4326 - show incoming changes with patches and full description::
4329 - show incoming changes with patches and full description::
4327
4330
4328 hg incoming -vp
4331 hg incoming -vp
4329
4332
4330 - show incoming changes excluding merges, store a bundle::
4333 - show incoming changes excluding merges, store a bundle::
4331
4334
4332 hg in -vpM --bundle incoming.hg
4335 hg in -vpM --bundle incoming.hg
4333 hg pull incoming.hg
4336 hg pull incoming.hg
4334
4337
4335 - briefly list changes inside a bundle::
4338 - briefly list changes inside a bundle::
4336
4339
4337 hg in changes.hg -T "{desc|firstline}\\n"
4340 hg in changes.hg -T "{desc|firstline}\\n"
4338
4341
4339 Returns 0 if there are incoming changes, 1 otherwise.
4342 Returns 0 if there are incoming changes, 1 otherwise.
4340 """
4343 """
4341 if opts.get('graph'):
4344 if opts.get('graph'):
4342 cmdutil.checkunsupportedgraphflags([], opts)
4345 cmdutil.checkunsupportedgraphflags([], opts)
4343 def display(other, chlist, displayer):
4346 def display(other, chlist, displayer):
4344 revdag = cmdutil.graphrevs(other, chlist, opts)
4347 revdag = cmdutil.graphrevs(other, chlist, opts)
4345 showparents = [ctx.node() for ctx in repo[None].parents()]
4348 showparents = [ctx.node() for ctx in repo[None].parents()]
4346 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4349 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4347 graphmod.asciiedges)
4350 graphmod.asciiedges)
4348
4351
4349 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4352 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4350 return 0
4353 return 0
4351
4354
4352 if opts.get('bundle') and opts.get('subrepos'):
4355 if opts.get('bundle') and opts.get('subrepos'):
4353 raise util.Abort(_('cannot combine --bundle and --subrepos'))
4356 raise util.Abort(_('cannot combine --bundle and --subrepos'))
4354
4357
4355 if opts.get('bookmarks'):
4358 if opts.get('bookmarks'):
4356 source, branches = hg.parseurl(ui.expandpath(source),
4359 source, branches = hg.parseurl(ui.expandpath(source),
4357 opts.get('branch'))
4360 opts.get('branch'))
4358 other = hg.peer(repo, opts, source)
4361 other = hg.peer(repo, opts, source)
4359 if 'bookmarks' not in other.listkeys('namespaces'):
4362 if 'bookmarks' not in other.listkeys('namespaces'):
4360 ui.warn(_("remote doesn't support bookmarks\n"))
4363 ui.warn(_("remote doesn't support bookmarks\n"))
4361 return 0
4364 return 0
4362 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4365 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4363 return bookmarks.incoming(ui, repo, other)
4366 return bookmarks.incoming(ui, repo, other)
4364
4367
4365 repo._subtoppath = ui.expandpath(source)
4368 repo._subtoppath = ui.expandpath(source)
4366 try:
4369 try:
4367 return hg.incoming(ui, repo, source, opts)
4370 return hg.incoming(ui, repo, source, opts)
4368 finally:
4371 finally:
4369 del repo._subtoppath
4372 del repo._subtoppath
4370
4373
4371
4374
4372 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4375 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4373 norepo=True)
4376 norepo=True)
4374 def init(ui, dest=".", **opts):
4377 def init(ui, dest=".", **opts):
4375 """create a new repository in the given directory
4378 """create a new repository in the given directory
4376
4379
4377 Initialize a new repository in the given directory. If the given
4380 Initialize a new repository in the given directory. If the given
4378 directory does not exist, it will be created.
4381 directory does not exist, it will be created.
4379
4382
4380 If no directory is given, the current directory is used.
4383 If no directory is given, the current directory is used.
4381
4384
4382 It is possible to specify an ``ssh://`` URL as the destination.
4385 It is possible to specify an ``ssh://`` URL as the destination.
4383 See :hg:`help urls` for more information.
4386 See :hg:`help urls` for more information.
4384
4387
4385 Returns 0 on success.
4388 Returns 0 on success.
4386 """
4389 """
4387 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4390 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4388
4391
4389 @command('locate',
4392 @command('locate',
4390 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4393 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4391 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4394 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4392 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4395 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4393 ] + walkopts,
4396 ] + walkopts,
4394 _('[OPTION]... [PATTERN]...'))
4397 _('[OPTION]... [PATTERN]...'))
4395 def locate(ui, repo, *pats, **opts):
4398 def locate(ui, repo, *pats, **opts):
4396 """locate files matching specific patterns (DEPRECATED)
4399 """locate files matching specific patterns (DEPRECATED)
4397
4400
4398 Print files under Mercurial control in the working directory whose
4401 Print files under Mercurial control in the working directory whose
4399 names match the given patterns.
4402 names match the given patterns.
4400
4403
4401 By default, this command searches all directories in the working
4404 By default, this command searches all directories in the working
4402 directory. To search just the current directory and its
4405 directory. To search just the current directory and its
4403 subdirectories, use "--include .".
4406 subdirectories, use "--include .".
4404
4407
4405 If no patterns are given to match, this command prints the names
4408 If no patterns are given to match, this command prints the names
4406 of all files under Mercurial control in the working directory.
4409 of all files under Mercurial control in the working directory.
4407
4410
4408 If you want to feed the output of this command into the "xargs"
4411 If you want to feed the output of this command into the "xargs"
4409 command, use the -0 option to both this command and "xargs". This
4412 command, use the -0 option to both this command and "xargs". This
4410 will avoid the problem of "xargs" treating single filenames that
4413 will avoid the problem of "xargs" treating single filenames that
4411 contain whitespace as multiple filenames.
4414 contain whitespace as multiple filenames.
4412
4415
4413 See :hg:`help files` for a more versatile command.
4416 See :hg:`help files` for a more versatile command.
4414
4417
4415 Returns 0 if a match is found, 1 otherwise.
4418 Returns 0 if a match is found, 1 otherwise.
4416 """
4419 """
4417 if opts.get('print0'):
4420 if opts.get('print0'):
4418 end = '\0'
4421 end = '\0'
4419 else:
4422 else:
4420 end = '\n'
4423 end = '\n'
4421 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4424 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4422
4425
4423 ret = 1
4426 ret = 1
4424 ctx = repo[rev]
4427 ctx = repo[rev]
4425 m = scmutil.match(ctx, pats, opts, default='relglob')
4428 m = scmutil.match(ctx, pats, opts, default='relglob')
4426 m.bad = lambda x, y: False
4429 m.bad = lambda x, y: False
4427
4430
4428 for abs in ctx.matches(m):
4431 for abs in ctx.matches(m):
4429 if opts.get('fullpath'):
4432 if opts.get('fullpath'):
4430 ui.write(repo.wjoin(abs), end)
4433 ui.write(repo.wjoin(abs), end)
4431 else:
4434 else:
4432 ui.write(((pats and m.rel(abs)) or abs), end)
4435 ui.write(((pats and m.rel(abs)) or abs), end)
4433 ret = 0
4436 ret = 0
4434
4437
4435 return ret
4438 return ret
4436
4439
4437 @command('^log|history',
4440 @command('^log|history',
4438 [('f', 'follow', None,
4441 [('f', 'follow', None,
4439 _('follow changeset history, or file history across copies and renames')),
4442 _('follow changeset history, or file history across copies and renames')),
4440 ('', 'follow-first', None,
4443 ('', 'follow-first', None,
4441 _('only follow the first parent of merge changesets (DEPRECATED)')),
4444 _('only follow the first parent of merge changesets (DEPRECATED)')),
4442 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4445 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4443 ('C', 'copies', None, _('show copied files')),
4446 ('C', 'copies', None, _('show copied files')),
4444 ('k', 'keyword', [],
4447 ('k', 'keyword', [],
4445 _('do case-insensitive search for a given text'), _('TEXT')),
4448 _('do case-insensitive search for a given text'), _('TEXT')),
4446 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4449 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4447 ('', 'removed', None, _('include revisions where files were removed')),
4450 ('', 'removed', None, _('include revisions where files were removed')),
4448 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4451 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4449 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4452 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4450 ('', 'only-branch', [],
4453 ('', 'only-branch', [],
4451 _('show only changesets within the given named branch (DEPRECATED)'),
4454 _('show only changesets within the given named branch (DEPRECATED)'),
4452 _('BRANCH')),
4455 _('BRANCH')),
4453 ('b', 'branch', [],
4456 ('b', 'branch', [],
4454 _('show changesets within the given named branch'), _('BRANCH')),
4457 _('show changesets within the given named branch'), _('BRANCH')),
4455 ('P', 'prune', [],
4458 ('P', 'prune', [],
4456 _('do not display revision or any of its ancestors'), _('REV')),
4459 _('do not display revision or any of its ancestors'), _('REV')),
4457 ] + logopts + walkopts,
4460 ] + logopts + walkopts,
4458 _('[OPTION]... [FILE]'),
4461 _('[OPTION]... [FILE]'),
4459 inferrepo=True)
4462 inferrepo=True)
4460 def log(ui, repo, *pats, **opts):
4463 def log(ui, repo, *pats, **opts):
4461 """show revision history of entire repository or files
4464 """show revision history of entire repository or files
4462
4465
4463 Print the revision history of the specified files or the entire
4466 Print the revision history of the specified files or the entire
4464 project.
4467 project.
4465
4468
4466 If no revision range is specified, the default is ``tip:0`` unless
4469 If no revision range is specified, the default is ``tip:0`` unless
4467 --follow is set, in which case the working directory parent is
4470 --follow is set, in which case the working directory parent is
4468 used as the starting revision.
4471 used as the starting revision.
4469
4472
4470 File history is shown without following rename or copy history of
4473 File history is shown without following rename or copy history of
4471 files. Use -f/--follow with a filename to follow history across
4474 files. Use -f/--follow with a filename to follow history across
4472 renames and copies. --follow without a filename will only show
4475 renames and copies. --follow without a filename will only show
4473 ancestors or descendants of the starting revision.
4476 ancestors or descendants of the starting revision.
4474
4477
4475 By default this command prints revision number and changeset id,
4478 By default this command prints revision number and changeset id,
4476 tags, non-trivial parents, user, date and time, and a summary for
4479 tags, non-trivial parents, user, date and time, and a summary for
4477 each commit. When the -v/--verbose switch is used, the list of
4480 each commit. When the -v/--verbose switch is used, the list of
4478 changed files and full commit message are shown.
4481 changed files and full commit message are shown.
4479
4482
4480 With --graph the revisions are shown as an ASCII art DAG with the most
4483 With --graph the revisions are shown as an ASCII art DAG with the most
4481 recent changeset at the top.
4484 recent changeset at the top.
4482 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4485 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4483 and '+' represents a fork where the changeset from the lines below is a
4486 and '+' represents a fork where the changeset from the lines below is a
4484 parent of the 'o' merge on the same line.
4487 parent of the 'o' merge on the same line.
4485
4488
4486 .. note::
4489 .. note::
4487
4490
4488 log -p/--patch may generate unexpected diff output for merge
4491 log -p/--patch may generate unexpected diff output for merge
4489 changesets, as it will only compare the merge changeset against
4492 changesets, as it will only compare the merge changeset against
4490 its first parent. Also, only files different from BOTH parents
4493 its first parent. Also, only files different from BOTH parents
4491 will appear in files:.
4494 will appear in files:.
4492
4495
4493 .. note::
4496 .. note::
4494
4497
4495 for performance reasons, log FILE may omit duplicate changes
4498 for performance reasons, log FILE may omit duplicate changes
4496 made on branches and will not show removals or mode changes. To
4499 made on branches and will not show removals or mode changes. To
4497 see all such changes, use the --removed switch.
4500 see all such changes, use the --removed switch.
4498
4501
4499 .. container:: verbose
4502 .. container:: verbose
4500
4503
4501 Some examples:
4504 Some examples:
4502
4505
4503 - changesets with full descriptions and file lists::
4506 - changesets with full descriptions and file lists::
4504
4507
4505 hg log -v
4508 hg log -v
4506
4509
4507 - changesets ancestral to the working directory::
4510 - changesets ancestral to the working directory::
4508
4511
4509 hg log -f
4512 hg log -f
4510
4513
4511 - last 10 commits on the current branch::
4514 - last 10 commits on the current branch::
4512
4515
4513 hg log -l 10 -b .
4516 hg log -l 10 -b .
4514
4517
4515 - changesets showing all modifications of a file, including removals::
4518 - changesets showing all modifications of a file, including removals::
4516
4519
4517 hg log --removed file.c
4520 hg log --removed file.c
4518
4521
4519 - all changesets that touch a directory, with diffs, excluding merges::
4522 - all changesets that touch a directory, with diffs, excluding merges::
4520
4523
4521 hg log -Mp lib/
4524 hg log -Mp lib/
4522
4525
4523 - all revision numbers that match a keyword::
4526 - all revision numbers that match a keyword::
4524
4527
4525 hg log -k bug --template "{rev}\\n"
4528 hg log -k bug --template "{rev}\\n"
4526
4529
4527 - list available log templates::
4530 - list available log templates::
4528
4531
4529 hg log -T list
4532 hg log -T list
4530
4533
4531 - check if a given changeset is included in a tagged release::
4534 - check if a given changeset is included in a tagged release::
4532
4535
4533 hg log -r "a21ccf and ancestor(1.9)"
4536 hg log -r "a21ccf and ancestor(1.9)"
4534
4537
4535 - find all changesets by some user in a date range::
4538 - find all changesets by some user in a date range::
4536
4539
4537 hg log -k alice -d "may 2008 to jul 2008"
4540 hg log -k alice -d "may 2008 to jul 2008"
4538
4541
4539 - summary of all changesets after the last tag::
4542 - summary of all changesets after the last tag::
4540
4543
4541 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4544 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4542
4545
4543 See :hg:`help dates` for a list of formats valid for -d/--date.
4546 See :hg:`help dates` for a list of formats valid for -d/--date.
4544
4547
4545 See :hg:`help revisions` and :hg:`help revsets` for more about
4548 See :hg:`help revisions` and :hg:`help revsets` for more about
4546 specifying revisions.
4549 specifying revisions.
4547
4550
4548 See :hg:`help templates` for more about pre-packaged styles and
4551 See :hg:`help templates` for more about pre-packaged styles and
4549 specifying custom templates.
4552 specifying custom templates.
4550
4553
4551 Returns 0 on success.
4554 Returns 0 on success.
4552
4555
4553 """
4556 """
4554 if opts.get('follow') and opts.get('rev'):
4557 if opts.get('follow') and opts.get('rev'):
4555 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
4558 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
4556 del opts['follow']
4559 del opts['follow']
4557
4560
4558 if opts.get('graph'):
4561 if opts.get('graph'):
4559 return cmdutil.graphlog(ui, repo, *pats, **opts)
4562 return cmdutil.graphlog(ui, repo, *pats, **opts)
4560
4563
4561 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4564 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4562 limit = cmdutil.loglimit(opts)
4565 limit = cmdutil.loglimit(opts)
4563 count = 0
4566 count = 0
4564
4567
4565 getrenamed = None
4568 getrenamed = None
4566 if opts.get('copies'):
4569 if opts.get('copies'):
4567 endrev = None
4570 endrev = None
4568 if opts.get('rev'):
4571 if opts.get('rev'):
4569 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4572 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4570 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4573 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4571
4574
4572 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4575 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4573 for rev in revs:
4576 for rev in revs:
4574 if count == limit:
4577 if count == limit:
4575 break
4578 break
4576 ctx = repo[rev]
4579 ctx = repo[rev]
4577 copies = None
4580 copies = None
4578 if getrenamed is not None and rev:
4581 if getrenamed is not None and rev:
4579 copies = []
4582 copies = []
4580 for fn in ctx.files():
4583 for fn in ctx.files():
4581 rename = getrenamed(fn, rev)
4584 rename = getrenamed(fn, rev)
4582 if rename:
4585 if rename:
4583 copies.append((fn, rename[0]))
4586 copies.append((fn, rename[0]))
4584 if filematcher:
4587 if filematcher:
4585 revmatchfn = filematcher(ctx.rev())
4588 revmatchfn = filematcher(ctx.rev())
4586 else:
4589 else:
4587 revmatchfn = None
4590 revmatchfn = None
4588 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4591 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4589 if displayer.flush(rev):
4592 if displayer.flush(rev):
4590 count += 1
4593 count += 1
4591
4594
4592 displayer.close()
4595 displayer.close()
4593
4596
4594 @command('manifest',
4597 @command('manifest',
4595 [('r', 'rev', '', _('revision to display'), _('REV')),
4598 [('r', 'rev', '', _('revision to display'), _('REV')),
4596 ('', 'all', False, _("list files from all revisions"))]
4599 ('', 'all', False, _("list files from all revisions"))]
4597 + formatteropts,
4600 + formatteropts,
4598 _('[-r REV]'))
4601 _('[-r REV]'))
4599 def manifest(ui, repo, node=None, rev=None, **opts):
4602 def manifest(ui, repo, node=None, rev=None, **opts):
4600 """output the current or given revision of the project manifest
4603 """output the current or given revision of the project manifest
4601
4604
4602 Print a list of version controlled files for the given revision.
4605 Print a list of version controlled files for the given revision.
4603 If no revision is given, the first parent of the working directory
4606 If no revision is given, the first parent of the working directory
4604 is used, or the null revision if no revision is checked out.
4607 is used, or the null revision if no revision is checked out.
4605
4608
4606 With -v, print file permissions, symlink and executable bits.
4609 With -v, print file permissions, symlink and executable bits.
4607 With --debug, print file revision hashes.
4610 With --debug, print file revision hashes.
4608
4611
4609 If option --all is specified, the list of all files from all revisions
4612 If option --all is specified, the list of all files from all revisions
4610 is printed. This includes deleted and renamed files.
4613 is printed. This includes deleted and renamed files.
4611
4614
4612 Returns 0 on success.
4615 Returns 0 on success.
4613 """
4616 """
4614
4617
4615 fm = ui.formatter('manifest', opts)
4618 fm = ui.formatter('manifest', opts)
4616
4619
4617 if opts.get('all'):
4620 if opts.get('all'):
4618 if rev or node:
4621 if rev or node:
4619 raise util.Abort(_("can't specify a revision with --all"))
4622 raise util.Abort(_("can't specify a revision with --all"))
4620
4623
4621 res = []
4624 res = []
4622 prefix = "data/"
4625 prefix = "data/"
4623 suffix = ".i"
4626 suffix = ".i"
4624 plen = len(prefix)
4627 plen = len(prefix)
4625 slen = len(suffix)
4628 slen = len(suffix)
4626 lock = repo.lock()
4629 lock = repo.lock()
4627 try:
4630 try:
4628 for fn, b, size in repo.store.datafiles():
4631 for fn, b, size in repo.store.datafiles():
4629 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4632 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4630 res.append(fn[plen:-slen])
4633 res.append(fn[plen:-slen])
4631 finally:
4634 finally:
4632 lock.release()
4635 lock.release()
4633 for f in res:
4636 for f in res:
4634 fm.startitem()
4637 fm.startitem()
4635 fm.write("path", '%s\n', f)
4638 fm.write("path", '%s\n', f)
4636 fm.end()
4639 fm.end()
4637 return
4640 return
4638
4641
4639 if rev and node:
4642 if rev and node:
4640 raise util.Abort(_("please specify just one revision"))
4643 raise util.Abort(_("please specify just one revision"))
4641
4644
4642 if not node:
4645 if not node:
4643 node = rev
4646 node = rev
4644
4647
4645 char = {'l': '@', 'x': '*', '': ''}
4648 char = {'l': '@', 'x': '*', '': ''}
4646 mode = {'l': '644', 'x': '755', '': '644'}
4649 mode = {'l': '644', 'x': '755', '': '644'}
4647 ctx = scmutil.revsingle(repo, node)
4650 ctx = scmutil.revsingle(repo, node)
4648 mf = ctx.manifest()
4651 mf = ctx.manifest()
4649 for f in ctx:
4652 for f in ctx:
4650 fm.startitem()
4653 fm.startitem()
4651 fl = ctx[f].flags()
4654 fl = ctx[f].flags()
4652 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4655 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4653 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4656 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4654 fm.write('path', '%s\n', f)
4657 fm.write('path', '%s\n', f)
4655 fm.end()
4658 fm.end()
4656
4659
4657 @command('^merge',
4660 @command('^merge',
4658 [('f', 'force', None,
4661 [('f', 'force', None,
4659 _('force a merge including outstanding changes (DEPRECATED)')),
4662 _('force a merge including outstanding changes (DEPRECATED)')),
4660 ('r', 'rev', '', _('revision to merge'), _('REV')),
4663 ('r', 'rev', '', _('revision to merge'), _('REV')),
4661 ('P', 'preview', None,
4664 ('P', 'preview', None,
4662 _('review revisions to merge (no merge is performed)'))
4665 _('review revisions to merge (no merge is performed)'))
4663 ] + mergetoolopts,
4666 ] + mergetoolopts,
4664 _('[-P] [-f] [[-r] REV]'))
4667 _('[-P] [-f] [[-r] REV]'))
4665 def merge(ui, repo, node=None, **opts):
4668 def merge(ui, repo, node=None, **opts):
4666 """merge another revision into working directory
4669 """merge another revision into working directory
4667
4670
4668 The current working directory is updated with all changes made in
4671 The current working directory is updated with all changes made in
4669 the requested revision since the last common predecessor revision.
4672 the requested revision since the last common predecessor revision.
4670
4673
4671 Files that changed between either parent are marked as changed for
4674 Files that changed between either parent are marked as changed for
4672 the next commit and a commit must be performed before any further
4675 the next commit and a commit must be performed before any further
4673 updates to the repository are allowed. The next commit will have
4676 updates to the repository are allowed. The next commit will have
4674 two parents.
4677 two parents.
4675
4678
4676 ``--tool`` can be used to specify the merge tool used for file
4679 ``--tool`` can be used to specify the merge tool used for file
4677 merges. It overrides the HGMERGE environment variable and your
4680 merges. It overrides the HGMERGE environment variable and your
4678 configuration files. See :hg:`help merge-tools` for options.
4681 configuration files. See :hg:`help merge-tools` for options.
4679
4682
4680 If no revision is specified, the working directory's parent is a
4683 If no revision is specified, the working directory's parent is a
4681 head revision, and the current branch contains exactly one other
4684 head revision, and the current branch contains exactly one other
4682 head, the other head is merged with by default. Otherwise, an
4685 head, the other head is merged with by default. Otherwise, an
4683 explicit revision with which to merge with must be provided.
4686 explicit revision with which to merge with must be provided.
4684
4687
4685 :hg:`resolve` must be used to resolve unresolved files.
4688 :hg:`resolve` must be used to resolve unresolved files.
4686
4689
4687 To undo an uncommitted merge, use :hg:`update --clean .` which
4690 To undo an uncommitted merge, use :hg:`update --clean .` which
4688 will check out a clean copy of the original merge parent, losing
4691 will check out a clean copy of the original merge parent, losing
4689 all changes.
4692 all changes.
4690
4693
4691 Returns 0 on success, 1 if there are unresolved files.
4694 Returns 0 on success, 1 if there are unresolved files.
4692 """
4695 """
4693
4696
4694 if opts.get('rev') and node:
4697 if opts.get('rev') and node:
4695 raise util.Abort(_("please specify just one revision"))
4698 raise util.Abort(_("please specify just one revision"))
4696 if not node:
4699 if not node:
4697 node = opts.get('rev')
4700 node = opts.get('rev')
4698
4701
4699 if node:
4702 if node:
4700 node = scmutil.revsingle(repo, node).node()
4703 node = scmutil.revsingle(repo, node).node()
4701
4704
4702 if not node and repo._activebookmark:
4705 if not node and repo._activebookmark:
4703 bmheads = repo.bookmarkheads(repo._activebookmark)
4706 bmheads = repo.bookmarkheads(repo._activebookmark)
4704 curhead = repo[repo._activebookmark].node()
4707 curhead = repo[repo._activebookmark].node()
4705 if len(bmheads) == 2:
4708 if len(bmheads) == 2:
4706 if curhead == bmheads[0]:
4709 if curhead == bmheads[0]:
4707 node = bmheads[1]
4710 node = bmheads[1]
4708 else:
4711 else:
4709 node = bmheads[0]
4712 node = bmheads[0]
4710 elif len(bmheads) > 2:
4713 elif len(bmheads) > 2:
4711 raise util.Abort(_("multiple matching bookmarks to merge - "
4714 raise util.Abort(_("multiple matching bookmarks to merge - "
4712 "please merge with an explicit rev or bookmark"),
4715 "please merge with an explicit rev or bookmark"),
4713 hint=_("run 'hg heads' to see all heads"))
4716 hint=_("run 'hg heads' to see all heads"))
4714 elif len(bmheads) <= 1:
4717 elif len(bmheads) <= 1:
4715 raise util.Abort(_("no matching bookmark to merge - "
4718 raise util.Abort(_("no matching bookmark to merge - "
4716 "please merge with an explicit rev or bookmark"),
4719 "please merge with an explicit rev or bookmark"),
4717 hint=_("run 'hg heads' to see all heads"))
4720 hint=_("run 'hg heads' to see all heads"))
4718
4721
4719 if not node and not repo._activebookmark:
4722 if not node and not repo._activebookmark:
4720 branch = repo[None].branch()
4723 branch = repo[None].branch()
4721 bheads = repo.branchheads(branch)
4724 bheads = repo.branchheads(branch)
4722 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4725 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4723
4726
4724 if len(nbhs) > 2:
4727 if len(nbhs) > 2:
4725 raise util.Abort(_("branch '%s' has %d heads - "
4728 raise util.Abort(_("branch '%s' has %d heads - "
4726 "please merge with an explicit rev")
4729 "please merge with an explicit rev")
4727 % (branch, len(bheads)),
4730 % (branch, len(bheads)),
4728 hint=_("run 'hg heads .' to see heads"))
4731 hint=_("run 'hg heads .' to see heads"))
4729
4732
4730 parent = repo.dirstate.p1()
4733 parent = repo.dirstate.p1()
4731 if len(nbhs) <= 1:
4734 if len(nbhs) <= 1:
4732 if len(bheads) > 1:
4735 if len(bheads) > 1:
4733 raise util.Abort(_("heads are bookmarked - "
4736 raise util.Abort(_("heads are bookmarked - "
4734 "please merge with an explicit rev"),
4737 "please merge with an explicit rev"),
4735 hint=_("run 'hg heads' to see all heads"))
4738 hint=_("run 'hg heads' to see all heads"))
4736 if len(repo.heads()) > 1:
4739 if len(repo.heads()) > 1:
4737 raise util.Abort(_("branch '%s' has one head - "
4740 raise util.Abort(_("branch '%s' has one head - "
4738 "please merge with an explicit rev")
4741 "please merge with an explicit rev")
4739 % branch,
4742 % branch,
4740 hint=_("run 'hg heads' to see all heads"))
4743 hint=_("run 'hg heads' to see all heads"))
4741 msg, hint = _('nothing to merge'), None
4744 msg, hint = _('nothing to merge'), None
4742 if parent != repo.lookup(branch):
4745 if parent != repo.lookup(branch):
4743 hint = _("use 'hg update' instead")
4746 hint = _("use 'hg update' instead")
4744 raise util.Abort(msg, hint=hint)
4747 raise util.Abort(msg, hint=hint)
4745
4748
4746 if parent not in bheads:
4749 if parent not in bheads:
4747 raise util.Abort(_('working directory not at a head revision'),
4750 raise util.Abort(_('working directory not at a head revision'),
4748 hint=_("use 'hg update' or merge with an "
4751 hint=_("use 'hg update' or merge with an "
4749 "explicit revision"))
4752 "explicit revision"))
4750 if parent == nbhs[0]:
4753 if parent == nbhs[0]:
4751 node = nbhs[-1]
4754 node = nbhs[-1]
4752 else:
4755 else:
4753 node = nbhs[0]
4756 node = nbhs[0]
4754
4757
4755 if opts.get('preview'):
4758 if opts.get('preview'):
4756 # find nodes that are ancestors of p2 but not of p1
4759 # find nodes that are ancestors of p2 but not of p1
4757 p1 = repo.lookup('.')
4760 p1 = repo.lookup('.')
4758 p2 = repo.lookup(node)
4761 p2 = repo.lookup(node)
4759 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4762 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4760
4763
4761 displayer = cmdutil.show_changeset(ui, repo, opts)
4764 displayer = cmdutil.show_changeset(ui, repo, opts)
4762 for node in nodes:
4765 for node in nodes:
4763 displayer.show(repo[node])
4766 displayer.show(repo[node])
4764 displayer.close()
4767 displayer.close()
4765 return 0
4768 return 0
4766
4769
4767 try:
4770 try:
4768 # ui.forcemerge is an internal variable, do not document
4771 # ui.forcemerge is an internal variable, do not document
4769 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4772 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4770 return hg.merge(repo, node, force=opts.get('force'))
4773 return hg.merge(repo, node, force=opts.get('force'))
4771 finally:
4774 finally:
4772 ui.setconfig('ui', 'forcemerge', '', 'merge')
4775 ui.setconfig('ui', 'forcemerge', '', 'merge')
4773
4776
4774 @command('outgoing|out',
4777 @command('outgoing|out',
4775 [('f', 'force', None, _('run even when the destination is unrelated')),
4778 [('f', 'force', None, _('run even when the destination is unrelated')),
4776 ('r', 'rev', [],
4779 ('r', 'rev', [],
4777 _('a changeset intended to be included in the destination'), _('REV')),
4780 _('a changeset intended to be included in the destination'), _('REV')),
4778 ('n', 'newest-first', None, _('show newest record first')),
4781 ('n', 'newest-first', None, _('show newest record first')),
4779 ('B', 'bookmarks', False, _('compare bookmarks')),
4782 ('B', 'bookmarks', False, _('compare bookmarks')),
4780 ('b', 'branch', [], _('a specific branch you would like to push'),
4783 ('b', 'branch', [], _('a specific branch you would like to push'),
4781 _('BRANCH')),
4784 _('BRANCH')),
4782 ] + logopts + remoteopts + subrepoopts,
4785 ] + logopts + remoteopts + subrepoopts,
4783 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4786 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4784 def outgoing(ui, repo, dest=None, **opts):
4787 def outgoing(ui, repo, dest=None, **opts):
4785 """show changesets not found in the destination
4788 """show changesets not found in the destination
4786
4789
4787 Show changesets not found in the specified destination repository
4790 Show changesets not found in the specified destination repository
4788 or the default push location. These are the changesets that would
4791 or the default push location. These are the changesets that would
4789 be pushed if a push was requested.
4792 be pushed if a push was requested.
4790
4793
4791 See pull for details of valid destination formats.
4794 See pull for details of valid destination formats.
4792
4795
4793 .. container:: verbose
4796 .. container:: verbose
4794
4797
4795 With -B/--bookmarks, the result of bookmark comparison between
4798 With -B/--bookmarks, the result of bookmark comparison between
4796 local and remote repositories is displayed. With -v/--verbose,
4799 local and remote repositories is displayed. With -v/--verbose,
4797 status is also displayed for each bookmark like below::
4800 status is also displayed for each bookmark like below::
4798
4801
4799 BM1 01234567890a added
4802 BM1 01234567890a added
4800 BM2 deleted
4803 BM2 deleted
4801 BM3 234567890abc advanced
4804 BM3 234567890abc advanced
4802 BM4 34567890abcd diverged
4805 BM4 34567890abcd diverged
4803 BM5 4567890abcde changed
4806 BM5 4567890abcde changed
4804
4807
4805 The action taken when pushing depends on the
4808 The action taken when pushing depends on the
4806 status of each bookmark:
4809 status of each bookmark:
4807
4810
4808 :``added``: push with ``-B`` will create it
4811 :``added``: push with ``-B`` will create it
4809 :``deleted``: push with ``-B`` will delete it
4812 :``deleted``: push with ``-B`` will delete it
4810 :``advanced``: push will update it
4813 :``advanced``: push will update it
4811 :``diverged``: push with ``-B`` will update it
4814 :``diverged``: push with ``-B`` will update it
4812 :``changed``: push with ``-B`` will update it
4815 :``changed``: push with ``-B`` will update it
4813
4816
4814 From the point of view of pushing behavior, bookmarks
4817 From the point of view of pushing behavior, bookmarks
4815 existing only in the remote repository are treated as
4818 existing only in the remote repository are treated as
4816 ``deleted``, even if it is in fact added remotely.
4819 ``deleted``, even if it is in fact added remotely.
4817
4820
4818 Returns 0 if there are outgoing changes, 1 otherwise.
4821 Returns 0 if there are outgoing changes, 1 otherwise.
4819 """
4822 """
4820 if opts.get('graph'):
4823 if opts.get('graph'):
4821 cmdutil.checkunsupportedgraphflags([], opts)
4824 cmdutil.checkunsupportedgraphflags([], opts)
4822 o, other = hg._outgoing(ui, repo, dest, opts)
4825 o, other = hg._outgoing(ui, repo, dest, opts)
4823 if not o:
4826 if not o:
4824 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4827 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4825 return
4828 return
4826
4829
4827 revdag = cmdutil.graphrevs(repo, o, opts)
4830 revdag = cmdutil.graphrevs(repo, o, opts)
4828 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4831 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4829 showparents = [ctx.node() for ctx in repo[None].parents()]
4832 showparents = [ctx.node() for ctx in repo[None].parents()]
4830 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4833 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4831 graphmod.asciiedges)
4834 graphmod.asciiedges)
4832 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4835 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4833 return 0
4836 return 0
4834
4837
4835 if opts.get('bookmarks'):
4838 if opts.get('bookmarks'):
4836 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4839 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4837 dest, branches = hg.parseurl(dest, opts.get('branch'))
4840 dest, branches = hg.parseurl(dest, opts.get('branch'))
4838 other = hg.peer(repo, opts, dest)
4841 other = hg.peer(repo, opts, dest)
4839 if 'bookmarks' not in other.listkeys('namespaces'):
4842 if 'bookmarks' not in other.listkeys('namespaces'):
4840 ui.warn(_("remote doesn't support bookmarks\n"))
4843 ui.warn(_("remote doesn't support bookmarks\n"))
4841 return 0
4844 return 0
4842 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4845 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4843 return bookmarks.outgoing(ui, repo, other)
4846 return bookmarks.outgoing(ui, repo, other)
4844
4847
4845 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4848 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4846 try:
4849 try:
4847 return hg.outgoing(ui, repo, dest, opts)
4850 return hg.outgoing(ui, repo, dest, opts)
4848 finally:
4851 finally:
4849 del repo._subtoppath
4852 del repo._subtoppath
4850
4853
4851 @command('parents',
4854 @command('parents',
4852 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4855 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4853 ] + templateopts,
4856 ] + templateopts,
4854 _('[-r REV] [FILE]'),
4857 _('[-r REV] [FILE]'),
4855 inferrepo=True)
4858 inferrepo=True)
4856 def parents(ui, repo, file_=None, **opts):
4859 def parents(ui, repo, file_=None, **opts):
4857 """show the parents of the working directory or revision (DEPRECATED)
4860 """show the parents of the working directory or revision (DEPRECATED)
4858
4861
4859 Print the working directory's parent revisions. If a revision is
4862 Print the working directory's parent revisions. If a revision is
4860 given via -r/--rev, the parent of that revision will be printed.
4863 given via -r/--rev, the parent of that revision will be printed.
4861 If a file argument is given, the revision in which the file was
4864 If a file argument is given, the revision in which the file was
4862 last changed (before the working directory revision or the
4865 last changed (before the working directory revision or the
4863 argument to --rev if given) is printed.
4866 argument to --rev if given) is printed.
4864
4867
4865 See :hg:`summary` and :hg:`help revsets` for related information.
4868 See :hg:`summary` and :hg:`help revsets` for related information.
4866
4869
4867 Returns 0 on success.
4870 Returns 0 on success.
4868 """
4871 """
4869
4872
4870 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4873 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4871
4874
4872 if file_:
4875 if file_:
4873 m = scmutil.match(ctx, (file_,), opts)
4876 m = scmutil.match(ctx, (file_,), opts)
4874 if m.anypats() or len(m.files()) != 1:
4877 if m.anypats() or len(m.files()) != 1:
4875 raise util.Abort(_('can only specify an explicit filename'))
4878 raise util.Abort(_('can only specify an explicit filename'))
4876 file_ = m.files()[0]
4879 file_ = m.files()[0]
4877 filenodes = []
4880 filenodes = []
4878 for cp in ctx.parents():
4881 for cp in ctx.parents():
4879 if not cp:
4882 if not cp:
4880 continue
4883 continue
4881 try:
4884 try:
4882 filenodes.append(cp.filenode(file_))
4885 filenodes.append(cp.filenode(file_))
4883 except error.LookupError:
4886 except error.LookupError:
4884 pass
4887 pass
4885 if not filenodes:
4888 if not filenodes:
4886 raise util.Abort(_("'%s' not found in manifest!") % file_)
4889 raise util.Abort(_("'%s' not found in manifest!") % file_)
4887 p = []
4890 p = []
4888 for fn in filenodes:
4891 for fn in filenodes:
4889 fctx = repo.filectx(file_, fileid=fn)
4892 fctx = repo.filectx(file_, fileid=fn)
4890 p.append(fctx.node())
4893 p.append(fctx.node())
4891 else:
4894 else:
4892 p = [cp.node() for cp in ctx.parents()]
4895 p = [cp.node() for cp in ctx.parents()]
4893
4896
4894 displayer = cmdutil.show_changeset(ui, repo, opts)
4897 displayer = cmdutil.show_changeset(ui, repo, opts)
4895 for n in p:
4898 for n in p:
4896 if n != nullid:
4899 if n != nullid:
4897 displayer.show(repo[n])
4900 displayer.show(repo[n])
4898 displayer.close()
4901 displayer.close()
4899
4902
4900 @command('paths', [], _('[NAME]'), optionalrepo=True)
4903 @command('paths', [], _('[NAME]'), optionalrepo=True)
4901 def paths(ui, repo, search=None):
4904 def paths(ui, repo, search=None):
4902 """show aliases for remote repositories
4905 """show aliases for remote repositories
4903
4906
4904 Show definition of symbolic path name NAME. If no name is given,
4907 Show definition of symbolic path name NAME. If no name is given,
4905 show definition of all available names.
4908 show definition of all available names.
4906
4909
4907 Option -q/--quiet suppresses all output when searching for NAME
4910 Option -q/--quiet suppresses all output when searching for NAME
4908 and shows only the path names when listing all definitions.
4911 and shows only the path names when listing all definitions.
4909
4912
4910 Path names are defined in the [paths] section of your
4913 Path names are defined in the [paths] section of your
4911 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4914 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4912 repository, ``.hg/hgrc`` is used, too.
4915 repository, ``.hg/hgrc`` is used, too.
4913
4916
4914 The path names ``default`` and ``default-push`` have a special
4917 The path names ``default`` and ``default-push`` have a special
4915 meaning. When performing a push or pull operation, they are used
4918 meaning. When performing a push or pull operation, they are used
4916 as fallbacks if no location is specified on the command-line.
4919 as fallbacks if no location is specified on the command-line.
4917 When ``default-push`` is set, it will be used for push and
4920 When ``default-push`` is set, it will be used for push and
4918 ``default`` will be used for pull; otherwise ``default`` is used
4921 ``default`` will be used for pull; otherwise ``default`` is used
4919 as the fallback for both. When cloning a repository, the clone
4922 as the fallback for both. When cloning a repository, the clone
4920 source is written as ``default`` in ``.hg/hgrc``. Note that
4923 source is written as ``default`` in ``.hg/hgrc``. Note that
4921 ``default`` and ``default-push`` apply to all inbound (e.g.
4924 ``default`` and ``default-push`` apply to all inbound (e.g.
4922 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4925 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4923 :hg:`bundle`) operations.
4926 :hg:`bundle`) operations.
4924
4927
4925 See :hg:`help urls` for more information.
4928 See :hg:`help urls` for more information.
4926
4929
4927 Returns 0 on success.
4930 Returns 0 on success.
4928 """
4931 """
4929 if search:
4932 if search:
4930 for name, path in sorted(ui.paths.iteritems()):
4933 for name, path in sorted(ui.paths.iteritems()):
4931 if name == search:
4934 if name == search:
4932 ui.status("%s\n" % util.hidepassword(path.loc))
4935 ui.status("%s\n" % util.hidepassword(path.loc))
4933 return
4936 return
4934 if not ui.quiet:
4937 if not ui.quiet:
4935 ui.warn(_("not found!\n"))
4938 ui.warn(_("not found!\n"))
4936 return 1
4939 return 1
4937 else:
4940 else:
4938 for name, path in sorted(ui.paths.iteritems()):
4941 for name, path in sorted(ui.paths.iteritems()):
4939 if ui.quiet:
4942 if ui.quiet:
4940 ui.write("%s\n" % name)
4943 ui.write("%s\n" % name)
4941 else:
4944 else:
4942 ui.write("%s = %s\n" % (name,
4945 ui.write("%s = %s\n" % (name,
4943 util.hidepassword(path.loc)))
4946 util.hidepassword(path.loc)))
4944
4947
4945 @command('phase',
4948 @command('phase',
4946 [('p', 'public', False, _('set changeset phase to public')),
4949 [('p', 'public', False, _('set changeset phase to public')),
4947 ('d', 'draft', False, _('set changeset phase to draft')),
4950 ('d', 'draft', False, _('set changeset phase to draft')),
4948 ('s', 'secret', False, _('set changeset phase to secret')),
4951 ('s', 'secret', False, _('set changeset phase to secret')),
4949 ('f', 'force', False, _('allow to move boundary backward')),
4952 ('f', 'force', False, _('allow to move boundary backward')),
4950 ('r', 'rev', [], _('target revision'), _('REV')),
4953 ('r', 'rev', [], _('target revision'), _('REV')),
4951 ],
4954 ],
4952 _('[-p|-d|-s] [-f] [-r] REV...'))
4955 _('[-p|-d|-s] [-f] [-r] REV...'))
4953 def phase(ui, repo, *revs, **opts):
4956 def phase(ui, repo, *revs, **opts):
4954 """set or show the current phase name
4957 """set or show the current phase name
4955
4958
4956 With no argument, show the phase name of specified revisions.
4959 With no argument, show the phase name of specified revisions.
4957
4960
4958 With one of -p/--public, -d/--draft or -s/--secret, change the
4961 With one of -p/--public, -d/--draft or -s/--secret, change the
4959 phase value of the specified revisions.
4962 phase value of the specified revisions.
4960
4963
4961 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4964 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4962 lower phase to an higher phase. Phases are ordered as follows::
4965 lower phase to an higher phase. Phases are ordered as follows::
4963
4966
4964 public < draft < secret
4967 public < draft < secret
4965
4968
4966 Returns 0 on success, 1 if no phases were changed or some could not
4969 Returns 0 on success, 1 if no phases were changed or some could not
4967 be changed.
4970 be changed.
4968 """
4971 """
4969 # search for a unique phase argument
4972 # search for a unique phase argument
4970 targetphase = None
4973 targetphase = None
4971 for idx, name in enumerate(phases.phasenames):
4974 for idx, name in enumerate(phases.phasenames):
4972 if opts[name]:
4975 if opts[name]:
4973 if targetphase is not None:
4976 if targetphase is not None:
4974 raise util.Abort(_('only one phase can be specified'))
4977 raise util.Abort(_('only one phase can be specified'))
4975 targetphase = idx
4978 targetphase = idx
4976
4979
4977 # look for specified revision
4980 # look for specified revision
4978 revs = list(revs)
4981 revs = list(revs)
4979 revs.extend(opts['rev'])
4982 revs.extend(opts['rev'])
4980 if not revs:
4983 if not revs:
4981 raise util.Abort(_('no revisions specified'))
4984 raise util.Abort(_('no revisions specified'))
4982
4985
4983 revs = scmutil.revrange(repo, revs)
4986 revs = scmutil.revrange(repo, revs)
4984
4987
4985 lock = None
4988 lock = None
4986 ret = 0
4989 ret = 0
4987 if targetphase is None:
4990 if targetphase is None:
4988 # display
4991 # display
4989 for r in revs:
4992 for r in revs:
4990 ctx = repo[r]
4993 ctx = repo[r]
4991 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4994 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4992 else:
4995 else:
4993 tr = None
4996 tr = None
4994 lock = repo.lock()
4997 lock = repo.lock()
4995 try:
4998 try:
4996 tr = repo.transaction("phase")
4999 tr = repo.transaction("phase")
4997 # set phase
5000 # set phase
4998 if not revs:
5001 if not revs:
4999 raise util.Abort(_('empty revision set'))
5002 raise util.Abort(_('empty revision set'))
5000 nodes = [repo[r].node() for r in revs]
5003 nodes = [repo[r].node() for r in revs]
5001 # moving revision from public to draft may hide them
5004 # moving revision from public to draft may hide them
5002 # We have to check result on an unfiltered repository
5005 # We have to check result on an unfiltered repository
5003 unfi = repo.unfiltered()
5006 unfi = repo.unfiltered()
5004 getphase = unfi._phasecache.phase
5007 getphase = unfi._phasecache.phase
5005 olddata = [getphase(unfi, r) for r in unfi]
5008 olddata = [getphase(unfi, r) for r in unfi]
5006 phases.advanceboundary(repo, tr, targetphase, nodes)
5009 phases.advanceboundary(repo, tr, targetphase, nodes)
5007 if opts['force']:
5010 if opts['force']:
5008 phases.retractboundary(repo, tr, targetphase, nodes)
5011 phases.retractboundary(repo, tr, targetphase, nodes)
5009 tr.close()
5012 tr.close()
5010 finally:
5013 finally:
5011 if tr is not None:
5014 if tr is not None:
5012 tr.release()
5015 tr.release()
5013 lock.release()
5016 lock.release()
5014 getphase = unfi._phasecache.phase
5017 getphase = unfi._phasecache.phase
5015 newdata = [getphase(unfi, r) for r in unfi]
5018 newdata = [getphase(unfi, r) for r in unfi]
5016 changes = sum(newdata[r] != olddata[r] for r in unfi)
5019 changes = sum(newdata[r] != olddata[r] for r in unfi)
5017 cl = unfi.changelog
5020 cl = unfi.changelog
5018 rejected = [n for n in nodes
5021 rejected = [n for n in nodes
5019 if newdata[cl.rev(n)] < targetphase]
5022 if newdata[cl.rev(n)] < targetphase]
5020 if rejected:
5023 if rejected:
5021 ui.warn(_('cannot move %i changesets to a higher '
5024 ui.warn(_('cannot move %i changesets to a higher '
5022 'phase, use --force\n') % len(rejected))
5025 'phase, use --force\n') % len(rejected))
5023 ret = 1
5026 ret = 1
5024 if changes:
5027 if changes:
5025 msg = _('phase changed for %i changesets\n') % changes
5028 msg = _('phase changed for %i changesets\n') % changes
5026 if ret:
5029 if ret:
5027 ui.status(msg)
5030 ui.status(msg)
5028 else:
5031 else:
5029 ui.note(msg)
5032 ui.note(msg)
5030 else:
5033 else:
5031 ui.warn(_('no phases changed\n'))
5034 ui.warn(_('no phases changed\n'))
5032 ret = 1
5035 ret = 1
5033 return ret
5036 return ret
5034
5037
5035 def postincoming(ui, repo, modheads, optupdate, checkout):
5038 def postincoming(ui, repo, modheads, optupdate, checkout):
5036 if modheads == 0:
5039 if modheads == 0:
5037 return
5040 return
5038 if optupdate:
5041 if optupdate:
5039 checkout, movemarkfrom = bookmarks.calculateupdate(ui, repo, checkout)
5042 checkout, movemarkfrom = bookmarks.calculateupdate(ui, repo, checkout)
5040 try:
5043 try:
5041 ret = hg.update(repo, checkout)
5044 ret = hg.update(repo, checkout)
5042 except util.Abort, inst:
5045 except util.Abort, inst:
5043 ui.warn(_("not updating: %s\n") % str(inst))
5046 ui.warn(_("not updating: %s\n") % str(inst))
5044 if inst.hint:
5047 if inst.hint:
5045 ui.warn(_("(%s)\n") % inst.hint)
5048 ui.warn(_("(%s)\n") % inst.hint)
5046 return 0
5049 return 0
5047 if not ret and not checkout:
5050 if not ret and not checkout:
5048 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5051 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5049 ui.status(_("updating bookmark %s\n") % repo._activebookmark)
5052 ui.status(_("updating bookmark %s\n") % repo._activebookmark)
5050 return ret
5053 return ret
5051 if modheads > 1:
5054 if modheads > 1:
5052 currentbranchheads = len(repo.branchheads())
5055 currentbranchheads = len(repo.branchheads())
5053 if currentbranchheads == modheads:
5056 if currentbranchheads == modheads:
5054 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
5057 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
5055 elif currentbranchheads > 1:
5058 elif currentbranchheads > 1:
5056 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
5059 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
5057 "merge)\n"))
5060 "merge)\n"))
5058 else:
5061 else:
5059 ui.status(_("(run 'hg heads' to see heads)\n"))
5062 ui.status(_("(run 'hg heads' to see heads)\n"))
5060 else:
5063 else:
5061 ui.status(_("(run 'hg update' to get a working copy)\n"))
5064 ui.status(_("(run 'hg update' to get a working copy)\n"))
5062
5065
5063 @command('^pull',
5066 @command('^pull',
5064 [('u', 'update', None,
5067 [('u', 'update', None,
5065 _('update to new branch head if changesets were pulled')),
5068 _('update to new branch head if changesets were pulled')),
5066 ('f', 'force', None, _('run even when remote repository is unrelated')),
5069 ('f', 'force', None, _('run even when remote repository is unrelated')),
5067 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
5070 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
5068 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
5071 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
5069 ('b', 'branch', [], _('a specific branch you would like to pull'),
5072 ('b', 'branch', [], _('a specific branch you would like to pull'),
5070 _('BRANCH')),
5073 _('BRANCH')),
5071 ] + remoteopts,
5074 ] + remoteopts,
5072 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
5075 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
5073 def pull(ui, repo, source="default", **opts):
5076 def pull(ui, repo, source="default", **opts):
5074 """pull changes from the specified source
5077 """pull changes from the specified source
5075
5078
5076 Pull changes from a remote repository to a local one.
5079 Pull changes from a remote repository to a local one.
5077
5080
5078 This finds all changes from the repository at the specified path
5081 This finds all changes from the repository at the specified path
5079 or URL and adds them to a local repository (the current one unless
5082 or URL and adds them to a local repository (the current one unless
5080 -R is specified). By default, this does not update the copy of the
5083 -R is specified). By default, this does not update the copy of the
5081 project in the working directory.
5084 project in the working directory.
5082
5085
5083 Use :hg:`incoming` if you want to see what would have been added
5086 Use :hg:`incoming` if you want to see what would have been added
5084 by a pull at the time you issued this command. If you then decide
5087 by a pull at the time you issued this command. If you then decide
5085 to add those changes to the repository, you should use :hg:`pull
5088 to add those changes to the repository, you should use :hg:`pull
5086 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5089 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5087
5090
5088 If SOURCE is omitted, the 'default' path will be used.
5091 If SOURCE is omitted, the 'default' path will be used.
5089 See :hg:`help urls` for more information.
5092 See :hg:`help urls` for more information.
5090
5093
5091 Returns 0 on success, 1 if an update had unresolved files.
5094 Returns 0 on success, 1 if an update had unresolved files.
5092 """
5095 """
5093 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5096 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5094 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5097 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5095 other = hg.peer(repo, opts, source)
5098 other = hg.peer(repo, opts, source)
5096 try:
5099 try:
5097 revs, checkout = hg.addbranchrevs(repo, other, branches,
5100 revs, checkout = hg.addbranchrevs(repo, other, branches,
5098 opts.get('rev'))
5101 opts.get('rev'))
5099
5102
5100 remotebookmarks = other.listkeys('bookmarks')
5103 remotebookmarks = other.listkeys('bookmarks')
5101
5104
5102 if opts.get('bookmark'):
5105 if opts.get('bookmark'):
5103 if not revs:
5106 if not revs:
5104 revs = []
5107 revs = []
5105 for b in opts['bookmark']:
5108 for b in opts['bookmark']:
5106 if b not in remotebookmarks:
5109 if b not in remotebookmarks:
5107 raise util.Abort(_('remote bookmark %s not found!') % b)
5110 raise util.Abort(_('remote bookmark %s not found!') % b)
5108 revs.append(remotebookmarks[b])
5111 revs.append(remotebookmarks[b])
5109
5112
5110 if revs:
5113 if revs:
5111 try:
5114 try:
5112 revs = [other.lookup(rev) for rev in revs]
5115 revs = [other.lookup(rev) for rev in revs]
5113 except error.CapabilityError:
5116 except error.CapabilityError:
5114 err = _("other repository doesn't support revision lookup, "
5117 err = _("other repository doesn't support revision lookup, "
5115 "so a rev cannot be specified.")
5118 "so a rev cannot be specified.")
5116 raise util.Abort(err)
5119 raise util.Abort(err)
5117
5120
5118 modheads = exchange.pull(repo, other, heads=revs,
5121 modheads = exchange.pull(repo, other, heads=revs,
5119 force=opts.get('force'),
5122 force=opts.get('force'),
5120 bookmarks=opts.get('bookmark', ())).cgresult
5123 bookmarks=opts.get('bookmark', ())).cgresult
5121 if checkout:
5124 if checkout:
5122 checkout = str(repo.changelog.rev(other.lookup(checkout)))
5125 checkout = str(repo.changelog.rev(other.lookup(checkout)))
5123 repo._subtoppath = source
5126 repo._subtoppath = source
5124 try:
5127 try:
5125 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
5128 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
5126
5129
5127 finally:
5130 finally:
5128 del repo._subtoppath
5131 del repo._subtoppath
5129
5132
5130 finally:
5133 finally:
5131 other.close()
5134 other.close()
5132 return ret
5135 return ret
5133
5136
5134 @command('^push',
5137 @command('^push',
5135 [('f', 'force', None, _('force push')),
5138 [('f', 'force', None, _('force push')),
5136 ('r', 'rev', [],
5139 ('r', 'rev', [],
5137 _('a changeset intended to be included in the destination'),
5140 _('a changeset intended to be included in the destination'),
5138 _('REV')),
5141 _('REV')),
5139 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5142 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5140 ('b', 'branch', [],
5143 ('b', 'branch', [],
5141 _('a specific branch you would like to push'), _('BRANCH')),
5144 _('a specific branch you would like to push'), _('BRANCH')),
5142 ('', 'new-branch', False, _('allow pushing a new branch')),
5145 ('', 'new-branch', False, _('allow pushing a new branch')),
5143 ] + remoteopts,
5146 ] + remoteopts,
5144 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5147 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5145 def push(ui, repo, dest=None, **opts):
5148 def push(ui, repo, dest=None, **opts):
5146 """push changes to the specified destination
5149 """push changes to the specified destination
5147
5150
5148 Push changesets from the local repository to the specified
5151 Push changesets from the local repository to the specified
5149 destination.
5152 destination.
5150
5153
5151 This operation is symmetrical to pull: it is identical to a pull
5154 This operation is symmetrical to pull: it is identical to a pull
5152 in the destination repository from the current one.
5155 in the destination repository from the current one.
5153
5156
5154 By default, push will not allow creation of new heads at the
5157 By default, push will not allow creation of new heads at the
5155 destination, since multiple heads would make it unclear which head
5158 destination, since multiple heads would make it unclear which head
5156 to use. In this situation, it is recommended to pull and merge
5159 to use. In this situation, it is recommended to pull and merge
5157 before pushing.
5160 before pushing.
5158
5161
5159 Use --new-branch if you want to allow push to create a new named
5162 Use --new-branch if you want to allow push to create a new named
5160 branch that is not present at the destination. This allows you to
5163 branch that is not present at the destination. This allows you to
5161 only create a new branch without forcing other changes.
5164 only create a new branch without forcing other changes.
5162
5165
5163 .. note::
5166 .. note::
5164
5167
5165 Extra care should be taken with the -f/--force option,
5168 Extra care should be taken with the -f/--force option,
5166 which will push all new heads on all branches, an action which will
5169 which will push all new heads on all branches, an action which will
5167 almost always cause confusion for collaborators.
5170 almost always cause confusion for collaborators.
5168
5171
5169 If -r/--rev is used, the specified revision and all its ancestors
5172 If -r/--rev is used, the specified revision and all its ancestors
5170 will be pushed to the remote repository.
5173 will be pushed to the remote repository.
5171
5174
5172 If -B/--bookmark is used, the specified bookmarked revision, its
5175 If -B/--bookmark is used, the specified bookmarked revision, its
5173 ancestors, and the bookmark will be pushed to the remote
5176 ancestors, and the bookmark will be pushed to the remote
5174 repository.
5177 repository.
5175
5178
5176 Please see :hg:`help urls` for important details about ``ssh://``
5179 Please see :hg:`help urls` for important details about ``ssh://``
5177 URLs. If DESTINATION is omitted, a default path will be used.
5180 URLs. If DESTINATION is omitted, a default path will be used.
5178
5181
5179 Returns 0 if push was successful, 1 if nothing to push.
5182 Returns 0 if push was successful, 1 if nothing to push.
5180 """
5183 """
5181
5184
5182 if opts.get('bookmark'):
5185 if opts.get('bookmark'):
5183 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5186 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5184 for b in opts['bookmark']:
5187 for b in opts['bookmark']:
5185 # translate -B options to -r so changesets get pushed
5188 # translate -B options to -r so changesets get pushed
5186 if b in repo._bookmarks:
5189 if b in repo._bookmarks:
5187 opts.setdefault('rev', []).append(b)
5190 opts.setdefault('rev', []).append(b)
5188 else:
5191 else:
5189 # if we try to push a deleted bookmark, translate it to null
5192 # if we try to push a deleted bookmark, translate it to null
5190 # this lets simultaneous -r, -b options continue working
5193 # this lets simultaneous -r, -b options continue working
5191 opts.setdefault('rev', []).append("null")
5194 opts.setdefault('rev', []).append("null")
5192
5195
5193 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5196 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5194 dest, branches = hg.parseurl(dest, opts.get('branch'))
5197 dest, branches = hg.parseurl(dest, opts.get('branch'))
5195 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5198 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5196 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5199 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5197 try:
5200 try:
5198 other = hg.peer(repo, opts, dest)
5201 other = hg.peer(repo, opts, dest)
5199 except error.RepoError:
5202 except error.RepoError:
5200 if dest == "default-push":
5203 if dest == "default-push":
5201 raise util.Abort(_("default repository not configured!"),
5204 raise util.Abort(_("default repository not configured!"),
5202 hint=_('see the "path" section in "hg help config"'))
5205 hint=_('see the "path" section in "hg help config"'))
5203 else:
5206 else:
5204 raise
5207 raise
5205
5208
5206 if revs:
5209 if revs:
5207 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5210 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5208 if not revs:
5211 if not revs:
5209 raise util.Abort(_("specified revisions evaluate to an empty set"),
5212 raise util.Abort(_("specified revisions evaluate to an empty set"),
5210 hint=_("use different revision arguments"))
5213 hint=_("use different revision arguments"))
5211
5214
5212 repo._subtoppath = dest
5215 repo._subtoppath = dest
5213 try:
5216 try:
5214 # push subrepos depth-first for coherent ordering
5217 # push subrepos depth-first for coherent ordering
5215 c = repo['']
5218 c = repo['']
5216 subs = c.substate # only repos that are committed
5219 subs = c.substate # only repos that are committed
5217 for s in sorted(subs):
5220 for s in sorted(subs):
5218 result = c.sub(s).push(opts)
5221 result = c.sub(s).push(opts)
5219 if result == 0:
5222 if result == 0:
5220 return not result
5223 return not result
5221 finally:
5224 finally:
5222 del repo._subtoppath
5225 del repo._subtoppath
5223 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5226 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5224 newbranch=opts.get('new_branch'),
5227 newbranch=opts.get('new_branch'),
5225 bookmarks=opts.get('bookmark', ()))
5228 bookmarks=opts.get('bookmark', ()))
5226
5229
5227 result = not pushop.cgresult
5230 result = not pushop.cgresult
5228
5231
5229 if pushop.bkresult is not None:
5232 if pushop.bkresult is not None:
5230 if pushop.bkresult == 2:
5233 if pushop.bkresult == 2:
5231 result = 2
5234 result = 2
5232 elif not result and pushop.bkresult:
5235 elif not result and pushop.bkresult:
5233 result = 2
5236 result = 2
5234
5237
5235 return result
5238 return result
5236
5239
5237 @command('recover', [])
5240 @command('recover', [])
5238 def recover(ui, repo):
5241 def recover(ui, repo):
5239 """roll back an interrupted transaction
5242 """roll back an interrupted transaction
5240
5243
5241 Recover from an interrupted commit or pull.
5244 Recover from an interrupted commit or pull.
5242
5245
5243 This command tries to fix the repository status after an
5246 This command tries to fix the repository status after an
5244 interrupted operation. It should only be necessary when Mercurial
5247 interrupted operation. It should only be necessary when Mercurial
5245 suggests it.
5248 suggests it.
5246
5249
5247 Returns 0 if successful, 1 if nothing to recover or verify fails.
5250 Returns 0 if successful, 1 if nothing to recover or verify fails.
5248 """
5251 """
5249 if repo.recover():
5252 if repo.recover():
5250 return hg.verify(repo)
5253 return hg.verify(repo)
5251 return 1
5254 return 1
5252
5255
5253 @command('^remove|rm',
5256 @command('^remove|rm',
5254 [('A', 'after', None, _('record delete for missing files')),
5257 [('A', 'after', None, _('record delete for missing files')),
5255 ('f', 'force', None,
5258 ('f', 'force', None,
5256 _('remove (and delete) file even if added or modified')),
5259 _('remove (and delete) file even if added or modified')),
5257 ] + subrepoopts + walkopts,
5260 ] + subrepoopts + walkopts,
5258 _('[OPTION]... FILE...'),
5261 _('[OPTION]... FILE...'),
5259 inferrepo=True)
5262 inferrepo=True)
5260 def remove(ui, repo, *pats, **opts):
5263 def remove(ui, repo, *pats, **opts):
5261 """remove the specified files on the next commit
5264 """remove the specified files on the next commit
5262
5265
5263 Schedule the indicated files for removal from the current branch.
5266 Schedule the indicated files for removal from the current branch.
5264
5267
5265 This command schedules the files to be removed at the next commit.
5268 This command schedules the files to be removed at the next commit.
5266 To undo a remove before that, see :hg:`revert`. To undo added
5269 To undo a remove before that, see :hg:`revert`. To undo added
5267 files, see :hg:`forget`.
5270 files, see :hg:`forget`.
5268
5271
5269 .. container:: verbose
5272 .. container:: verbose
5270
5273
5271 -A/--after can be used to remove only files that have already
5274 -A/--after can be used to remove only files that have already
5272 been deleted, -f/--force can be used to force deletion, and -Af
5275 been deleted, -f/--force can be used to force deletion, and -Af
5273 can be used to remove files from the next revision without
5276 can be used to remove files from the next revision without
5274 deleting them from the working directory.
5277 deleting them from the working directory.
5275
5278
5276 The following table details the behavior of remove for different
5279 The following table details the behavior of remove for different
5277 file states (columns) and option combinations (rows). The file
5280 file states (columns) and option combinations (rows). The file
5278 states are Added [A], Clean [C], Modified [M] and Missing [!]
5281 states are Added [A], Clean [C], Modified [M] and Missing [!]
5279 (as reported by :hg:`status`). The actions are Warn, Remove
5282 (as reported by :hg:`status`). The actions are Warn, Remove
5280 (from branch) and Delete (from disk):
5283 (from branch) and Delete (from disk):
5281
5284
5282 ========= == == == ==
5285 ========= == == == ==
5283 opt/state A C M !
5286 opt/state A C M !
5284 ========= == == == ==
5287 ========= == == == ==
5285 none W RD W R
5288 none W RD W R
5286 -f R RD RD R
5289 -f R RD RD R
5287 -A W W W R
5290 -A W W W R
5288 -Af R R R R
5291 -Af R R R R
5289 ========= == == == ==
5292 ========= == == == ==
5290
5293
5291 Note that remove never deletes files in Added [A] state from the
5294 Note that remove never deletes files in Added [A] state from the
5292 working directory, not even if option --force is specified.
5295 working directory, not even if option --force is specified.
5293
5296
5294 Returns 0 on success, 1 if any warnings encountered.
5297 Returns 0 on success, 1 if any warnings encountered.
5295 """
5298 """
5296
5299
5297 after, force = opts.get('after'), opts.get('force')
5300 after, force = opts.get('after'), opts.get('force')
5298 if not pats and not after:
5301 if not pats and not after:
5299 raise util.Abort(_('no files specified'))
5302 raise util.Abort(_('no files specified'))
5300
5303
5301 m = scmutil.match(repo[None], pats, opts)
5304 m = scmutil.match(repo[None], pats, opts)
5302 subrepos = opts.get('subrepos')
5305 subrepos = opts.get('subrepos')
5303 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5306 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5304
5307
5305 @command('rename|move|mv',
5308 @command('rename|move|mv',
5306 [('A', 'after', None, _('record a rename that has already occurred')),
5309 [('A', 'after', None, _('record a rename that has already occurred')),
5307 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5310 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5308 ] + walkopts + dryrunopts,
5311 ] + walkopts + dryrunopts,
5309 _('[OPTION]... SOURCE... DEST'))
5312 _('[OPTION]... SOURCE... DEST'))
5310 def rename(ui, repo, *pats, **opts):
5313 def rename(ui, repo, *pats, **opts):
5311 """rename files; equivalent of copy + remove
5314 """rename files; equivalent of copy + remove
5312
5315
5313 Mark dest as copies of sources; mark sources for deletion. If dest
5316 Mark dest as copies of sources; mark sources for deletion. If dest
5314 is a directory, copies are put in that directory. If dest is a
5317 is a directory, copies are put in that directory. If dest is a
5315 file, there can only be one source.
5318 file, there can only be one source.
5316
5319
5317 By default, this command copies the contents of files as they
5320 By default, this command copies the contents of files as they
5318 exist in the working directory. If invoked with -A/--after, the
5321 exist in the working directory. If invoked with -A/--after, the
5319 operation is recorded, but no copying is performed.
5322 operation is recorded, but no copying is performed.
5320
5323
5321 This command takes effect at the next commit. To undo a rename
5324 This command takes effect at the next commit. To undo a rename
5322 before that, see :hg:`revert`.
5325 before that, see :hg:`revert`.
5323
5326
5324 Returns 0 on success, 1 if errors are encountered.
5327 Returns 0 on success, 1 if errors are encountered.
5325 """
5328 """
5326 wlock = repo.wlock(False)
5329 wlock = repo.wlock(False)
5327 try:
5330 try:
5328 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5331 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5329 finally:
5332 finally:
5330 wlock.release()
5333 wlock.release()
5331
5334
5332 @command('resolve',
5335 @command('resolve',
5333 [('a', 'all', None, _('select all unresolved files')),
5336 [('a', 'all', None, _('select all unresolved files')),
5334 ('l', 'list', None, _('list state of files needing merge')),
5337 ('l', 'list', None, _('list state of files needing merge')),
5335 ('m', 'mark', None, _('mark files as resolved')),
5338 ('m', 'mark', None, _('mark files as resolved')),
5336 ('u', 'unmark', None, _('mark files as unresolved')),
5339 ('u', 'unmark', None, _('mark files as unresolved')),
5337 ('n', 'no-status', None, _('hide status prefix'))]
5340 ('n', 'no-status', None, _('hide status prefix'))]
5338 + mergetoolopts + walkopts + formatteropts,
5341 + mergetoolopts + walkopts + formatteropts,
5339 _('[OPTION]... [FILE]...'),
5342 _('[OPTION]... [FILE]...'),
5340 inferrepo=True)
5343 inferrepo=True)
5341 def resolve(ui, repo, *pats, **opts):
5344 def resolve(ui, repo, *pats, **opts):
5342 """redo merges or set/view the merge status of files
5345 """redo merges or set/view the merge status of files
5343
5346
5344 Merges with unresolved conflicts are often the result of
5347 Merges with unresolved conflicts are often the result of
5345 non-interactive merging using the ``internal:merge`` configuration
5348 non-interactive merging using the ``internal:merge`` configuration
5346 setting, or a command-line merge tool like ``diff3``. The resolve
5349 setting, or a command-line merge tool like ``diff3``. The resolve
5347 command is used to manage the files involved in a merge, after
5350 command is used to manage the files involved in a merge, after
5348 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5351 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5349 working directory must have two parents). See :hg:`help
5352 working directory must have two parents). See :hg:`help
5350 merge-tools` for information on configuring merge tools.
5353 merge-tools` for information on configuring merge tools.
5351
5354
5352 The resolve command can be used in the following ways:
5355 The resolve command can be used in the following ways:
5353
5356
5354 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5357 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5355 files, discarding any previous merge attempts. Re-merging is not
5358 files, discarding any previous merge attempts. Re-merging is not
5356 performed for files already marked as resolved. Use ``--all/-a``
5359 performed for files already marked as resolved. Use ``--all/-a``
5357 to select all unresolved files. ``--tool`` can be used to specify
5360 to select all unresolved files. ``--tool`` can be used to specify
5358 the merge tool used for the given files. It overrides the HGMERGE
5361 the merge tool used for the given files. It overrides the HGMERGE
5359 environment variable and your configuration files. Previous file
5362 environment variable and your configuration files. Previous file
5360 contents are saved with a ``.orig`` suffix.
5363 contents are saved with a ``.orig`` suffix.
5361
5364
5362 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5365 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5363 (e.g. after having manually fixed-up the files). The default is
5366 (e.g. after having manually fixed-up the files). The default is
5364 to mark all unresolved files.
5367 to mark all unresolved files.
5365
5368
5366 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5369 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5367 default is to mark all resolved files.
5370 default is to mark all resolved files.
5368
5371
5369 - :hg:`resolve -l`: list files which had or still have conflicts.
5372 - :hg:`resolve -l`: list files which had or still have conflicts.
5370 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5373 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5371
5374
5372 Note that Mercurial will not let you commit files with unresolved
5375 Note that Mercurial will not let you commit files with unresolved
5373 merge conflicts. You must use :hg:`resolve -m ...` before you can
5376 merge conflicts. You must use :hg:`resolve -m ...` before you can
5374 commit after a conflicting merge.
5377 commit after a conflicting merge.
5375
5378
5376 Returns 0 on success, 1 if any files fail a resolve attempt.
5379 Returns 0 on success, 1 if any files fail a resolve attempt.
5377 """
5380 """
5378
5381
5379 all, mark, unmark, show, nostatus = \
5382 all, mark, unmark, show, nostatus = \
5380 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
5383 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
5381
5384
5382 if (show and (mark or unmark)) or (mark and unmark):
5385 if (show and (mark or unmark)) or (mark and unmark):
5383 raise util.Abort(_("too many options specified"))
5386 raise util.Abort(_("too many options specified"))
5384 if pats and all:
5387 if pats and all:
5385 raise util.Abort(_("can't specify --all and patterns"))
5388 raise util.Abort(_("can't specify --all and patterns"))
5386 if not (all or pats or show or mark or unmark):
5389 if not (all or pats or show or mark or unmark):
5387 raise util.Abort(_('no files or directories specified'),
5390 raise util.Abort(_('no files or directories specified'),
5388 hint=('use --all to remerge all files'))
5391 hint=('use --all to remerge all files'))
5389
5392
5390 if show:
5393 if show:
5391 fm = ui.formatter('resolve', opts)
5394 fm = ui.formatter('resolve', opts)
5392 ms = mergemod.mergestate(repo)
5395 ms = mergemod.mergestate(repo)
5393 m = scmutil.match(repo[None], pats, opts)
5396 m = scmutil.match(repo[None], pats, opts)
5394 for f in ms:
5397 for f in ms:
5395 if not m(f):
5398 if not m(f):
5396 continue
5399 continue
5397 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved'}[ms[f]]
5400 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved'}[ms[f]]
5398 fm.startitem()
5401 fm.startitem()
5399 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
5402 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
5400 fm.write('path', '%s\n', f, label=l)
5403 fm.write('path', '%s\n', f, label=l)
5401 fm.end()
5404 fm.end()
5402 return 0
5405 return 0
5403
5406
5404 wlock = repo.wlock()
5407 wlock = repo.wlock()
5405 try:
5408 try:
5406 ms = mergemod.mergestate(repo)
5409 ms = mergemod.mergestate(repo)
5407
5410
5408 if not (ms.active() or repo.dirstate.p2() != nullid):
5411 if not (ms.active() or repo.dirstate.p2() != nullid):
5409 raise util.Abort(
5412 raise util.Abort(
5410 _('resolve command not applicable when not merging'))
5413 _('resolve command not applicable when not merging'))
5411
5414
5412 m = scmutil.match(repo[None], pats, opts)
5415 m = scmutil.match(repo[None], pats, opts)
5413 ret = 0
5416 ret = 0
5414 didwork = False
5417 didwork = False
5415
5418
5416 for f in ms:
5419 for f in ms:
5417 if not m(f):
5420 if not m(f):
5418 continue
5421 continue
5419
5422
5420 didwork = True
5423 didwork = True
5421
5424
5422 if mark:
5425 if mark:
5423 ms.mark(f, "r")
5426 ms.mark(f, "r")
5424 elif unmark:
5427 elif unmark:
5425 ms.mark(f, "u")
5428 ms.mark(f, "u")
5426 else:
5429 else:
5427 wctx = repo[None]
5430 wctx = repo[None]
5428
5431
5429 # backup pre-resolve (merge uses .orig for its own purposes)
5432 # backup pre-resolve (merge uses .orig for its own purposes)
5430 a = repo.wjoin(f)
5433 a = repo.wjoin(f)
5431 util.copyfile(a, a + ".resolve")
5434 util.copyfile(a, a + ".resolve")
5432
5435
5433 try:
5436 try:
5434 # resolve file
5437 # resolve file
5435 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5438 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5436 'resolve')
5439 'resolve')
5437 if ms.resolve(f, wctx):
5440 if ms.resolve(f, wctx):
5438 ret = 1
5441 ret = 1
5439 finally:
5442 finally:
5440 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5443 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5441 ms.commit()
5444 ms.commit()
5442
5445
5443 # replace filemerge's .orig file with our resolve file
5446 # replace filemerge's .orig file with our resolve file
5444 util.rename(a + ".resolve", a + ".orig")
5447 util.rename(a + ".resolve", a + ".orig")
5445
5448
5446 ms.commit()
5449 ms.commit()
5447
5450
5448 if not didwork and pats:
5451 if not didwork and pats:
5449 ui.warn(_("arguments do not match paths that need resolving\n"))
5452 ui.warn(_("arguments do not match paths that need resolving\n"))
5450
5453
5451 finally:
5454 finally:
5452 wlock.release()
5455 wlock.release()
5453
5456
5454 # Nudge users into finishing an unfinished operation
5457 # Nudge users into finishing an unfinished operation
5455 if not list(ms.unresolved()):
5458 if not list(ms.unresolved()):
5456 ui.status(_('(no more unresolved files)\n'))
5459 ui.status(_('(no more unresolved files)\n'))
5457
5460
5458 return ret
5461 return ret
5459
5462
5460 @command('revert',
5463 @command('revert',
5461 [('a', 'all', None, _('revert all changes when no arguments given')),
5464 [('a', 'all', None, _('revert all changes when no arguments given')),
5462 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5465 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5463 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5466 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5464 ('C', 'no-backup', None, _('do not save backup copies of files')),
5467 ('C', 'no-backup', None, _('do not save backup copies of files')),
5465 ('i', 'interactive', None,
5468 ('i', 'interactive', None,
5466 _('interactively select the changes (EXPERIMENTAL)')),
5469 _('interactively select the changes (EXPERIMENTAL)')),
5467 ] + walkopts + dryrunopts,
5470 ] + walkopts + dryrunopts,
5468 _('[OPTION]... [-r REV] [NAME]...'))
5471 _('[OPTION]... [-r REV] [NAME]...'))
5469 def revert(ui, repo, *pats, **opts):
5472 def revert(ui, repo, *pats, **opts):
5470 """restore files to their checkout state
5473 """restore files to their checkout state
5471
5474
5472 .. note::
5475 .. note::
5473
5476
5474 To check out earlier revisions, you should use :hg:`update REV`.
5477 To check out earlier revisions, you should use :hg:`update REV`.
5475 To cancel an uncommitted merge (and lose your changes),
5478 To cancel an uncommitted merge (and lose your changes),
5476 use :hg:`update --clean .`.
5479 use :hg:`update --clean .`.
5477
5480
5478 With no revision specified, revert the specified files or directories
5481 With no revision specified, revert the specified files or directories
5479 to the contents they had in the parent of the working directory.
5482 to the contents they had in the parent of the working directory.
5480 This restores the contents of files to an unmodified
5483 This restores the contents of files to an unmodified
5481 state and unschedules adds, removes, copies, and renames. If the
5484 state and unschedules adds, removes, copies, and renames. If the
5482 working directory has two parents, you must explicitly specify a
5485 working directory has two parents, you must explicitly specify a
5483 revision.
5486 revision.
5484
5487
5485 Using the -r/--rev or -d/--date options, revert the given files or
5488 Using the -r/--rev or -d/--date options, revert the given files or
5486 directories to their states as of a specific revision. Because
5489 directories to their states as of a specific revision. Because
5487 revert does not change the working directory parents, this will
5490 revert does not change the working directory parents, this will
5488 cause these files to appear modified. This can be helpful to "back
5491 cause these files to appear modified. This can be helpful to "back
5489 out" some or all of an earlier change. See :hg:`backout` for a
5492 out" some or all of an earlier change. See :hg:`backout` for a
5490 related method.
5493 related method.
5491
5494
5492 Modified files are saved with a .orig suffix before reverting.
5495 Modified files are saved with a .orig suffix before reverting.
5493 To disable these backups, use --no-backup.
5496 To disable these backups, use --no-backup.
5494
5497
5495 See :hg:`help dates` for a list of formats valid for -d/--date.
5498 See :hg:`help dates` for a list of formats valid for -d/--date.
5496
5499
5497 Returns 0 on success.
5500 Returns 0 on success.
5498 """
5501 """
5499
5502
5500 if opts.get("date"):
5503 if opts.get("date"):
5501 if opts.get("rev"):
5504 if opts.get("rev"):
5502 raise util.Abort(_("you can't specify a revision and a date"))
5505 raise util.Abort(_("you can't specify a revision and a date"))
5503 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5506 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5504
5507
5505 parent, p2 = repo.dirstate.parents()
5508 parent, p2 = repo.dirstate.parents()
5506 if not opts.get('rev') and p2 != nullid:
5509 if not opts.get('rev') and p2 != nullid:
5507 # revert after merge is a trap for new users (issue2915)
5510 # revert after merge is a trap for new users (issue2915)
5508 raise util.Abort(_('uncommitted merge with no revision specified'),
5511 raise util.Abort(_('uncommitted merge with no revision specified'),
5509 hint=_('use "hg update" or see "hg help revert"'))
5512 hint=_('use "hg update" or see "hg help revert"'))
5510
5513
5511 ctx = scmutil.revsingle(repo, opts.get('rev'))
5514 ctx = scmutil.revsingle(repo, opts.get('rev'))
5512
5515
5513 if (not (pats or opts.get('include') or opts.get('exclude') or
5516 if (not (pats or opts.get('include') or opts.get('exclude') or
5514 opts.get('all') or opts.get('interactive'))):
5517 opts.get('all') or opts.get('interactive'))):
5515 msg = _("no files or directories specified")
5518 msg = _("no files or directories specified")
5516 if p2 != nullid:
5519 if p2 != nullid:
5517 hint = _("uncommitted merge, use --all to discard all changes,"
5520 hint = _("uncommitted merge, use --all to discard all changes,"
5518 " or 'hg update -C .' to abort the merge")
5521 " or 'hg update -C .' to abort the merge")
5519 raise util.Abort(msg, hint=hint)
5522 raise util.Abort(msg, hint=hint)
5520 dirty = util.any(repo.status())
5523 dirty = util.any(repo.status())
5521 node = ctx.node()
5524 node = ctx.node()
5522 if node != parent:
5525 if node != parent:
5523 if dirty:
5526 if dirty:
5524 hint = _("uncommitted changes, use --all to discard all"
5527 hint = _("uncommitted changes, use --all to discard all"
5525 " changes, or 'hg update %s' to update") % ctx.rev()
5528 " changes, or 'hg update %s' to update") % ctx.rev()
5526 else:
5529 else:
5527 hint = _("use --all to revert all files,"
5530 hint = _("use --all to revert all files,"
5528 " or 'hg update %s' to update") % ctx.rev()
5531 " or 'hg update %s' to update") % ctx.rev()
5529 elif dirty:
5532 elif dirty:
5530 hint = _("uncommitted changes, use --all to discard all changes")
5533 hint = _("uncommitted changes, use --all to discard all changes")
5531 else:
5534 else:
5532 hint = _("use --all to revert all files")
5535 hint = _("use --all to revert all files")
5533 raise util.Abort(msg, hint=hint)
5536 raise util.Abort(msg, hint=hint)
5534
5537
5535 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5538 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5536
5539
5537 @command('rollback', dryrunopts +
5540 @command('rollback', dryrunopts +
5538 [('f', 'force', False, _('ignore safety measures'))])
5541 [('f', 'force', False, _('ignore safety measures'))])
5539 def rollback(ui, repo, **opts):
5542 def rollback(ui, repo, **opts):
5540 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5543 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5541
5544
5542 Please use :hg:`commit --amend` instead of rollback to correct
5545 Please use :hg:`commit --amend` instead of rollback to correct
5543 mistakes in the last commit.
5546 mistakes in the last commit.
5544
5547
5545 This command should be used with care. There is only one level of
5548 This command should be used with care. There is only one level of
5546 rollback, and there is no way to undo a rollback. It will also
5549 rollback, and there is no way to undo a rollback. It will also
5547 restore the dirstate at the time of the last transaction, losing
5550 restore the dirstate at the time of the last transaction, losing
5548 any dirstate changes since that time. This command does not alter
5551 any dirstate changes since that time. This command does not alter
5549 the working directory.
5552 the working directory.
5550
5553
5551 Transactions are used to encapsulate the effects of all commands
5554 Transactions are used to encapsulate the effects of all commands
5552 that create new changesets or propagate existing changesets into a
5555 that create new changesets or propagate existing changesets into a
5553 repository.
5556 repository.
5554
5557
5555 .. container:: verbose
5558 .. container:: verbose
5556
5559
5557 For example, the following commands are transactional, and their
5560 For example, the following commands are transactional, and their
5558 effects can be rolled back:
5561 effects can be rolled back:
5559
5562
5560 - commit
5563 - commit
5561 - import
5564 - import
5562 - pull
5565 - pull
5563 - push (with this repository as the destination)
5566 - push (with this repository as the destination)
5564 - unbundle
5567 - unbundle
5565
5568
5566 To avoid permanent data loss, rollback will refuse to rollback a
5569 To avoid permanent data loss, rollback will refuse to rollback a
5567 commit transaction if it isn't checked out. Use --force to
5570 commit transaction if it isn't checked out. Use --force to
5568 override this protection.
5571 override this protection.
5569
5572
5570 This command is not intended for use on public repositories. Once
5573 This command is not intended for use on public repositories. Once
5571 changes are visible for pull by other users, rolling a transaction
5574 changes are visible for pull by other users, rolling a transaction
5572 back locally is ineffective (someone else may already have pulled
5575 back locally is ineffective (someone else may already have pulled
5573 the changes). Furthermore, a race is possible with readers of the
5576 the changes). Furthermore, a race is possible with readers of the
5574 repository; for example an in-progress pull from the repository
5577 repository; for example an in-progress pull from the repository
5575 may fail if a rollback is performed.
5578 may fail if a rollback is performed.
5576
5579
5577 Returns 0 on success, 1 if no rollback data is available.
5580 Returns 0 on success, 1 if no rollback data is available.
5578 """
5581 """
5579 return repo.rollback(dryrun=opts.get('dry_run'),
5582 return repo.rollback(dryrun=opts.get('dry_run'),
5580 force=opts.get('force'))
5583 force=opts.get('force'))
5581
5584
5582 @command('root', [])
5585 @command('root', [])
5583 def root(ui, repo):
5586 def root(ui, repo):
5584 """print the root (top) of the current working directory
5587 """print the root (top) of the current working directory
5585
5588
5586 Print the root directory of the current repository.
5589 Print the root directory of the current repository.
5587
5590
5588 Returns 0 on success.
5591 Returns 0 on success.
5589 """
5592 """
5590 ui.write(repo.root + "\n")
5593 ui.write(repo.root + "\n")
5591
5594
5592 @command('^serve',
5595 @command('^serve',
5593 [('A', 'accesslog', '', _('name of access log file to write to'),
5596 [('A', 'accesslog', '', _('name of access log file to write to'),
5594 _('FILE')),
5597 _('FILE')),
5595 ('d', 'daemon', None, _('run server in background')),
5598 ('d', 'daemon', None, _('run server in background')),
5596 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('FILE')),
5599 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('FILE')),
5597 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5600 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5598 # use string type, then we can check if something was passed
5601 # use string type, then we can check if something was passed
5599 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5602 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5600 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5603 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5601 _('ADDR')),
5604 _('ADDR')),
5602 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5605 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5603 _('PREFIX')),
5606 _('PREFIX')),
5604 ('n', 'name', '',
5607 ('n', 'name', '',
5605 _('name to show in web pages (default: working directory)'), _('NAME')),
5608 _('name to show in web pages (default: working directory)'), _('NAME')),
5606 ('', 'web-conf', '',
5609 ('', 'web-conf', '',
5607 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5610 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5608 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5611 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5609 _('FILE')),
5612 _('FILE')),
5610 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5613 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5611 ('', 'stdio', None, _('for remote clients')),
5614 ('', 'stdio', None, _('for remote clients')),
5612 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5615 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5613 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5616 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5614 ('', 'style', '', _('template style to use'), _('STYLE')),
5617 ('', 'style', '', _('template style to use'), _('STYLE')),
5615 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5618 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5616 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5619 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5617 _('[OPTION]...'),
5620 _('[OPTION]...'),
5618 optionalrepo=True)
5621 optionalrepo=True)
5619 def serve(ui, repo, **opts):
5622 def serve(ui, repo, **opts):
5620 """start stand-alone webserver
5623 """start stand-alone webserver
5621
5624
5622 Start a local HTTP repository browser and pull server. You can use
5625 Start a local HTTP repository browser and pull server. You can use
5623 this for ad-hoc sharing and browsing of repositories. It is
5626 this for ad-hoc sharing and browsing of repositories. It is
5624 recommended to use a real web server to serve a repository for
5627 recommended to use a real web server to serve a repository for
5625 longer periods of time.
5628 longer periods of time.
5626
5629
5627 Please note that the server does not implement access control.
5630 Please note that the server does not implement access control.
5628 This means that, by default, anybody can read from the server and
5631 This means that, by default, anybody can read from the server and
5629 nobody can write to it by default. Set the ``web.allow_push``
5632 nobody can write to it by default. Set the ``web.allow_push``
5630 option to ``*`` to allow everybody to push to the server. You
5633 option to ``*`` to allow everybody to push to the server. You
5631 should use a real web server if you need to authenticate users.
5634 should use a real web server if you need to authenticate users.
5632
5635
5633 By default, the server logs accesses to stdout and errors to
5636 By default, the server logs accesses to stdout and errors to
5634 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5637 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5635 files.
5638 files.
5636
5639
5637 To have the server choose a free port number to listen on, specify
5640 To have the server choose a free port number to listen on, specify
5638 a port number of 0; in this case, the server will print the port
5641 a port number of 0; in this case, the server will print the port
5639 number it uses.
5642 number it uses.
5640
5643
5641 Returns 0 on success.
5644 Returns 0 on success.
5642 """
5645 """
5643
5646
5644 if opts["stdio"] and opts["cmdserver"]:
5647 if opts["stdio"] and opts["cmdserver"]:
5645 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5648 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5646
5649
5647 if opts["stdio"]:
5650 if opts["stdio"]:
5648 if repo is None:
5651 if repo is None:
5649 raise error.RepoError(_("there is no Mercurial repository here"
5652 raise error.RepoError(_("there is no Mercurial repository here"
5650 " (.hg not found)"))
5653 " (.hg not found)"))
5651 s = sshserver.sshserver(ui, repo)
5654 s = sshserver.sshserver(ui, repo)
5652 s.serve_forever()
5655 s.serve_forever()
5653
5656
5654 if opts["cmdserver"]:
5657 if opts["cmdserver"]:
5655 service = commandserver.createservice(ui, repo, opts)
5658 service = commandserver.createservice(ui, repo, opts)
5656 return cmdutil.service(opts, initfn=service.init, runfn=service.run)
5659 return cmdutil.service(opts, initfn=service.init, runfn=service.run)
5657
5660
5658 # this way we can check if something was given in the command-line
5661 # this way we can check if something was given in the command-line
5659 if opts.get('port'):
5662 if opts.get('port'):
5660 opts['port'] = util.getport(opts.get('port'))
5663 opts['port'] = util.getport(opts.get('port'))
5661
5664
5662 if repo:
5665 if repo:
5663 baseui = repo.baseui
5666 baseui = repo.baseui
5664 else:
5667 else:
5665 baseui = ui
5668 baseui = ui
5666 optlist = ("name templates style address port prefix ipv6"
5669 optlist = ("name templates style address port prefix ipv6"
5667 " accesslog errorlog certificate encoding")
5670 " accesslog errorlog certificate encoding")
5668 for o in optlist.split():
5671 for o in optlist.split():
5669 val = opts.get(o, '')
5672 val = opts.get(o, '')
5670 if val in (None, ''): # should check against default options instead
5673 if val in (None, ''): # should check against default options instead
5671 continue
5674 continue
5672 baseui.setconfig("web", o, val, 'serve')
5675 baseui.setconfig("web", o, val, 'serve')
5673 if repo and repo.ui != baseui:
5676 if repo and repo.ui != baseui:
5674 repo.ui.setconfig("web", o, val, 'serve')
5677 repo.ui.setconfig("web", o, val, 'serve')
5675
5678
5676 o = opts.get('web_conf') or opts.get('webdir_conf')
5679 o = opts.get('web_conf') or opts.get('webdir_conf')
5677 if not o:
5680 if not o:
5678 if not repo:
5681 if not repo:
5679 raise error.RepoError(_("there is no Mercurial repository"
5682 raise error.RepoError(_("there is no Mercurial repository"
5680 " here (.hg not found)"))
5683 " here (.hg not found)"))
5681 o = repo
5684 o = repo
5682
5685
5683 app = hgweb.hgweb(o, baseui=baseui)
5686 app = hgweb.hgweb(o, baseui=baseui)
5684 service = httpservice(ui, app, opts)
5687 service = httpservice(ui, app, opts)
5685 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5688 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5686
5689
5687 class httpservice(object):
5690 class httpservice(object):
5688 def __init__(self, ui, app, opts):
5691 def __init__(self, ui, app, opts):
5689 self.ui = ui
5692 self.ui = ui
5690 self.app = app
5693 self.app = app
5691 self.opts = opts
5694 self.opts = opts
5692
5695
5693 def init(self):
5696 def init(self):
5694 util.setsignalhandler()
5697 util.setsignalhandler()
5695 self.httpd = hgweb_server.create_server(self.ui, self.app)
5698 self.httpd = hgweb_server.create_server(self.ui, self.app)
5696
5699
5697 if self.opts['port'] and not self.ui.verbose:
5700 if self.opts['port'] and not self.ui.verbose:
5698 return
5701 return
5699
5702
5700 if self.httpd.prefix:
5703 if self.httpd.prefix:
5701 prefix = self.httpd.prefix.strip('/') + '/'
5704 prefix = self.httpd.prefix.strip('/') + '/'
5702 else:
5705 else:
5703 prefix = ''
5706 prefix = ''
5704
5707
5705 port = ':%d' % self.httpd.port
5708 port = ':%d' % self.httpd.port
5706 if port == ':80':
5709 if port == ':80':
5707 port = ''
5710 port = ''
5708
5711
5709 bindaddr = self.httpd.addr
5712 bindaddr = self.httpd.addr
5710 if bindaddr == '0.0.0.0':
5713 if bindaddr == '0.0.0.0':
5711 bindaddr = '*'
5714 bindaddr = '*'
5712 elif ':' in bindaddr: # IPv6
5715 elif ':' in bindaddr: # IPv6
5713 bindaddr = '[%s]' % bindaddr
5716 bindaddr = '[%s]' % bindaddr
5714
5717
5715 fqaddr = self.httpd.fqaddr
5718 fqaddr = self.httpd.fqaddr
5716 if ':' in fqaddr:
5719 if ':' in fqaddr:
5717 fqaddr = '[%s]' % fqaddr
5720 fqaddr = '[%s]' % fqaddr
5718 if self.opts['port']:
5721 if self.opts['port']:
5719 write = self.ui.status
5722 write = self.ui.status
5720 else:
5723 else:
5721 write = self.ui.write
5724 write = self.ui.write
5722 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5725 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5723 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5726 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5724 self.ui.flush() # avoid buffering of status message
5727 self.ui.flush() # avoid buffering of status message
5725
5728
5726 def run(self):
5729 def run(self):
5727 self.httpd.serve_forever()
5730 self.httpd.serve_forever()
5728
5731
5729
5732
5730 @command('^status|st',
5733 @command('^status|st',
5731 [('A', 'all', None, _('show status of all files')),
5734 [('A', 'all', None, _('show status of all files')),
5732 ('m', 'modified', None, _('show only modified files')),
5735 ('m', 'modified', None, _('show only modified files')),
5733 ('a', 'added', None, _('show only added files')),
5736 ('a', 'added', None, _('show only added files')),
5734 ('r', 'removed', None, _('show only removed files')),
5737 ('r', 'removed', None, _('show only removed files')),
5735 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5738 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5736 ('c', 'clean', None, _('show only files without changes')),
5739 ('c', 'clean', None, _('show only files without changes')),
5737 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5740 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5738 ('i', 'ignored', None, _('show only ignored files')),
5741 ('i', 'ignored', None, _('show only ignored files')),
5739 ('n', 'no-status', None, _('hide status prefix')),
5742 ('n', 'no-status', None, _('hide status prefix')),
5740 ('C', 'copies', None, _('show source of copied files')),
5743 ('C', 'copies', None, _('show source of copied files')),
5741 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5744 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5742 ('', 'rev', [], _('show difference from revision'), _('REV')),
5745 ('', 'rev', [], _('show difference from revision'), _('REV')),
5743 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5746 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5744 ] + walkopts + subrepoopts + formatteropts,
5747 ] + walkopts + subrepoopts + formatteropts,
5745 _('[OPTION]... [FILE]...'),
5748 _('[OPTION]... [FILE]...'),
5746 inferrepo=True)
5749 inferrepo=True)
5747 def status(ui, repo, *pats, **opts):
5750 def status(ui, repo, *pats, **opts):
5748 """show changed files in the working directory
5751 """show changed files in the working directory
5749
5752
5750 Show status of files in the repository. If names are given, only
5753 Show status of files in the repository. If names are given, only
5751 files that match are shown. Files that are clean or ignored or
5754 files that match are shown. Files that are clean or ignored or
5752 the source of a copy/move operation, are not listed unless
5755 the source of a copy/move operation, are not listed unless
5753 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5756 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5754 Unless options described with "show only ..." are given, the
5757 Unless options described with "show only ..." are given, the
5755 options -mardu are used.
5758 options -mardu are used.
5756
5759
5757 Option -q/--quiet hides untracked (unknown and ignored) files
5760 Option -q/--quiet hides untracked (unknown and ignored) files
5758 unless explicitly requested with -u/--unknown or -i/--ignored.
5761 unless explicitly requested with -u/--unknown or -i/--ignored.
5759
5762
5760 .. note::
5763 .. note::
5761
5764
5762 status may appear to disagree with diff if permissions have
5765 status may appear to disagree with diff if permissions have
5763 changed or a merge has occurred. The standard diff format does
5766 changed or a merge has occurred. The standard diff format does
5764 not report permission changes and diff only reports changes
5767 not report permission changes and diff only reports changes
5765 relative to one merge parent.
5768 relative to one merge parent.
5766
5769
5767 If one revision is given, it is used as the base revision.
5770 If one revision is given, it is used as the base revision.
5768 If two revisions are given, the differences between them are
5771 If two revisions are given, the differences between them are
5769 shown. The --change option can also be used as a shortcut to list
5772 shown. The --change option can also be used as a shortcut to list
5770 the changed files of a revision from its first parent.
5773 the changed files of a revision from its first parent.
5771
5774
5772 The codes used to show the status of files are::
5775 The codes used to show the status of files are::
5773
5776
5774 M = modified
5777 M = modified
5775 A = added
5778 A = added
5776 R = removed
5779 R = removed
5777 C = clean
5780 C = clean
5778 ! = missing (deleted by non-hg command, but still tracked)
5781 ! = missing (deleted by non-hg command, but still tracked)
5779 ? = not tracked
5782 ? = not tracked
5780 I = ignored
5783 I = ignored
5781 = origin of the previous file (with --copies)
5784 = origin of the previous file (with --copies)
5782
5785
5783 .. container:: verbose
5786 .. container:: verbose
5784
5787
5785 Examples:
5788 Examples:
5786
5789
5787 - show changes in the working directory relative to a
5790 - show changes in the working directory relative to a
5788 changeset::
5791 changeset::
5789
5792
5790 hg status --rev 9353
5793 hg status --rev 9353
5791
5794
5792 - show changes in the working directory relative to the
5795 - show changes in the working directory relative to the
5793 current directory (see :hg:`help patterns` for more information)::
5796 current directory (see :hg:`help patterns` for more information)::
5794
5797
5795 hg status re:
5798 hg status re:
5796
5799
5797 - show all changes including copies in an existing changeset::
5800 - show all changes including copies in an existing changeset::
5798
5801
5799 hg status --copies --change 9353
5802 hg status --copies --change 9353
5800
5803
5801 - get a NUL separated list of added files, suitable for xargs::
5804 - get a NUL separated list of added files, suitable for xargs::
5802
5805
5803 hg status -an0
5806 hg status -an0
5804
5807
5805 Returns 0 on success.
5808 Returns 0 on success.
5806 """
5809 """
5807
5810
5808 revs = opts.get('rev')
5811 revs = opts.get('rev')
5809 change = opts.get('change')
5812 change = opts.get('change')
5810
5813
5811 if revs and change:
5814 if revs and change:
5812 msg = _('cannot specify --rev and --change at the same time')
5815 msg = _('cannot specify --rev and --change at the same time')
5813 raise util.Abort(msg)
5816 raise util.Abort(msg)
5814 elif change:
5817 elif change:
5815 node2 = scmutil.revsingle(repo, change, None).node()
5818 node2 = scmutil.revsingle(repo, change, None).node()
5816 node1 = repo[node2].p1().node()
5819 node1 = repo[node2].p1().node()
5817 else:
5820 else:
5818 node1, node2 = scmutil.revpair(repo, revs)
5821 node1, node2 = scmutil.revpair(repo, revs)
5819
5822
5820 if pats:
5823 if pats:
5821 cwd = repo.getcwd()
5824 cwd = repo.getcwd()
5822 else:
5825 else:
5823 cwd = ''
5826 cwd = ''
5824
5827
5825 if opts.get('print0'):
5828 if opts.get('print0'):
5826 end = '\0'
5829 end = '\0'
5827 else:
5830 else:
5828 end = '\n'
5831 end = '\n'
5829 copy = {}
5832 copy = {}
5830 states = 'modified added removed deleted unknown ignored clean'.split()
5833 states = 'modified added removed deleted unknown ignored clean'.split()
5831 show = [k for k in states if opts.get(k)]
5834 show = [k for k in states if opts.get(k)]
5832 if opts.get('all'):
5835 if opts.get('all'):
5833 show += ui.quiet and (states[:4] + ['clean']) or states
5836 show += ui.quiet and (states[:4] + ['clean']) or states
5834 if not show:
5837 if not show:
5835 if ui.quiet:
5838 if ui.quiet:
5836 show = states[:4]
5839 show = states[:4]
5837 else:
5840 else:
5838 show = states[:5]
5841 show = states[:5]
5839
5842
5840 m = scmutil.match(repo[node2], pats, opts)
5843 m = scmutil.match(repo[node2], pats, opts)
5841 stat = repo.status(node1, node2, m,
5844 stat = repo.status(node1, node2, m,
5842 'ignored' in show, 'clean' in show, 'unknown' in show,
5845 'ignored' in show, 'clean' in show, 'unknown' in show,
5843 opts.get('subrepos'))
5846 opts.get('subrepos'))
5844 changestates = zip(states, 'MAR!?IC', stat)
5847 changestates = zip(states, 'MAR!?IC', stat)
5845
5848
5846 if (opts.get('all') or opts.get('copies')
5849 if (opts.get('all') or opts.get('copies')
5847 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5850 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5848 copy = copies.pathcopies(repo[node1], repo[node2], m)
5851 copy = copies.pathcopies(repo[node1], repo[node2], m)
5849
5852
5850 fm = ui.formatter('status', opts)
5853 fm = ui.formatter('status', opts)
5851 fmt = '%s' + end
5854 fmt = '%s' + end
5852 showchar = not opts.get('no_status')
5855 showchar = not opts.get('no_status')
5853
5856
5854 for state, char, files in changestates:
5857 for state, char, files in changestates:
5855 if state in show:
5858 if state in show:
5856 label = 'status.' + state
5859 label = 'status.' + state
5857 for f in files:
5860 for f in files:
5858 fm.startitem()
5861 fm.startitem()
5859 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5862 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5860 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5863 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5861 if f in copy:
5864 if f in copy:
5862 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5865 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5863 label='status.copied')
5866 label='status.copied')
5864 fm.end()
5867 fm.end()
5865
5868
5866 @command('^summary|sum',
5869 @command('^summary|sum',
5867 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5870 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5868 def summary(ui, repo, **opts):
5871 def summary(ui, repo, **opts):
5869 """summarize working directory state
5872 """summarize working directory state
5870
5873
5871 This generates a brief summary of the working directory state,
5874 This generates a brief summary of the working directory state,
5872 including parents, branch, commit status, phase and available updates.
5875 including parents, branch, commit status, phase and available updates.
5873
5876
5874 With the --remote option, this will check the default paths for
5877 With the --remote option, this will check the default paths for
5875 incoming and outgoing changes. This can be time-consuming.
5878 incoming and outgoing changes. This can be time-consuming.
5876
5879
5877 Returns 0 on success.
5880 Returns 0 on success.
5878 """
5881 """
5879
5882
5880 ctx = repo[None]
5883 ctx = repo[None]
5881 parents = ctx.parents()
5884 parents = ctx.parents()
5882 pnode = parents[0].node()
5885 pnode = parents[0].node()
5883 marks = []
5886 marks = []
5884
5887
5885 for p in parents:
5888 for p in parents:
5886 # label with log.changeset (instead of log.parent) since this
5889 # label with log.changeset (instead of log.parent) since this
5887 # shows a working directory parent *changeset*:
5890 # shows a working directory parent *changeset*:
5888 # i18n: column positioning for "hg summary"
5891 # i18n: column positioning for "hg summary"
5889 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5892 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5890 label='log.changeset changeset.%s' % p.phasestr())
5893 label='log.changeset changeset.%s' % p.phasestr())
5891 ui.write(' '.join(p.tags()), label='log.tag')
5894 ui.write(' '.join(p.tags()), label='log.tag')
5892 if p.bookmarks():
5895 if p.bookmarks():
5893 marks.extend(p.bookmarks())
5896 marks.extend(p.bookmarks())
5894 if p.rev() == -1:
5897 if p.rev() == -1:
5895 if not len(repo):
5898 if not len(repo):
5896 ui.write(_(' (empty repository)'))
5899 ui.write(_(' (empty repository)'))
5897 else:
5900 else:
5898 ui.write(_(' (no revision checked out)'))
5901 ui.write(_(' (no revision checked out)'))
5899 ui.write('\n')
5902 ui.write('\n')
5900 if p.description():
5903 if p.description():
5901 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5904 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5902 label='log.summary')
5905 label='log.summary')
5903
5906
5904 branch = ctx.branch()
5907 branch = ctx.branch()
5905 bheads = repo.branchheads(branch)
5908 bheads = repo.branchheads(branch)
5906 # i18n: column positioning for "hg summary"
5909 # i18n: column positioning for "hg summary"
5907 m = _('branch: %s\n') % branch
5910 m = _('branch: %s\n') % branch
5908 if branch != 'default':
5911 if branch != 'default':
5909 ui.write(m, label='log.branch')
5912 ui.write(m, label='log.branch')
5910 else:
5913 else:
5911 ui.status(m, label='log.branch')
5914 ui.status(m, label='log.branch')
5912
5915
5913 if marks:
5916 if marks:
5914 current = repo._activebookmark
5917 current = repo._activebookmark
5915 # i18n: column positioning for "hg summary"
5918 # i18n: column positioning for "hg summary"
5916 ui.write(_('bookmarks:'), label='log.bookmark')
5919 ui.write(_('bookmarks:'), label='log.bookmark')
5917 if current is not None:
5920 if current is not None:
5918 if current in marks:
5921 if current in marks:
5919 ui.write(' *' + current, label='bookmarks.current')
5922 ui.write(' *' + current, label='bookmarks.current')
5920 marks.remove(current)
5923 marks.remove(current)
5921 else:
5924 else:
5922 ui.write(' [%s]' % current, label='bookmarks.current')
5925 ui.write(' [%s]' % current, label='bookmarks.current')
5923 for m in marks:
5926 for m in marks:
5924 ui.write(' ' + m, label='log.bookmark')
5927 ui.write(' ' + m, label='log.bookmark')
5925 ui.write('\n', label='log.bookmark')
5928 ui.write('\n', label='log.bookmark')
5926
5929
5927 status = repo.status(unknown=True)
5930 status = repo.status(unknown=True)
5928
5931
5929 c = repo.dirstate.copies()
5932 c = repo.dirstate.copies()
5930 copied, renamed = [], []
5933 copied, renamed = [], []
5931 for d, s in c.iteritems():
5934 for d, s in c.iteritems():
5932 if s in status.removed:
5935 if s in status.removed:
5933 status.removed.remove(s)
5936 status.removed.remove(s)
5934 renamed.append(d)
5937 renamed.append(d)
5935 else:
5938 else:
5936 copied.append(d)
5939 copied.append(d)
5937 if d in status.added:
5940 if d in status.added:
5938 status.added.remove(d)
5941 status.added.remove(d)
5939
5942
5940 ms = mergemod.mergestate(repo)
5943 ms = mergemod.mergestate(repo)
5941 unresolved = [f for f in ms if ms[f] == 'u']
5944 unresolved = [f for f in ms if ms[f] == 'u']
5942
5945
5943 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5946 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5944
5947
5945 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5948 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5946 (ui.label(_('%d added'), 'status.added'), status.added),
5949 (ui.label(_('%d added'), 'status.added'), status.added),
5947 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5950 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5948 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5951 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5949 (ui.label(_('%d copied'), 'status.copied'), copied),
5952 (ui.label(_('%d copied'), 'status.copied'), copied),
5950 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5953 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5951 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5954 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5952 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5955 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5953 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5956 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5954 t = []
5957 t = []
5955 for l, s in labels:
5958 for l, s in labels:
5956 if s:
5959 if s:
5957 t.append(l % len(s))
5960 t.append(l % len(s))
5958
5961
5959 t = ', '.join(t)
5962 t = ', '.join(t)
5960 cleanworkdir = False
5963 cleanworkdir = False
5961
5964
5962 if repo.vfs.exists('updatestate'):
5965 if repo.vfs.exists('updatestate'):
5963 t += _(' (interrupted update)')
5966 t += _(' (interrupted update)')
5964 elif len(parents) > 1:
5967 elif len(parents) > 1:
5965 t += _(' (merge)')
5968 t += _(' (merge)')
5966 elif branch != parents[0].branch():
5969 elif branch != parents[0].branch():
5967 t += _(' (new branch)')
5970 t += _(' (new branch)')
5968 elif (parents[0].closesbranch() and
5971 elif (parents[0].closesbranch() and
5969 pnode in repo.branchheads(branch, closed=True)):
5972 pnode in repo.branchheads(branch, closed=True)):
5970 t += _(' (head closed)')
5973 t += _(' (head closed)')
5971 elif not (status.modified or status.added or status.removed or renamed or
5974 elif not (status.modified or status.added or status.removed or renamed or
5972 copied or subs):
5975 copied or subs):
5973 t += _(' (clean)')
5976 t += _(' (clean)')
5974 cleanworkdir = True
5977 cleanworkdir = True
5975 elif pnode not in bheads:
5978 elif pnode not in bheads:
5976 t += _(' (new branch head)')
5979 t += _(' (new branch head)')
5977
5980
5978 if cleanworkdir:
5981 if cleanworkdir:
5979 # i18n: column positioning for "hg summary"
5982 # i18n: column positioning for "hg summary"
5980 ui.status(_('commit: %s\n') % t.strip())
5983 ui.status(_('commit: %s\n') % t.strip())
5981 else:
5984 else:
5982 # i18n: column positioning for "hg summary"
5985 # i18n: column positioning for "hg summary"
5983 ui.write(_('commit: %s\n') % t.strip())
5986 ui.write(_('commit: %s\n') % t.strip())
5984
5987
5985 # all ancestors of branch heads - all ancestors of parent = new csets
5988 # all ancestors of branch heads - all ancestors of parent = new csets
5986 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5989 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5987 bheads))
5990 bheads))
5988
5991
5989 if new == 0:
5992 if new == 0:
5990 # i18n: column positioning for "hg summary"
5993 # i18n: column positioning for "hg summary"
5991 ui.status(_('update: (current)\n'))
5994 ui.status(_('update: (current)\n'))
5992 elif pnode not in bheads:
5995 elif pnode not in bheads:
5993 # i18n: column positioning for "hg summary"
5996 # i18n: column positioning for "hg summary"
5994 ui.write(_('update: %d new changesets (update)\n') % new)
5997 ui.write(_('update: %d new changesets (update)\n') % new)
5995 else:
5998 else:
5996 # i18n: column positioning for "hg summary"
5999 # i18n: column positioning for "hg summary"
5997 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
6000 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5998 (new, len(bheads)))
6001 (new, len(bheads)))
5999
6002
6000 t = []
6003 t = []
6001 draft = len(repo.revs('draft()'))
6004 draft = len(repo.revs('draft()'))
6002 if draft:
6005 if draft:
6003 t.append(_('%d draft') % draft)
6006 t.append(_('%d draft') % draft)
6004 secret = len(repo.revs('secret()'))
6007 secret = len(repo.revs('secret()'))
6005 if secret:
6008 if secret:
6006 t.append(_('%d secret') % secret)
6009 t.append(_('%d secret') % secret)
6007
6010
6008 if parents:
6011 if parents:
6009 parentphase = max(p.phase() for p in parents)
6012 parentphase = max(p.phase() for p in parents)
6010 else:
6013 else:
6011 parentphase = phases.public
6014 parentphase = phases.public
6012
6015
6013 if draft or secret:
6016 if draft or secret:
6014 ui.status(_('phases: %s (%s)\n') % (', '.join(t),
6017 ui.status(_('phases: %s (%s)\n') % (', '.join(t),
6015 phases.phasenames[parentphase]))
6018 phases.phasenames[parentphase]))
6016 else:
6019 else:
6017 ui.note(_('phases: (%s)\n') % phases.phasenames[parentphase])
6020 ui.note(_('phases: (%s)\n') % phases.phasenames[parentphase])
6018
6021
6019 cmdutil.summaryhooks(ui, repo)
6022 cmdutil.summaryhooks(ui, repo)
6020
6023
6021 if opts.get('remote'):
6024 if opts.get('remote'):
6022 needsincoming, needsoutgoing = True, True
6025 needsincoming, needsoutgoing = True, True
6023 else:
6026 else:
6024 needsincoming, needsoutgoing = False, False
6027 needsincoming, needsoutgoing = False, False
6025 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
6028 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
6026 if i:
6029 if i:
6027 needsincoming = True
6030 needsincoming = True
6028 if o:
6031 if o:
6029 needsoutgoing = True
6032 needsoutgoing = True
6030 if not needsincoming and not needsoutgoing:
6033 if not needsincoming and not needsoutgoing:
6031 return
6034 return
6032
6035
6033 def getincoming():
6036 def getincoming():
6034 source, branches = hg.parseurl(ui.expandpath('default'))
6037 source, branches = hg.parseurl(ui.expandpath('default'))
6035 sbranch = branches[0]
6038 sbranch = branches[0]
6036 try:
6039 try:
6037 other = hg.peer(repo, {}, source)
6040 other = hg.peer(repo, {}, source)
6038 except error.RepoError:
6041 except error.RepoError:
6039 if opts.get('remote'):
6042 if opts.get('remote'):
6040 raise
6043 raise
6041 return source, sbranch, None, None, None
6044 return source, sbranch, None, None, None
6042 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6045 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6043 if revs:
6046 if revs:
6044 revs = [other.lookup(rev) for rev in revs]
6047 revs = [other.lookup(rev) for rev in revs]
6045 ui.debug('comparing with %s\n' % util.hidepassword(source))
6048 ui.debug('comparing with %s\n' % util.hidepassword(source))
6046 repo.ui.pushbuffer()
6049 repo.ui.pushbuffer()
6047 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6050 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6048 repo.ui.popbuffer()
6051 repo.ui.popbuffer()
6049 return source, sbranch, other, commoninc, commoninc[1]
6052 return source, sbranch, other, commoninc, commoninc[1]
6050
6053
6051 if needsincoming:
6054 if needsincoming:
6052 source, sbranch, sother, commoninc, incoming = getincoming()
6055 source, sbranch, sother, commoninc, incoming = getincoming()
6053 else:
6056 else:
6054 source = sbranch = sother = commoninc = incoming = None
6057 source = sbranch = sother = commoninc = incoming = None
6055
6058
6056 def getoutgoing():
6059 def getoutgoing():
6057 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
6060 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
6058 dbranch = branches[0]
6061 dbranch = branches[0]
6059 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
6062 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
6060 if source != dest:
6063 if source != dest:
6061 try:
6064 try:
6062 dother = hg.peer(repo, {}, dest)
6065 dother = hg.peer(repo, {}, dest)
6063 except error.RepoError:
6066 except error.RepoError:
6064 if opts.get('remote'):
6067 if opts.get('remote'):
6065 raise
6068 raise
6066 return dest, dbranch, None, None
6069 return dest, dbranch, None, None
6067 ui.debug('comparing with %s\n' % util.hidepassword(dest))
6070 ui.debug('comparing with %s\n' % util.hidepassword(dest))
6068 elif sother is None:
6071 elif sother is None:
6069 # there is no explicit destination peer, but source one is invalid
6072 # there is no explicit destination peer, but source one is invalid
6070 return dest, dbranch, None, None
6073 return dest, dbranch, None, None
6071 else:
6074 else:
6072 dother = sother
6075 dother = sother
6073 if (source != dest or (sbranch is not None and sbranch != dbranch)):
6076 if (source != dest or (sbranch is not None and sbranch != dbranch)):
6074 common = None
6077 common = None
6075 else:
6078 else:
6076 common = commoninc
6079 common = commoninc
6077 if revs:
6080 if revs:
6078 revs = [repo.lookup(rev) for rev in revs]
6081 revs = [repo.lookup(rev) for rev in revs]
6079 repo.ui.pushbuffer()
6082 repo.ui.pushbuffer()
6080 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
6083 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
6081 commoninc=common)
6084 commoninc=common)
6082 repo.ui.popbuffer()
6085 repo.ui.popbuffer()
6083 return dest, dbranch, dother, outgoing
6086 return dest, dbranch, dother, outgoing
6084
6087
6085 if needsoutgoing:
6088 if needsoutgoing:
6086 dest, dbranch, dother, outgoing = getoutgoing()
6089 dest, dbranch, dother, outgoing = getoutgoing()
6087 else:
6090 else:
6088 dest = dbranch = dother = outgoing = None
6091 dest = dbranch = dother = outgoing = None
6089
6092
6090 if opts.get('remote'):
6093 if opts.get('remote'):
6091 t = []
6094 t = []
6092 if incoming:
6095 if incoming:
6093 t.append(_('1 or more incoming'))
6096 t.append(_('1 or more incoming'))
6094 o = outgoing.missing
6097 o = outgoing.missing
6095 if o:
6098 if o:
6096 t.append(_('%d outgoing') % len(o))
6099 t.append(_('%d outgoing') % len(o))
6097 other = dother or sother
6100 other = dother or sother
6098 if 'bookmarks' in other.listkeys('namespaces'):
6101 if 'bookmarks' in other.listkeys('namespaces'):
6099 counts = bookmarks.summary(repo, other)
6102 counts = bookmarks.summary(repo, other)
6100 if counts[0] > 0:
6103 if counts[0] > 0:
6101 t.append(_('%d incoming bookmarks') % counts[0])
6104 t.append(_('%d incoming bookmarks') % counts[0])
6102 if counts[1] > 0:
6105 if counts[1] > 0:
6103 t.append(_('%d outgoing bookmarks') % counts[1])
6106 t.append(_('%d outgoing bookmarks') % counts[1])
6104
6107
6105 if t:
6108 if t:
6106 # i18n: column positioning for "hg summary"
6109 # i18n: column positioning for "hg summary"
6107 ui.write(_('remote: %s\n') % (', '.join(t)))
6110 ui.write(_('remote: %s\n') % (', '.join(t)))
6108 else:
6111 else:
6109 # i18n: column positioning for "hg summary"
6112 # i18n: column positioning for "hg summary"
6110 ui.status(_('remote: (synced)\n'))
6113 ui.status(_('remote: (synced)\n'))
6111
6114
6112 cmdutil.summaryremotehooks(ui, repo, opts,
6115 cmdutil.summaryremotehooks(ui, repo, opts,
6113 ((source, sbranch, sother, commoninc),
6116 ((source, sbranch, sother, commoninc),
6114 (dest, dbranch, dother, outgoing)))
6117 (dest, dbranch, dother, outgoing)))
6115
6118
6116 @command('tag',
6119 @command('tag',
6117 [('f', 'force', None, _('force tag')),
6120 [('f', 'force', None, _('force tag')),
6118 ('l', 'local', None, _('make the tag local')),
6121 ('l', 'local', None, _('make the tag local')),
6119 ('r', 'rev', '', _('revision to tag'), _('REV')),
6122 ('r', 'rev', '', _('revision to tag'), _('REV')),
6120 ('', 'remove', None, _('remove a tag')),
6123 ('', 'remove', None, _('remove a tag')),
6121 # -l/--local is already there, commitopts cannot be used
6124 # -l/--local is already there, commitopts cannot be used
6122 ('e', 'edit', None, _('invoke editor on commit messages')),
6125 ('e', 'edit', None, _('invoke editor on commit messages')),
6123 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6126 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6124 ] + commitopts2,
6127 ] + commitopts2,
6125 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6128 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6126 def tag(ui, repo, name1, *names, **opts):
6129 def tag(ui, repo, name1, *names, **opts):
6127 """add one or more tags for the current or given revision
6130 """add one or more tags for the current or given revision
6128
6131
6129 Name a particular revision using <name>.
6132 Name a particular revision using <name>.
6130
6133
6131 Tags are used to name particular revisions of the repository and are
6134 Tags are used to name particular revisions of the repository and are
6132 very useful to compare different revisions, to go back to significant
6135 very useful to compare different revisions, to go back to significant
6133 earlier versions or to mark branch points as releases, etc. Changing
6136 earlier versions or to mark branch points as releases, etc. Changing
6134 an existing tag is normally disallowed; use -f/--force to override.
6137 an existing tag is normally disallowed; use -f/--force to override.
6135
6138
6136 If no revision is given, the parent of the working directory is
6139 If no revision is given, the parent of the working directory is
6137 used.
6140 used.
6138
6141
6139 To facilitate version control, distribution, and merging of tags,
6142 To facilitate version control, distribution, and merging of tags,
6140 they are stored as a file named ".hgtags" which is managed similarly
6143 they are stored as a file named ".hgtags" which is managed similarly
6141 to other project files and can be hand-edited if necessary. This
6144 to other project files and can be hand-edited if necessary. This
6142 also means that tagging creates a new commit. The file
6145 also means that tagging creates a new commit. The file
6143 ".hg/localtags" is used for local tags (not shared among
6146 ".hg/localtags" is used for local tags (not shared among
6144 repositories).
6147 repositories).
6145
6148
6146 Tag commits are usually made at the head of a branch. If the parent
6149 Tag commits are usually made at the head of a branch. If the parent
6147 of the working directory is not a branch head, :hg:`tag` aborts; use
6150 of the working directory is not a branch head, :hg:`tag` aborts; use
6148 -f/--force to force the tag commit to be based on a non-head
6151 -f/--force to force the tag commit to be based on a non-head
6149 changeset.
6152 changeset.
6150
6153
6151 See :hg:`help dates` for a list of formats valid for -d/--date.
6154 See :hg:`help dates` for a list of formats valid for -d/--date.
6152
6155
6153 Since tag names have priority over branch names during revision
6156 Since tag names have priority over branch names during revision
6154 lookup, using an existing branch name as a tag name is discouraged.
6157 lookup, using an existing branch name as a tag name is discouraged.
6155
6158
6156 Returns 0 on success.
6159 Returns 0 on success.
6157 """
6160 """
6158 wlock = lock = None
6161 wlock = lock = None
6159 try:
6162 try:
6160 wlock = repo.wlock()
6163 wlock = repo.wlock()
6161 lock = repo.lock()
6164 lock = repo.lock()
6162 rev_ = "."
6165 rev_ = "."
6163 names = [t.strip() for t in (name1,) + names]
6166 names = [t.strip() for t in (name1,) + names]
6164 if len(names) != len(set(names)):
6167 if len(names) != len(set(names)):
6165 raise util.Abort(_('tag names must be unique'))
6168 raise util.Abort(_('tag names must be unique'))
6166 for n in names:
6169 for n in names:
6167 scmutil.checknewlabel(repo, n, 'tag')
6170 scmutil.checknewlabel(repo, n, 'tag')
6168 if not n:
6171 if not n:
6169 raise util.Abort(_('tag names cannot consist entirely of '
6172 raise util.Abort(_('tag names cannot consist entirely of '
6170 'whitespace'))
6173 'whitespace'))
6171 if opts.get('rev') and opts.get('remove'):
6174 if opts.get('rev') and opts.get('remove'):
6172 raise util.Abort(_("--rev and --remove are incompatible"))
6175 raise util.Abort(_("--rev and --remove are incompatible"))
6173 if opts.get('rev'):
6176 if opts.get('rev'):
6174 rev_ = opts['rev']
6177 rev_ = opts['rev']
6175 message = opts.get('message')
6178 message = opts.get('message')
6176 if opts.get('remove'):
6179 if opts.get('remove'):
6177 if opts.get('local'):
6180 if opts.get('local'):
6178 expectedtype = 'local'
6181 expectedtype = 'local'
6179 else:
6182 else:
6180 expectedtype = 'global'
6183 expectedtype = 'global'
6181
6184
6182 for n in names:
6185 for n in names:
6183 if not repo.tagtype(n):
6186 if not repo.tagtype(n):
6184 raise util.Abort(_("tag '%s' does not exist") % n)
6187 raise util.Abort(_("tag '%s' does not exist") % n)
6185 if repo.tagtype(n) != expectedtype:
6188 if repo.tagtype(n) != expectedtype:
6186 if expectedtype == 'global':
6189 if expectedtype == 'global':
6187 raise util.Abort(_("tag '%s' is not a global tag") % n)
6190 raise util.Abort(_("tag '%s' is not a global tag") % n)
6188 else:
6191 else:
6189 raise util.Abort(_("tag '%s' is not a local tag") % n)
6192 raise util.Abort(_("tag '%s' is not a local tag") % n)
6190 rev_ = nullid
6193 rev_ = nullid
6191 if not message:
6194 if not message:
6192 # we don't translate commit messages
6195 # we don't translate commit messages
6193 message = 'Removed tag %s' % ', '.join(names)
6196 message = 'Removed tag %s' % ', '.join(names)
6194 elif not opts.get('force'):
6197 elif not opts.get('force'):
6195 for n in names:
6198 for n in names:
6196 if n in repo.tags():
6199 if n in repo.tags():
6197 raise util.Abort(_("tag '%s' already exists "
6200 raise util.Abort(_("tag '%s' already exists "
6198 "(use -f to force)") % n)
6201 "(use -f to force)") % n)
6199 if not opts.get('local'):
6202 if not opts.get('local'):
6200 p1, p2 = repo.dirstate.parents()
6203 p1, p2 = repo.dirstate.parents()
6201 if p2 != nullid:
6204 if p2 != nullid:
6202 raise util.Abort(_('uncommitted merge'))
6205 raise util.Abort(_('uncommitted merge'))
6203 bheads = repo.branchheads()
6206 bheads = repo.branchheads()
6204 if not opts.get('force') and bheads and p1 not in bheads:
6207 if not opts.get('force') and bheads and p1 not in bheads:
6205 raise util.Abort(_('not at a branch head (use -f to force)'))
6208 raise util.Abort(_('not at a branch head (use -f to force)'))
6206 r = scmutil.revsingle(repo, rev_).node()
6209 r = scmutil.revsingle(repo, rev_).node()
6207
6210
6208 if not message:
6211 if not message:
6209 # we don't translate commit messages
6212 # we don't translate commit messages
6210 message = ('Added tag %s for changeset %s' %
6213 message = ('Added tag %s for changeset %s' %
6211 (', '.join(names), short(r)))
6214 (', '.join(names), short(r)))
6212
6215
6213 date = opts.get('date')
6216 date = opts.get('date')
6214 if date:
6217 if date:
6215 date = util.parsedate(date)
6218 date = util.parsedate(date)
6216
6219
6217 if opts.get('remove'):
6220 if opts.get('remove'):
6218 editform = 'tag.remove'
6221 editform = 'tag.remove'
6219 else:
6222 else:
6220 editform = 'tag.add'
6223 editform = 'tag.add'
6221 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6224 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6222
6225
6223 # don't allow tagging the null rev
6226 # don't allow tagging the null rev
6224 if (not opts.get('remove') and
6227 if (not opts.get('remove') and
6225 scmutil.revsingle(repo, rev_).rev() == nullrev):
6228 scmutil.revsingle(repo, rev_).rev() == nullrev):
6226 raise util.Abort(_("cannot tag null revision"))
6229 raise util.Abort(_("cannot tag null revision"))
6227
6230
6228 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6231 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6229 editor=editor)
6232 editor=editor)
6230 finally:
6233 finally:
6231 release(lock, wlock)
6234 release(lock, wlock)
6232
6235
6233 @command('tags', formatteropts, '')
6236 @command('tags', formatteropts, '')
6234 def tags(ui, repo, **opts):
6237 def tags(ui, repo, **opts):
6235 """list repository tags
6238 """list repository tags
6236
6239
6237 This lists both regular and local tags. When the -v/--verbose
6240 This lists both regular and local tags. When the -v/--verbose
6238 switch is used, a third column "local" is printed for local tags.
6241 switch is used, a third column "local" is printed for local tags.
6239
6242
6240 Returns 0 on success.
6243 Returns 0 on success.
6241 """
6244 """
6242
6245
6243 fm = ui.formatter('tags', opts)
6246 fm = ui.formatter('tags', opts)
6244 hexfunc = fm.hexfunc
6247 hexfunc = fm.hexfunc
6245 tagtype = ""
6248 tagtype = ""
6246
6249
6247 for t, n in reversed(repo.tagslist()):
6250 for t, n in reversed(repo.tagslist()):
6248 hn = hexfunc(n)
6251 hn = hexfunc(n)
6249 label = 'tags.normal'
6252 label = 'tags.normal'
6250 tagtype = ''
6253 tagtype = ''
6251 if repo.tagtype(t) == 'local':
6254 if repo.tagtype(t) == 'local':
6252 label = 'tags.local'
6255 label = 'tags.local'
6253 tagtype = 'local'
6256 tagtype = 'local'
6254
6257
6255 fm.startitem()
6258 fm.startitem()
6256 fm.write('tag', '%s', t, label=label)
6259 fm.write('tag', '%s', t, label=label)
6257 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6260 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6258 fm.condwrite(not ui.quiet, 'rev node', fmt,
6261 fm.condwrite(not ui.quiet, 'rev node', fmt,
6259 repo.changelog.rev(n), hn, label=label)
6262 repo.changelog.rev(n), hn, label=label)
6260 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6263 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6261 tagtype, label=label)
6264 tagtype, label=label)
6262 fm.plain('\n')
6265 fm.plain('\n')
6263 fm.end()
6266 fm.end()
6264
6267
6265 @command('tip',
6268 @command('tip',
6266 [('p', 'patch', None, _('show patch')),
6269 [('p', 'patch', None, _('show patch')),
6267 ('g', 'git', None, _('use git extended diff format')),
6270 ('g', 'git', None, _('use git extended diff format')),
6268 ] + templateopts,
6271 ] + templateopts,
6269 _('[-p] [-g]'))
6272 _('[-p] [-g]'))
6270 def tip(ui, repo, **opts):
6273 def tip(ui, repo, **opts):
6271 """show the tip revision (DEPRECATED)
6274 """show the tip revision (DEPRECATED)
6272
6275
6273 The tip revision (usually just called the tip) is the changeset
6276 The tip revision (usually just called the tip) is the changeset
6274 most recently added to the repository (and therefore the most
6277 most recently added to the repository (and therefore the most
6275 recently changed head).
6278 recently changed head).
6276
6279
6277 If you have just made a commit, that commit will be the tip. If
6280 If you have just made a commit, that commit will be the tip. If
6278 you have just pulled changes from another repository, the tip of
6281 you have just pulled changes from another repository, the tip of
6279 that repository becomes the current tip. The "tip" tag is special
6282 that repository becomes the current tip. The "tip" tag is special
6280 and cannot be renamed or assigned to a different changeset.
6283 and cannot be renamed or assigned to a different changeset.
6281
6284
6282 This command is deprecated, please use :hg:`heads` instead.
6285 This command is deprecated, please use :hg:`heads` instead.
6283
6286
6284 Returns 0 on success.
6287 Returns 0 on success.
6285 """
6288 """
6286 displayer = cmdutil.show_changeset(ui, repo, opts)
6289 displayer = cmdutil.show_changeset(ui, repo, opts)
6287 displayer.show(repo['tip'])
6290 displayer.show(repo['tip'])
6288 displayer.close()
6291 displayer.close()
6289
6292
6290 @command('unbundle',
6293 @command('unbundle',
6291 [('u', 'update', None,
6294 [('u', 'update', None,
6292 _('update to new branch head if changesets were unbundled'))],
6295 _('update to new branch head if changesets were unbundled'))],
6293 _('[-u] FILE...'))
6296 _('[-u] FILE...'))
6294 def unbundle(ui, repo, fname1, *fnames, **opts):
6297 def unbundle(ui, repo, fname1, *fnames, **opts):
6295 """apply one or more changegroup files
6298 """apply one or more changegroup files
6296
6299
6297 Apply one or more compressed changegroup files generated by the
6300 Apply one or more compressed changegroup files generated by the
6298 bundle command.
6301 bundle command.
6299
6302
6300 Returns 0 on success, 1 if an update has unresolved files.
6303 Returns 0 on success, 1 if an update has unresolved files.
6301 """
6304 """
6302 fnames = (fname1,) + fnames
6305 fnames = (fname1,) + fnames
6303
6306
6304 lock = repo.lock()
6307 lock = repo.lock()
6305 try:
6308 try:
6306 for fname in fnames:
6309 for fname in fnames:
6307 f = hg.openpath(ui, fname)
6310 f = hg.openpath(ui, fname)
6308 gen = exchange.readbundle(ui, f, fname)
6311 gen = exchange.readbundle(ui, f, fname)
6309 if isinstance(gen, bundle2.unbundle20):
6312 if isinstance(gen, bundle2.unbundle20):
6310 tr = repo.transaction('unbundle')
6313 tr = repo.transaction('unbundle')
6311 try:
6314 try:
6312 op = bundle2.processbundle(repo, gen, lambda: tr)
6315 op = bundle2.processbundle(repo, gen, lambda: tr)
6313 tr.close()
6316 tr.close()
6314 finally:
6317 finally:
6315 if tr:
6318 if tr:
6316 tr.release()
6319 tr.release()
6317 changes = [r.get('result', 0)
6320 changes = [r.get('result', 0)
6318 for r in op.records['changegroup']]
6321 for r in op.records['changegroup']]
6319 modheads = changegroup.combineresults(changes)
6322 modheads = changegroup.combineresults(changes)
6320 else:
6323 else:
6321 modheads = changegroup.addchangegroup(repo, gen, 'unbundle',
6324 modheads = changegroup.addchangegroup(repo, gen, 'unbundle',
6322 'bundle:' + fname)
6325 'bundle:' + fname)
6323 finally:
6326 finally:
6324 lock.release()
6327 lock.release()
6325
6328
6326 return postincoming(ui, repo, modheads, opts.get('update'), None)
6329 return postincoming(ui, repo, modheads, opts.get('update'), None)
6327
6330
6328 @command('^update|up|checkout|co',
6331 @command('^update|up|checkout|co',
6329 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6332 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6330 ('c', 'check', None,
6333 ('c', 'check', None,
6331 _('update across branches if no uncommitted changes')),
6334 _('update across branches if no uncommitted changes')),
6332 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6335 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6333 ('r', 'rev', '', _('revision'), _('REV'))
6336 ('r', 'rev', '', _('revision'), _('REV'))
6334 ] + mergetoolopts,
6337 ] + mergetoolopts,
6335 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6338 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6336 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6339 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6337 tool=None):
6340 tool=None):
6338 """update working directory (or switch revisions)
6341 """update working directory (or switch revisions)
6339
6342
6340 Update the repository's working directory to the specified
6343 Update the repository's working directory to the specified
6341 changeset. If no changeset is specified, update to the tip of the
6344 changeset. If no changeset is specified, update to the tip of the
6342 current named branch and move the current bookmark (see :hg:`help
6345 current named branch and move the current bookmark (see :hg:`help
6343 bookmarks`).
6346 bookmarks`).
6344
6347
6345 Update sets the working directory's parent revision to the specified
6348 Update sets the working directory's parent revision to the specified
6346 changeset (see :hg:`help parents`).
6349 changeset (see :hg:`help parents`).
6347
6350
6348 If the changeset is not a descendant or ancestor of the working
6351 If the changeset is not a descendant or ancestor of the working
6349 directory's parent, the update is aborted. With the -c/--check
6352 directory's parent, the update is aborted. With the -c/--check
6350 option, the working directory is checked for uncommitted changes; if
6353 option, the working directory is checked for uncommitted changes; if
6351 none are found, the working directory is updated to the specified
6354 none are found, the working directory is updated to the specified
6352 changeset.
6355 changeset.
6353
6356
6354 .. container:: verbose
6357 .. container:: verbose
6355
6358
6356 The following rules apply when the working directory contains
6359 The following rules apply when the working directory contains
6357 uncommitted changes:
6360 uncommitted changes:
6358
6361
6359 1. If neither -c/--check nor -C/--clean is specified, and if
6362 1. If neither -c/--check nor -C/--clean is specified, and if
6360 the requested changeset is an ancestor or descendant of
6363 the requested changeset is an ancestor or descendant of
6361 the working directory's parent, the uncommitted changes
6364 the working directory's parent, the uncommitted changes
6362 are merged into the requested changeset and the merged
6365 are merged into the requested changeset and the merged
6363 result is left uncommitted. If the requested changeset is
6366 result is left uncommitted. If the requested changeset is
6364 not an ancestor or descendant (that is, it is on another
6367 not an ancestor or descendant (that is, it is on another
6365 branch), the update is aborted and the uncommitted changes
6368 branch), the update is aborted and the uncommitted changes
6366 are preserved.
6369 are preserved.
6367
6370
6368 2. With the -c/--check option, the update is aborted and the
6371 2. With the -c/--check option, the update is aborted and the
6369 uncommitted changes are preserved.
6372 uncommitted changes are preserved.
6370
6373
6371 3. With the -C/--clean option, uncommitted changes are discarded and
6374 3. With the -C/--clean option, uncommitted changes are discarded and
6372 the working directory is updated to the requested changeset.
6375 the working directory is updated to the requested changeset.
6373
6376
6374 To cancel an uncommitted merge (and lose your changes), use
6377 To cancel an uncommitted merge (and lose your changes), use
6375 :hg:`update --clean .`.
6378 :hg:`update --clean .`.
6376
6379
6377 Use null as the changeset to remove the working directory (like
6380 Use null as the changeset to remove the working directory (like
6378 :hg:`clone -U`).
6381 :hg:`clone -U`).
6379
6382
6380 If you want to revert just one file to an older revision, use
6383 If you want to revert just one file to an older revision, use
6381 :hg:`revert [-r REV] NAME`.
6384 :hg:`revert [-r REV] NAME`.
6382
6385
6383 See :hg:`help dates` for a list of formats valid for -d/--date.
6386 See :hg:`help dates` for a list of formats valid for -d/--date.
6384
6387
6385 Returns 0 on success, 1 if there are unresolved files.
6388 Returns 0 on success, 1 if there are unresolved files.
6386 """
6389 """
6387 if rev and node:
6390 if rev and node:
6388 raise util.Abort(_("please specify just one revision"))
6391 raise util.Abort(_("please specify just one revision"))
6389
6392
6390 if rev is None or rev == '':
6393 if rev is None or rev == '':
6391 rev = node
6394 rev = node
6392
6395
6393 cmdutil.clearunfinished(repo)
6396 cmdutil.clearunfinished(repo)
6394
6397
6395 # with no argument, we also move the current bookmark, if any
6398 # with no argument, we also move the current bookmark, if any
6396 rev, movemarkfrom = bookmarks.calculateupdate(ui, repo, rev)
6399 rev, movemarkfrom = bookmarks.calculateupdate(ui, repo, rev)
6397
6400
6398 # if we defined a bookmark, we have to remember the original bookmark name
6401 # if we defined a bookmark, we have to remember the original bookmark name
6399 brev = rev
6402 brev = rev
6400 rev = scmutil.revsingle(repo, rev, rev).rev()
6403 rev = scmutil.revsingle(repo, rev, rev).rev()
6401
6404
6402 if check and clean:
6405 if check and clean:
6403 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
6406 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
6404
6407
6405 if date:
6408 if date:
6406 if rev is not None:
6409 if rev is not None:
6407 raise util.Abort(_("you can't specify a revision and a date"))
6410 raise util.Abort(_("you can't specify a revision and a date"))
6408 rev = cmdutil.finddate(ui, repo, date)
6411 rev = cmdutil.finddate(ui, repo, date)
6409
6412
6410 if check:
6413 if check:
6411 cmdutil.bailifchanged(repo, merge=False)
6414 cmdutil.bailifchanged(repo, merge=False)
6412 if rev is None:
6415 if rev is None:
6413 rev = repo[repo[None].branch()].rev()
6416 rev = repo[repo[None].branch()].rev()
6414
6417
6415 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6418 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6416
6419
6417 if clean:
6420 if clean:
6418 ret = hg.clean(repo, rev)
6421 ret = hg.clean(repo, rev)
6419 else:
6422 else:
6420 ret = hg.update(repo, rev)
6423 ret = hg.update(repo, rev)
6421
6424
6422 if not ret and movemarkfrom:
6425 if not ret and movemarkfrom:
6423 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
6426 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
6424 ui.status(_("updating bookmark %s\n") % repo._activebookmark)
6427 ui.status(_("updating bookmark %s\n") % repo._activebookmark)
6425 elif brev in repo._bookmarks:
6428 elif brev in repo._bookmarks:
6426 bookmarks.activate(repo, brev)
6429 bookmarks.activate(repo, brev)
6427 ui.status(_("(activating bookmark %s)\n") % brev)
6430 ui.status(_("(activating bookmark %s)\n") % brev)
6428 elif brev:
6431 elif brev:
6429 if repo._activebookmark:
6432 if repo._activebookmark:
6430 ui.status(_("(leaving bookmark %s)\n") %
6433 ui.status(_("(leaving bookmark %s)\n") %
6431 repo._activebookmark)
6434 repo._activebookmark)
6432 bookmarks.deactivate(repo)
6435 bookmarks.deactivate(repo)
6433
6436
6434 return ret
6437 return ret
6435
6438
6436 @command('verify', [])
6439 @command('verify', [])
6437 def verify(ui, repo):
6440 def verify(ui, repo):
6438 """verify the integrity of the repository
6441 """verify the integrity of the repository
6439
6442
6440 Verify the integrity of the current repository.
6443 Verify the integrity of the current repository.
6441
6444
6442 This will perform an extensive check of the repository's
6445 This will perform an extensive check of the repository's
6443 integrity, validating the hashes and checksums of each entry in
6446 integrity, validating the hashes and checksums of each entry in
6444 the changelog, manifest, and tracked files, as well as the
6447 the changelog, manifest, and tracked files, as well as the
6445 integrity of their crosslinks and indices.
6448 integrity of their crosslinks and indices.
6446
6449
6447 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
6450 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
6448 for more information about recovery from corruption of the
6451 for more information about recovery from corruption of the
6449 repository.
6452 repository.
6450
6453
6451 Returns 0 on success, 1 if errors are encountered.
6454 Returns 0 on success, 1 if errors are encountered.
6452 """
6455 """
6453 return hg.verify(repo)
6456 return hg.verify(repo)
6454
6457
6455 @command('version', [], norepo=True)
6458 @command('version', [], norepo=True)
6456 def version_(ui):
6459 def version_(ui):
6457 """output version and copyright information"""
6460 """output version and copyright information"""
6458 ui.write(_("Mercurial Distributed SCM (version %s)\n")
6461 ui.write(_("Mercurial Distributed SCM (version %s)\n")
6459 % util.version())
6462 % util.version())
6460 ui.status(_(
6463 ui.status(_(
6461 "(see http://mercurial.selenic.com for more information)\n"
6464 "(see http://mercurial.selenic.com for more information)\n"
6462 "\nCopyright (C) 2005-2015 Matt Mackall and others\n"
6465 "\nCopyright (C) 2005-2015 Matt Mackall and others\n"
6463 "This is free software; see the source for copying conditions. "
6466 "This is free software; see the source for copying conditions. "
6464 "There is NO\nwarranty; "
6467 "There is NO\nwarranty; "
6465 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6468 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6466 ))
6469 ))
6467
6470
6468 ui.note(_("\nEnabled extensions:\n\n"))
6471 ui.note(_("\nEnabled extensions:\n\n"))
6469 if ui.verbose:
6472 if ui.verbose:
6470 # format names and versions into columns
6473 # format names and versions into columns
6471 names = []
6474 names = []
6472 vers = []
6475 vers = []
6473 for name, module in extensions.extensions():
6476 for name, module in extensions.extensions():
6474 names.append(name)
6477 names.append(name)
6475 vers.append(extensions.moduleversion(module))
6478 vers.append(extensions.moduleversion(module))
6476 if names:
6479 if names:
6477 maxnamelen = max(len(n) for n in names)
6480 maxnamelen = max(len(n) for n in names)
6478 for i, name in enumerate(names):
6481 for i, name in enumerate(names):
6479 ui.write(" %-*s %s\n" % (maxnamelen, name, vers[i]))
6482 ui.write(" %-*s %s\n" % (maxnamelen, name, vers[i]))
@@ -1,1970 +1,1973 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class 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 from node import hex, nullid, short
7 from node import hex, nullid, short
8 from i18n import _
8 from i18n import _
9 import urllib
9 import urllib
10 import peer, changegroup, subrepo, pushkey, obsolete, repoview
10 import peer, changegroup, subrepo, pushkey, obsolete, repoview
11 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
11 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
12 import lock as lockmod
12 import lock as lockmod
13 import transaction, store, encoding, exchange, bundle2
13 import transaction, store, encoding, exchange, bundle2
14 import scmutil, util, extensions, hook, error, revset
14 import scmutil, util, extensions, hook, error, revset
15 import match as matchmod
15 import match as matchmod
16 import merge as mergemod
16 import merge as mergemod
17 import tags as tagsmod
17 import tags as tagsmod
18 from lock import release
18 from lock import release
19 import weakref, errno, os, time, inspect
19 import weakref, errno, os, time, inspect
20 import branchmap, pathutil
20 import branchmap, pathutil
21 import namespaces
21 import namespaces
22 propertycache = util.propertycache
22 propertycache = util.propertycache
23 filecache = scmutil.filecache
23 filecache = scmutil.filecache
24
24
25 class repofilecache(filecache):
25 class repofilecache(filecache):
26 """All filecache usage on repo are done for logic that should be unfiltered
26 """All filecache usage on repo are done for logic that should be unfiltered
27 """
27 """
28
28
29 def __get__(self, repo, type=None):
29 def __get__(self, repo, type=None):
30 return super(repofilecache, self).__get__(repo.unfiltered(), type)
30 return super(repofilecache, self).__get__(repo.unfiltered(), type)
31 def __set__(self, repo, value):
31 def __set__(self, repo, value):
32 return super(repofilecache, self).__set__(repo.unfiltered(), value)
32 return super(repofilecache, self).__set__(repo.unfiltered(), value)
33 def __delete__(self, repo):
33 def __delete__(self, repo):
34 return super(repofilecache, self).__delete__(repo.unfiltered())
34 return super(repofilecache, self).__delete__(repo.unfiltered())
35
35
36 class storecache(repofilecache):
36 class storecache(repofilecache):
37 """filecache for files in the store"""
37 """filecache for files in the store"""
38 def join(self, obj, fname):
38 def join(self, obj, fname):
39 return obj.sjoin(fname)
39 return obj.sjoin(fname)
40
40
41 class unfilteredpropertycache(propertycache):
41 class unfilteredpropertycache(propertycache):
42 """propertycache that apply to unfiltered repo only"""
42 """propertycache that apply to unfiltered repo only"""
43
43
44 def __get__(self, repo, type=None):
44 def __get__(self, repo, type=None):
45 unfi = repo.unfiltered()
45 unfi = repo.unfiltered()
46 if unfi is repo:
46 if unfi is repo:
47 return super(unfilteredpropertycache, self).__get__(unfi)
47 return super(unfilteredpropertycache, self).__get__(unfi)
48 return getattr(unfi, self.name)
48 return getattr(unfi, self.name)
49
49
50 class filteredpropertycache(propertycache):
50 class filteredpropertycache(propertycache):
51 """propertycache that must take filtering in account"""
51 """propertycache that must take filtering in account"""
52
52
53 def cachevalue(self, obj, value):
53 def cachevalue(self, obj, value):
54 object.__setattr__(obj, self.name, value)
54 object.__setattr__(obj, self.name, value)
55
55
56
56
57 def hasunfilteredcache(repo, name):
57 def hasunfilteredcache(repo, name):
58 """check if a repo has an unfilteredpropertycache value for <name>"""
58 """check if a repo has an unfilteredpropertycache value for <name>"""
59 return name in vars(repo.unfiltered())
59 return name in vars(repo.unfiltered())
60
60
61 def unfilteredmethod(orig):
61 def unfilteredmethod(orig):
62 """decorate method that always need to be run on unfiltered version"""
62 """decorate method that always need to be run on unfiltered version"""
63 def wrapper(repo, *args, **kwargs):
63 def wrapper(repo, *args, **kwargs):
64 return orig(repo.unfiltered(), *args, **kwargs)
64 return orig(repo.unfiltered(), *args, **kwargs)
65 return wrapper
65 return wrapper
66
66
67 moderncaps = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle',
67 moderncaps = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle',
68 'unbundle'))
68 'unbundle'))
69 legacycaps = moderncaps.union(set(['changegroupsubset']))
69 legacycaps = moderncaps.union(set(['changegroupsubset']))
70
70
71 class localpeer(peer.peerrepository):
71 class localpeer(peer.peerrepository):
72 '''peer for a local repo; reflects only the most recent API'''
72 '''peer for a local repo; reflects only the most recent API'''
73
73
74 def __init__(self, repo, caps=moderncaps):
74 def __init__(self, repo, caps=moderncaps):
75 peer.peerrepository.__init__(self)
75 peer.peerrepository.__init__(self)
76 self._repo = repo.filtered('served')
76 self._repo = repo.filtered('served')
77 self.ui = repo.ui
77 self.ui = repo.ui
78 self._caps = repo._restrictcapabilities(caps)
78 self._caps = repo._restrictcapabilities(caps)
79 self.requirements = repo.requirements
79 self.requirements = repo.requirements
80 self.supportedformats = repo.supportedformats
80 self.supportedformats = repo.supportedformats
81
81
82 def close(self):
82 def close(self):
83 self._repo.close()
83 self._repo.close()
84
84
85 def _capabilities(self):
85 def _capabilities(self):
86 return self._caps
86 return self._caps
87
87
88 def local(self):
88 def local(self):
89 return self._repo
89 return self._repo
90
90
91 def canpush(self):
91 def canpush(self):
92 return True
92 return True
93
93
94 def url(self):
94 def url(self):
95 return self._repo.url()
95 return self._repo.url()
96
96
97 def lookup(self, key):
97 def lookup(self, key):
98 return self._repo.lookup(key)
98 return self._repo.lookup(key)
99
99
100 def branchmap(self):
100 def branchmap(self):
101 return self._repo.branchmap()
101 return self._repo.branchmap()
102
102
103 def heads(self):
103 def heads(self):
104 return self._repo.heads()
104 return self._repo.heads()
105
105
106 def known(self, nodes):
106 def known(self, nodes):
107 return self._repo.known(nodes)
107 return self._repo.known(nodes)
108
108
109 def getbundle(self, source, heads=None, common=None, bundlecaps=None,
109 def getbundle(self, source, heads=None, common=None, bundlecaps=None,
110 **kwargs):
110 **kwargs):
111 cg = exchange.getbundle(self._repo, source, heads=heads,
111 cg = exchange.getbundle(self._repo, source, heads=heads,
112 common=common, bundlecaps=bundlecaps, **kwargs)
112 common=common, bundlecaps=bundlecaps, **kwargs)
113 if bundlecaps is not None and 'HG20' in bundlecaps:
113 if bundlecaps is not None and 'HG20' in bundlecaps:
114 # When requesting a bundle2, getbundle returns a stream to make the
114 # When requesting a bundle2, getbundle returns a stream to make the
115 # wire level function happier. We need to build a proper object
115 # wire level function happier. We need to build a proper object
116 # from it in local peer.
116 # from it in local peer.
117 cg = bundle2.getunbundler(self.ui, cg)
117 cg = bundle2.getunbundler(self.ui, cg)
118 return cg
118 return cg
119
119
120 # TODO We might want to move the next two calls into legacypeer and add
120 # TODO We might want to move the next two calls into legacypeer and add
121 # unbundle instead.
121 # unbundle instead.
122
122
123 def unbundle(self, cg, heads, url):
123 def unbundle(self, cg, heads, url):
124 """apply a bundle on a repo
124 """apply a bundle on a repo
125
125
126 This function handles the repo locking itself."""
126 This function handles the repo locking itself."""
127 try:
127 try:
128 try:
128 try:
129 cg = exchange.readbundle(self.ui, cg, None)
129 cg = exchange.readbundle(self.ui, cg, None)
130 ret = exchange.unbundle(self._repo, cg, heads, 'push', url)
130 ret = exchange.unbundle(self._repo, cg, heads, 'push', url)
131 if util.safehasattr(ret, 'getchunks'):
131 if util.safehasattr(ret, 'getchunks'):
132 # This is a bundle20 object, turn it into an unbundler.
132 # This is a bundle20 object, turn it into an unbundler.
133 # This little dance should be dropped eventually when the
133 # This little dance should be dropped eventually when the
134 # API is finally improved.
134 # API is finally improved.
135 stream = util.chunkbuffer(ret.getchunks())
135 stream = util.chunkbuffer(ret.getchunks())
136 ret = bundle2.getunbundler(self.ui, stream)
136 ret = bundle2.getunbundler(self.ui, stream)
137 return ret
137 return ret
138 except Exception, exc:
138 except Exception, exc:
139 # If the exception contains output salvaged from a bundle2
139 # If the exception contains output salvaged from a bundle2
140 # reply, we need to make sure it is printed before continuing
140 # reply, we need to make sure it is printed before continuing
141 # to fail. So we build a bundle2 with such output and consume
141 # to fail. So we build a bundle2 with such output and consume
142 # it directly.
142 # it directly.
143 #
143 #
144 # This is not very elegant but allows a "simple" solution for
144 # This is not very elegant but allows a "simple" solution for
145 # issue4594
145 # issue4594
146 output = getattr(exc, '_bundle2salvagedoutput', ())
146 output = getattr(exc, '_bundle2salvagedoutput', ())
147 if output:
147 if output:
148 bundler = bundle2.bundle20(self._repo.ui)
148 bundler = bundle2.bundle20(self._repo.ui)
149 for out in output:
149 for out in output:
150 bundler.addpart(out)
150 bundler.addpart(out)
151 stream = util.chunkbuffer(bundler.getchunks())
151 stream = util.chunkbuffer(bundler.getchunks())
152 b = bundle2.getunbundler(self.ui, stream)
152 b = bundle2.getunbundler(self.ui, stream)
153 bundle2.processbundle(self._repo, b)
153 bundle2.processbundle(self._repo, b)
154 raise
154 raise
155 except error.PushRaced, exc:
155 except error.PushRaced, exc:
156 raise error.ResponseError(_('push failed:'), str(exc))
156 raise error.ResponseError(_('push failed:'), str(exc))
157
157
158 def lock(self):
158 def lock(self):
159 return self._repo.lock()
159 return self._repo.lock()
160
160
161 def addchangegroup(self, cg, source, url):
161 def addchangegroup(self, cg, source, url):
162 return changegroup.addchangegroup(self._repo, cg, source, url)
162 return changegroup.addchangegroup(self._repo, cg, source, url)
163
163
164 def pushkey(self, namespace, key, old, new):
164 def pushkey(self, namespace, key, old, new):
165 return self._repo.pushkey(namespace, key, old, new)
165 return self._repo.pushkey(namespace, key, old, new)
166
166
167 def listkeys(self, namespace):
167 def listkeys(self, namespace):
168 return self._repo.listkeys(namespace)
168 return self._repo.listkeys(namespace)
169
169
170 def debugwireargs(self, one, two, three=None, four=None, five=None):
170 def debugwireargs(self, one, two, three=None, four=None, five=None):
171 '''used to test argument passing over the wire'''
171 '''used to test argument passing over the wire'''
172 return "%s %s %s %s %s" % (one, two, three, four, five)
172 return "%s %s %s %s %s" % (one, two, three, four, five)
173
173
174 class locallegacypeer(localpeer):
174 class locallegacypeer(localpeer):
175 '''peer extension which implements legacy methods too; used for tests with
175 '''peer extension which implements legacy methods too; used for tests with
176 restricted capabilities'''
176 restricted capabilities'''
177
177
178 def __init__(self, repo):
178 def __init__(self, repo):
179 localpeer.__init__(self, repo, caps=legacycaps)
179 localpeer.__init__(self, repo, caps=legacycaps)
180
180
181 def branches(self, nodes):
181 def branches(self, nodes):
182 return self._repo.branches(nodes)
182 return self._repo.branches(nodes)
183
183
184 def between(self, pairs):
184 def between(self, pairs):
185 return self._repo.between(pairs)
185 return self._repo.between(pairs)
186
186
187 def changegroup(self, basenodes, source):
187 def changegroup(self, basenodes, source):
188 return changegroup.changegroup(self._repo, basenodes, source)
188 return changegroup.changegroup(self._repo, basenodes, source)
189
189
190 def changegroupsubset(self, bases, heads, source):
190 def changegroupsubset(self, bases, heads, source):
191 return changegroup.changegroupsubset(self._repo, bases, heads, source)
191 return changegroup.changegroupsubset(self._repo, bases, heads, source)
192
192
193 class localrepository(object):
193 class localrepository(object):
194
194
195 supportedformats = set(('revlogv1', 'generaldelta', 'treemanifest',
195 supportedformats = set(('revlogv1', 'generaldelta', 'treemanifest',
196 'manifestv2'))
196 'manifestv2'))
197 _basesupported = supportedformats | set(('store', 'fncache', 'shared',
197 _basesupported = supportedformats | set(('store', 'fncache', 'shared',
198 'dotencode'))
198 'dotencode'))
199 openerreqs = set(('revlogv1', 'generaldelta', 'treemanifest', 'manifestv2'))
199 openerreqs = set(('revlogv1', 'generaldelta', 'treemanifest', 'manifestv2'))
200 filtername = None
200 filtername = None
201
201
202 # a list of (ui, featureset) functions.
202 # a list of (ui, featureset) functions.
203 # only functions defined in module of enabled extensions are invoked
203 # only functions defined in module of enabled extensions are invoked
204 featuresetupfuncs = set()
204 featuresetupfuncs = set()
205
205
206 def _baserequirements(self, create):
206 def _baserequirements(self, create):
207 return ['revlogv1']
207 return ['revlogv1']
208
208
209 def __init__(self, baseui, path=None, create=False):
209 def __init__(self, baseui, path=None, create=False):
210 self.requirements = set()
210 self.requirements = set()
211 self.wvfs = scmutil.vfs(path, expandpath=True, realpath=True)
211 self.wvfs = scmutil.vfs(path, expandpath=True, realpath=True)
212 self.wopener = self.wvfs
212 self.wopener = self.wvfs
213 self.root = self.wvfs.base
213 self.root = self.wvfs.base
214 self.path = self.wvfs.join(".hg")
214 self.path = self.wvfs.join(".hg")
215 self.origroot = path
215 self.origroot = path
216 self.auditor = pathutil.pathauditor(self.root, self._checknested)
216 self.auditor = pathutil.pathauditor(self.root, self._checknested)
217 self.vfs = scmutil.vfs(self.path)
217 self.vfs = scmutil.vfs(self.path)
218 self.opener = self.vfs
218 self.opener = self.vfs
219 self.baseui = baseui
219 self.baseui = baseui
220 self.ui = baseui.copy()
220 self.ui = baseui.copy()
221 self.ui.copy = baseui.copy # prevent copying repo configuration
221 self.ui.copy = baseui.copy # prevent copying repo configuration
222 # A list of callback to shape the phase if no data were found.
222 # A list of callback to shape the phase if no data were found.
223 # Callback are in the form: func(repo, roots) --> processed root.
223 # Callback are in the form: func(repo, roots) --> processed root.
224 # This list it to be filled by extension during repo setup
224 # This list it to be filled by extension during repo setup
225 self._phasedefaults = []
225 self._phasedefaults = []
226 try:
226 try:
227 self.ui.readconfig(self.join("hgrc"), self.root)
227 self.ui.readconfig(self.join("hgrc"), self.root)
228 extensions.loadall(self.ui)
228 extensions.loadall(self.ui)
229 except IOError:
229 except IOError:
230 pass
230 pass
231
231
232 if self.featuresetupfuncs:
232 if self.featuresetupfuncs:
233 self.supported = set(self._basesupported) # use private copy
233 self.supported = set(self._basesupported) # use private copy
234 extmods = set(m.__name__ for n, m
234 extmods = set(m.__name__ for n, m
235 in extensions.extensions(self.ui))
235 in extensions.extensions(self.ui))
236 for setupfunc in self.featuresetupfuncs:
236 for setupfunc in self.featuresetupfuncs:
237 if setupfunc.__module__ in extmods:
237 if setupfunc.__module__ in extmods:
238 setupfunc(self.ui, self.supported)
238 setupfunc(self.ui, self.supported)
239 else:
239 else:
240 self.supported = self._basesupported
240 self.supported = self._basesupported
241
241
242 if not self.vfs.isdir():
242 if not self.vfs.isdir():
243 if create:
243 if create:
244 if not self.wvfs.exists():
244 if not self.wvfs.exists():
245 self.wvfs.makedirs()
245 self.wvfs.makedirs()
246 self.vfs.makedir(notindexed=True)
246 self.vfs.makedir(notindexed=True)
247 self.requirements.update(self._baserequirements(create))
247 self.requirements.update(self._baserequirements(create))
248 if self.ui.configbool('format', 'usestore', True):
248 if self.ui.configbool('format', 'usestore', True):
249 self.vfs.mkdir("store")
249 self.vfs.mkdir("store")
250 self.requirements.add("store")
250 self.requirements.add("store")
251 if self.ui.configbool('format', 'usefncache', True):
251 if self.ui.configbool('format', 'usefncache', True):
252 self.requirements.add("fncache")
252 self.requirements.add("fncache")
253 if self.ui.configbool('format', 'dotencode', True):
253 if self.ui.configbool('format', 'dotencode', True):
254 self.requirements.add('dotencode')
254 self.requirements.add('dotencode')
255 # create an invalid changelog
255 # create an invalid changelog
256 self.vfs.append(
256 self.vfs.append(
257 "00changelog.i",
257 "00changelog.i",
258 '\0\0\0\2' # represents revlogv2
258 '\0\0\0\2' # represents revlogv2
259 ' dummy changelog to prevent using the old repo layout'
259 ' dummy changelog to prevent using the old repo layout'
260 )
260 )
261 if self.ui.configbool('format', 'generaldelta', False):
261 if self.ui.configbool('format', 'generaldelta', False):
262 self.requirements.add("generaldelta")
262 self.requirements.add("generaldelta")
263 if self.ui.configbool('experimental', 'treemanifest', False):
263 if self.ui.configbool('experimental', 'treemanifest', False):
264 self.requirements.add("treemanifest")
264 self.requirements.add("treemanifest")
265 if self.ui.configbool('experimental', 'manifestv2', False):
265 if self.ui.configbool('experimental', 'manifestv2', False):
266 self.requirements.add("manifestv2")
266 self.requirements.add("manifestv2")
267 else:
267 else:
268 raise error.RepoError(_("repository %s not found") % path)
268 raise error.RepoError(_("repository %s not found") % path)
269 elif create:
269 elif create:
270 raise error.RepoError(_("repository %s already exists") % path)
270 raise error.RepoError(_("repository %s already exists") % path)
271 else:
271 else:
272 try:
272 try:
273 self.requirements = scmutil.readrequires(
273 self.requirements = scmutil.readrequires(
274 self.vfs, self.supported)
274 self.vfs, self.supported)
275 except IOError, inst:
275 except IOError, inst:
276 if inst.errno != errno.ENOENT:
276 if inst.errno != errno.ENOENT:
277 raise
277 raise
278
278
279 self.sharedpath = self.path
279 self.sharedpath = self.path
280 try:
280 try:
281 vfs = scmutil.vfs(self.vfs.read("sharedpath").rstrip('\n'),
281 vfs = scmutil.vfs(self.vfs.read("sharedpath").rstrip('\n'),
282 realpath=True)
282 realpath=True)
283 s = vfs.base
283 s = vfs.base
284 if not vfs.exists():
284 if not vfs.exists():
285 raise error.RepoError(
285 raise error.RepoError(
286 _('.hg/sharedpath points to nonexistent directory %s') % s)
286 _('.hg/sharedpath points to nonexistent directory %s') % s)
287 self.sharedpath = s
287 self.sharedpath = s
288 except IOError, inst:
288 except IOError, inst:
289 if inst.errno != errno.ENOENT:
289 if inst.errno != errno.ENOENT:
290 raise
290 raise
291
291
292 self.store = store.store(
292 self.store = store.store(
293 self.requirements, self.sharedpath, scmutil.vfs)
293 self.requirements, self.sharedpath, scmutil.vfs)
294 self.spath = self.store.path
294 self.spath = self.store.path
295 self.svfs = self.store.vfs
295 self.svfs = self.store.vfs
296 self.sopener = self.svfs
296 self.sopener = self.svfs
297 self.sjoin = self.store.join
297 self.sjoin = self.store.join
298 self.vfs.createmode = self.store.createmode
298 self.vfs.createmode = self.store.createmode
299 self._applyopenerreqs()
299 self._applyopenerreqs()
300 if create:
300 if create:
301 self._writerequirements()
301 self._writerequirements()
302
302
303
303
304 self._branchcaches = {}
304 self._branchcaches = {}
305 self._revbranchcache = None
305 self._revbranchcache = None
306 self.filterpats = {}
306 self.filterpats = {}
307 self._datafilters = {}
307 self._datafilters = {}
308 self._transref = self._lockref = self._wlockref = None
308 self._transref = self._lockref = self._wlockref = None
309
309
310 # A cache for various files under .hg/ that tracks file changes,
310 # A cache for various files under .hg/ that tracks file changes,
311 # (used by the filecache decorator)
311 # (used by the filecache decorator)
312 #
312 #
313 # Maps a property name to its util.filecacheentry
313 # Maps a property name to its util.filecacheentry
314 self._filecache = {}
314 self._filecache = {}
315
315
316 # hold sets of revision to be filtered
316 # hold sets of revision to be filtered
317 # should be cleared when something might have changed the filter value:
317 # should be cleared when something might have changed the filter value:
318 # - new changesets,
318 # - new changesets,
319 # - phase change,
319 # - phase change,
320 # - new obsolescence marker,
320 # - new obsolescence marker,
321 # - working directory parent change,
321 # - working directory parent change,
322 # - bookmark changes
322 # - bookmark changes
323 self.filteredrevcache = {}
323 self.filteredrevcache = {}
324
324
325 # generic mapping between names and nodes
325 # generic mapping between names and nodes
326 self.names = namespaces.namespaces()
326 self.names = namespaces.namespaces()
327
327
328 def close(self):
328 def close(self):
329 self._writecaches()
329 self._writecaches()
330
330
331 def _writecaches(self):
331 def _writecaches(self):
332 if self._revbranchcache:
332 if self._revbranchcache:
333 self._revbranchcache.write()
333 self._revbranchcache.write()
334
334
335 def _restrictcapabilities(self, caps):
335 def _restrictcapabilities(self, caps):
336 if self.ui.configbool('experimental', 'bundle2-advertise', True):
336 if self.ui.configbool('experimental', 'bundle2-advertise', True):
337 caps = set(caps)
337 caps = set(caps)
338 capsblob = bundle2.encodecaps(bundle2.getrepocaps(self))
338 capsblob = bundle2.encodecaps(bundle2.getrepocaps(self))
339 caps.add('bundle2=' + urllib.quote(capsblob))
339 caps.add('bundle2=' + urllib.quote(capsblob))
340 return caps
340 return caps
341
341
342 def _applyopenerreqs(self):
342 def _applyopenerreqs(self):
343 self.svfs.options = dict((r, 1) for r in self.requirements
343 self.svfs.options = dict((r, 1) for r in self.requirements
344 if r in self.openerreqs)
344 if r in self.openerreqs)
345 chunkcachesize = self.ui.configint('format', 'chunkcachesize')
345 chunkcachesize = self.ui.configint('format', 'chunkcachesize')
346 if chunkcachesize is not None:
346 if chunkcachesize is not None:
347 self.svfs.options['chunkcachesize'] = chunkcachesize
347 self.svfs.options['chunkcachesize'] = chunkcachesize
348 maxchainlen = self.ui.configint('format', 'maxchainlen')
348 maxchainlen = self.ui.configint('format', 'maxchainlen')
349 if maxchainlen is not None:
349 if maxchainlen is not None:
350 self.svfs.options['maxchainlen'] = maxchainlen
350 self.svfs.options['maxchainlen'] = maxchainlen
351 manifestcachesize = self.ui.configint('format', 'manifestcachesize')
351 manifestcachesize = self.ui.configint('format', 'manifestcachesize')
352 if manifestcachesize is not None:
352 if manifestcachesize is not None:
353 self.svfs.options['manifestcachesize'] = manifestcachesize
353 self.svfs.options['manifestcachesize'] = manifestcachesize
354
354
355 def _writerequirements(self):
355 def _writerequirements(self):
356 scmutil.writerequires(self.vfs, self.requirements)
356 scmutil.writerequires(self.vfs, self.requirements)
357
357
358 def _checknested(self, path):
358 def _checknested(self, path):
359 """Determine if path is a legal nested repository."""
359 """Determine if path is a legal nested repository."""
360 if not path.startswith(self.root):
360 if not path.startswith(self.root):
361 return False
361 return False
362 subpath = path[len(self.root) + 1:]
362 subpath = path[len(self.root) + 1:]
363 normsubpath = util.pconvert(subpath)
363 normsubpath = util.pconvert(subpath)
364
364
365 # XXX: Checking against the current working copy is wrong in
365 # XXX: Checking against the current working copy is wrong in
366 # the sense that it can reject things like
366 # the sense that it can reject things like
367 #
367 #
368 # $ hg cat -r 10 sub/x.txt
368 # $ hg cat -r 10 sub/x.txt
369 #
369 #
370 # if sub/ is no longer a subrepository in the working copy
370 # if sub/ is no longer a subrepository in the working copy
371 # parent revision.
371 # parent revision.
372 #
372 #
373 # However, it can of course also allow things that would have
373 # However, it can of course also allow things that would have
374 # been rejected before, such as the above cat command if sub/
374 # been rejected before, such as the above cat command if sub/
375 # is a subrepository now, but was a normal directory before.
375 # is a subrepository now, but was a normal directory before.
376 # The old path auditor would have rejected by mistake since it
376 # The old path auditor would have rejected by mistake since it
377 # panics when it sees sub/.hg/.
377 # panics when it sees sub/.hg/.
378 #
378 #
379 # All in all, checking against the working copy seems sensible
379 # All in all, checking against the working copy seems sensible
380 # since we want to prevent access to nested repositories on
380 # since we want to prevent access to nested repositories on
381 # the filesystem *now*.
381 # the filesystem *now*.
382 ctx = self[None]
382 ctx = self[None]
383 parts = util.splitpath(subpath)
383 parts = util.splitpath(subpath)
384 while parts:
384 while parts:
385 prefix = '/'.join(parts)
385 prefix = '/'.join(parts)
386 if prefix in ctx.substate:
386 if prefix in ctx.substate:
387 if prefix == normsubpath:
387 if prefix == normsubpath:
388 return True
388 return True
389 else:
389 else:
390 sub = ctx.sub(prefix)
390 sub = ctx.sub(prefix)
391 return sub.checknested(subpath[len(prefix) + 1:])
391 return sub.checknested(subpath[len(prefix) + 1:])
392 else:
392 else:
393 parts.pop()
393 parts.pop()
394 return False
394 return False
395
395
396 def peer(self):
396 def peer(self):
397 return localpeer(self) # not cached to avoid reference cycle
397 return localpeer(self) # not cached to avoid reference cycle
398
398
399 def unfiltered(self):
399 def unfiltered(self):
400 """Return unfiltered version of the repository
400 """Return unfiltered version of the repository
401
401
402 Intended to be overwritten by filtered repo."""
402 Intended to be overwritten by filtered repo."""
403 return self
403 return self
404
404
405 def filtered(self, name):
405 def filtered(self, name):
406 """Return a filtered version of a repository"""
406 """Return a filtered version of a repository"""
407 # build a new class with the mixin and the current class
407 # build a new class with the mixin and the current class
408 # (possibly subclass of the repo)
408 # (possibly subclass of the repo)
409 class proxycls(repoview.repoview, self.unfiltered().__class__):
409 class proxycls(repoview.repoview, self.unfiltered().__class__):
410 pass
410 pass
411 return proxycls(self, name)
411 return proxycls(self, name)
412
412
413 @repofilecache('bookmarks')
413 @repofilecache('bookmarks')
414 def _bookmarks(self):
414 def _bookmarks(self):
415 return bookmarks.bmstore(self)
415 return bookmarks.bmstore(self)
416
416
417 @repofilecache('bookmarks.current')
417 @repofilecache('bookmarks.current')
418 def _activebookmark(self):
418 def _activebookmark(self):
419 return bookmarks.readactive(self)
419 return bookmarks.readactive(self)
420
420
421 def bookmarkheads(self, bookmark):
421 def bookmarkheads(self, bookmark):
422 name = bookmark.split('@', 1)[0]
422 name = bookmark.split('@', 1)[0]
423 heads = []
423 heads = []
424 for mark, n in self._bookmarks.iteritems():
424 for mark, n in self._bookmarks.iteritems():
425 if mark.split('@', 1)[0] == name:
425 if mark.split('@', 1)[0] == name:
426 heads.append(n)
426 heads.append(n)
427 return heads
427 return heads
428
428
429 @storecache('phaseroots')
429 @storecache('phaseroots')
430 def _phasecache(self):
430 def _phasecache(self):
431 return phases.phasecache(self, self._phasedefaults)
431 return phases.phasecache(self, self._phasedefaults)
432
432
433 @storecache('obsstore')
433 @storecache('obsstore')
434 def obsstore(self):
434 def obsstore(self):
435 # read default format for new obsstore.
435 # read default format for new obsstore.
436 defaultformat = self.ui.configint('format', 'obsstore-version', None)
436 defaultformat = self.ui.configint('format', 'obsstore-version', None)
437 # rely on obsstore class default when possible.
437 # rely on obsstore class default when possible.
438 kwargs = {}
438 kwargs = {}
439 if defaultformat is not None:
439 if defaultformat is not None:
440 kwargs['defaultformat'] = defaultformat
440 kwargs['defaultformat'] = defaultformat
441 readonly = not obsolete.isenabled(self, obsolete.createmarkersopt)
441 readonly = not obsolete.isenabled(self, obsolete.createmarkersopt)
442 store = obsolete.obsstore(self.svfs, readonly=readonly,
442 store = obsolete.obsstore(self.svfs, readonly=readonly,
443 **kwargs)
443 **kwargs)
444 if store and readonly:
444 if store and readonly:
445 self.ui.warn(
445 self.ui.warn(
446 _('obsolete feature not enabled but %i markers found!\n')
446 _('obsolete feature not enabled but %i markers found!\n')
447 % len(list(store)))
447 % len(list(store)))
448 return store
448 return store
449
449
450 @storecache('00changelog.i')
450 @storecache('00changelog.i')
451 def changelog(self):
451 def changelog(self):
452 c = changelog.changelog(self.svfs)
452 c = changelog.changelog(self.svfs)
453 if 'HG_PENDING' in os.environ:
453 if 'HG_PENDING' in os.environ:
454 p = os.environ['HG_PENDING']
454 p = os.environ['HG_PENDING']
455 if p.startswith(self.root):
455 if p.startswith(self.root):
456 c.readpending('00changelog.i.a')
456 c.readpending('00changelog.i.a')
457 return c
457 return c
458
458
459 @storecache('00manifest.i')
459 @storecache('00manifest.i')
460 def manifest(self):
460 def manifest(self):
461 return manifest.manifest(self.svfs)
461 return manifest.manifest(self.svfs)
462
462
463 def dirlog(self, dir):
464 return manifest.manifest(self.svfs, dir)
465
463 @repofilecache('dirstate')
466 @repofilecache('dirstate')
464 def dirstate(self):
467 def dirstate(self):
465 warned = [0]
468 warned = [0]
466 def validate(node):
469 def validate(node):
467 try:
470 try:
468 self.changelog.rev(node)
471 self.changelog.rev(node)
469 return node
472 return node
470 except error.LookupError:
473 except error.LookupError:
471 if not warned[0]:
474 if not warned[0]:
472 warned[0] = True
475 warned[0] = True
473 self.ui.warn(_("warning: ignoring unknown"
476 self.ui.warn(_("warning: ignoring unknown"
474 " working parent %s!\n") % short(node))
477 " working parent %s!\n") % short(node))
475 return nullid
478 return nullid
476
479
477 return dirstate.dirstate(self.vfs, self.ui, self.root, validate)
480 return dirstate.dirstate(self.vfs, self.ui, self.root, validate)
478
481
479 def __getitem__(self, changeid):
482 def __getitem__(self, changeid):
480 if changeid is None:
483 if changeid is None:
481 return context.workingctx(self)
484 return context.workingctx(self)
482 if isinstance(changeid, slice):
485 if isinstance(changeid, slice):
483 return [context.changectx(self, i)
486 return [context.changectx(self, i)
484 for i in xrange(*changeid.indices(len(self)))
487 for i in xrange(*changeid.indices(len(self)))
485 if i not in self.changelog.filteredrevs]
488 if i not in self.changelog.filteredrevs]
486 return context.changectx(self, changeid)
489 return context.changectx(self, changeid)
487
490
488 def __contains__(self, changeid):
491 def __contains__(self, changeid):
489 try:
492 try:
490 self[changeid]
493 self[changeid]
491 return True
494 return True
492 except error.RepoLookupError:
495 except error.RepoLookupError:
493 return False
496 return False
494
497
495 def __nonzero__(self):
498 def __nonzero__(self):
496 return True
499 return True
497
500
498 def __len__(self):
501 def __len__(self):
499 return len(self.changelog)
502 return len(self.changelog)
500
503
501 def __iter__(self):
504 def __iter__(self):
502 return iter(self.changelog)
505 return iter(self.changelog)
503
506
504 def revs(self, expr, *args):
507 def revs(self, expr, *args):
505 '''Return a list of revisions matching the given revset'''
508 '''Return a list of revisions matching the given revset'''
506 expr = revset.formatspec(expr, *args)
509 expr = revset.formatspec(expr, *args)
507 m = revset.match(None, expr)
510 m = revset.match(None, expr)
508 return m(self)
511 return m(self)
509
512
510 def set(self, expr, *args):
513 def set(self, expr, *args):
511 '''
514 '''
512 Yield a context for each matching revision, after doing arg
515 Yield a context for each matching revision, after doing arg
513 replacement via revset.formatspec
516 replacement via revset.formatspec
514 '''
517 '''
515 for r in self.revs(expr, *args):
518 for r in self.revs(expr, *args):
516 yield self[r]
519 yield self[r]
517
520
518 def url(self):
521 def url(self):
519 return 'file:' + self.root
522 return 'file:' + self.root
520
523
521 def hook(self, name, throw=False, **args):
524 def hook(self, name, throw=False, **args):
522 """Call a hook, passing this repo instance.
525 """Call a hook, passing this repo instance.
523
526
524 This a convenience method to aid invoking hooks. Extensions likely
527 This a convenience method to aid invoking hooks. Extensions likely
525 won't call this unless they have registered a custom hook or are
528 won't call this unless they have registered a custom hook or are
526 replacing code that is expected to call a hook.
529 replacing code that is expected to call a hook.
527 """
530 """
528 return hook.hook(self.ui, self, name, throw, **args)
531 return hook.hook(self.ui, self, name, throw, **args)
529
532
530 @unfilteredmethod
533 @unfilteredmethod
531 def _tag(self, names, node, message, local, user, date, extra={},
534 def _tag(self, names, node, message, local, user, date, extra={},
532 editor=False):
535 editor=False):
533 if isinstance(names, str):
536 if isinstance(names, str):
534 names = (names,)
537 names = (names,)
535
538
536 branches = self.branchmap()
539 branches = self.branchmap()
537 for name in names:
540 for name in names:
538 self.hook('pretag', throw=True, node=hex(node), tag=name,
541 self.hook('pretag', throw=True, node=hex(node), tag=name,
539 local=local)
542 local=local)
540 if name in branches:
543 if name in branches:
541 self.ui.warn(_("warning: tag %s conflicts with existing"
544 self.ui.warn(_("warning: tag %s conflicts with existing"
542 " branch name\n") % name)
545 " branch name\n") % name)
543
546
544 def writetags(fp, names, munge, prevtags):
547 def writetags(fp, names, munge, prevtags):
545 fp.seek(0, 2)
548 fp.seek(0, 2)
546 if prevtags and prevtags[-1] != '\n':
549 if prevtags and prevtags[-1] != '\n':
547 fp.write('\n')
550 fp.write('\n')
548 for name in names:
551 for name in names:
549 if munge:
552 if munge:
550 m = munge(name)
553 m = munge(name)
551 else:
554 else:
552 m = name
555 m = name
553
556
554 if (self._tagscache.tagtypes and
557 if (self._tagscache.tagtypes and
555 name in self._tagscache.tagtypes):
558 name in self._tagscache.tagtypes):
556 old = self.tags().get(name, nullid)
559 old = self.tags().get(name, nullid)
557 fp.write('%s %s\n' % (hex(old), m))
560 fp.write('%s %s\n' % (hex(old), m))
558 fp.write('%s %s\n' % (hex(node), m))
561 fp.write('%s %s\n' % (hex(node), m))
559 fp.close()
562 fp.close()
560
563
561 prevtags = ''
564 prevtags = ''
562 if local:
565 if local:
563 try:
566 try:
564 fp = self.vfs('localtags', 'r+')
567 fp = self.vfs('localtags', 'r+')
565 except IOError:
568 except IOError:
566 fp = self.vfs('localtags', 'a')
569 fp = self.vfs('localtags', 'a')
567 else:
570 else:
568 prevtags = fp.read()
571 prevtags = fp.read()
569
572
570 # local tags are stored in the current charset
573 # local tags are stored in the current charset
571 writetags(fp, names, None, prevtags)
574 writetags(fp, names, None, prevtags)
572 for name in names:
575 for name in names:
573 self.hook('tag', node=hex(node), tag=name, local=local)
576 self.hook('tag', node=hex(node), tag=name, local=local)
574 return
577 return
575
578
576 try:
579 try:
577 fp = self.wfile('.hgtags', 'rb+')
580 fp = self.wfile('.hgtags', 'rb+')
578 except IOError, e:
581 except IOError, e:
579 if e.errno != errno.ENOENT:
582 if e.errno != errno.ENOENT:
580 raise
583 raise
581 fp = self.wfile('.hgtags', 'ab')
584 fp = self.wfile('.hgtags', 'ab')
582 else:
585 else:
583 prevtags = fp.read()
586 prevtags = fp.read()
584
587
585 # committed tags are stored in UTF-8
588 # committed tags are stored in UTF-8
586 writetags(fp, names, encoding.fromlocal, prevtags)
589 writetags(fp, names, encoding.fromlocal, prevtags)
587
590
588 fp.close()
591 fp.close()
589
592
590 self.invalidatecaches()
593 self.invalidatecaches()
591
594
592 if '.hgtags' not in self.dirstate:
595 if '.hgtags' not in self.dirstate:
593 self[None].add(['.hgtags'])
596 self[None].add(['.hgtags'])
594
597
595 m = matchmod.exact(self.root, '', ['.hgtags'])
598 m = matchmod.exact(self.root, '', ['.hgtags'])
596 tagnode = self.commit(message, user, date, extra=extra, match=m,
599 tagnode = self.commit(message, user, date, extra=extra, match=m,
597 editor=editor)
600 editor=editor)
598
601
599 for name in names:
602 for name in names:
600 self.hook('tag', node=hex(node), tag=name, local=local)
603 self.hook('tag', node=hex(node), tag=name, local=local)
601
604
602 return tagnode
605 return tagnode
603
606
604 def tag(self, names, node, message, local, user, date, editor=False):
607 def tag(self, names, node, message, local, user, date, editor=False):
605 '''tag a revision with one or more symbolic names.
608 '''tag a revision with one or more symbolic names.
606
609
607 names is a list of strings or, when adding a single tag, names may be a
610 names is a list of strings or, when adding a single tag, names may be a
608 string.
611 string.
609
612
610 if local is True, the tags are stored in a per-repository file.
613 if local is True, the tags are stored in a per-repository file.
611 otherwise, they are stored in the .hgtags file, and a new
614 otherwise, they are stored in the .hgtags file, and a new
612 changeset is committed with the change.
615 changeset is committed with the change.
613
616
614 keyword arguments:
617 keyword arguments:
615
618
616 local: whether to store tags in non-version-controlled file
619 local: whether to store tags in non-version-controlled file
617 (default False)
620 (default False)
618
621
619 message: commit message to use if committing
622 message: commit message to use if committing
620
623
621 user: name of user to use if committing
624 user: name of user to use if committing
622
625
623 date: date tuple to use if committing'''
626 date: date tuple to use if committing'''
624
627
625 if not local:
628 if not local:
626 m = matchmod.exact(self.root, '', ['.hgtags'])
629 m = matchmod.exact(self.root, '', ['.hgtags'])
627 if util.any(self.status(match=m, unknown=True, ignored=True)):
630 if util.any(self.status(match=m, unknown=True, ignored=True)):
628 raise util.Abort(_('working copy of .hgtags is changed'),
631 raise util.Abort(_('working copy of .hgtags is changed'),
629 hint=_('please commit .hgtags manually'))
632 hint=_('please commit .hgtags manually'))
630
633
631 self.tags() # instantiate the cache
634 self.tags() # instantiate the cache
632 self._tag(names, node, message, local, user, date, editor=editor)
635 self._tag(names, node, message, local, user, date, editor=editor)
633
636
634 @filteredpropertycache
637 @filteredpropertycache
635 def _tagscache(self):
638 def _tagscache(self):
636 '''Returns a tagscache object that contains various tags related
639 '''Returns a tagscache object that contains various tags related
637 caches.'''
640 caches.'''
638
641
639 # This simplifies its cache management by having one decorated
642 # This simplifies its cache management by having one decorated
640 # function (this one) and the rest simply fetch things from it.
643 # function (this one) and the rest simply fetch things from it.
641 class tagscache(object):
644 class tagscache(object):
642 def __init__(self):
645 def __init__(self):
643 # These two define the set of tags for this repository. tags
646 # These two define the set of tags for this repository. tags
644 # maps tag name to node; tagtypes maps tag name to 'global' or
647 # maps tag name to node; tagtypes maps tag name to 'global' or
645 # 'local'. (Global tags are defined by .hgtags across all
648 # 'local'. (Global tags are defined by .hgtags across all
646 # heads, and local tags are defined in .hg/localtags.)
649 # heads, and local tags are defined in .hg/localtags.)
647 # They constitute the in-memory cache of tags.
650 # They constitute the in-memory cache of tags.
648 self.tags = self.tagtypes = None
651 self.tags = self.tagtypes = None
649
652
650 self.nodetagscache = self.tagslist = None
653 self.nodetagscache = self.tagslist = None
651
654
652 cache = tagscache()
655 cache = tagscache()
653 cache.tags, cache.tagtypes = self._findtags()
656 cache.tags, cache.tagtypes = self._findtags()
654
657
655 return cache
658 return cache
656
659
657 def tags(self):
660 def tags(self):
658 '''return a mapping of tag to node'''
661 '''return a mapping of tag to node'''
659 t = {}
662 t = {}
660 if self.changelog.filteredrevs:
663 if self.changelog.filteredrevs:
661 tags, tt = self._findtags()
664 tags, tt = self._findtags()
662 else:
665 else:
663 tags = self._tagscache.tags
666 tags = self._tagscache.tags
664 for k, v in tags.iteritems():
667 for k, v in tags.iteritems():
665 try:
668 try:
666 # ignore tags to unknown nodes
669 # ignore tags to unknown nodes
667 self.changelog.rev(v)
670 self.changelog.rev(v)
668 t[k] = v
671 t[k] = v
669 except (error.LookupError, ValueError):
672 except (error.LookupError, ValueError):
670 pass
673 pass
671 return t
674 return t
672
675
673 def _findtags(self):
676 def _findtags(self):
674 '''Do the hard work of finding tags. Return a pair of dicts
677 '''Do the hard work of finding tags. Return a pair of dicts
675 (tags, tagtypes) where tags maps tag name to node, and tagtypes
678 (tags, tagtypes) where tags maps tag name to node, and tagtypes
676 maps tag name to a string like \'global\' or \'local\'.
679 maps tag name to a string like \'global\' or \'local\'.
677 Subclasses or extensions are free to add their own tags, but
680 Subclasses or extensions are free to add their own tags, but
678 should be aware that the returned dicts will be retained for the
681 should be aware that the returned dicts will be retained for the
679 duration of the localrepo object.'''
682 duration of the localrepo object.'''
680
683
681 # XXX what tagtype should subclasses/extensions use? Currently
684 # XXX what tagtype should subclasses/extensions use? Currently
682 # mq and bookmarks add tags, but do not set the tagtype at all.
685 # mq and bookmarks add tags, but do not set the tagtype at all.
683 # Should each extension invent its own tag type? Should there
686 # Should each extension invent its own tag type? Should there
684 # be one tagtype for all such "virtual" tags? Or is the status
687 # be one tagtype for all such "virtual" tags? Or is the status
685 # quo fine?
688 # quo fine?
686
689
687 alltags = {} # map tag name to (node, hist)
690 alltags = {} # map tag name to (node, hist)
688 tagtypes = {}
691 tagtypes = {}
689
692
690 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
693 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
691 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
694 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
692
695
693 # Build the return dicts. Have to re-encode tag names because
696 # Build the return dicts. Have to re-encode tag names because
694 # the tags module always uses UTF-8 (in order not to lose info
697 # the tags module always uses UTF-8 (in order not to lose info
695 # writing to the cache), but the rest of Mercurial wants them in
698 # writing to the cache), but the rest of Mercurial wants them in
696 # local encoding.
699 # local encoding.
697 tags = {}
700 tags = {}
698 for (name, (node, hist)) in alltags.iteritems():
701 for (name, (node, hist)) in alltags.iteritems():
699 if node != nullid:
702 if node != nullid:
700 tags[encoding.tolocal(name)] = node
703 tags[encoding.tolocal(name)] = node
701 tags['tip'] = self.changelog.tip()
704 tags['tip'] = self.changelog.tip()
702 tagtypes = dict([(encoding.tolocal(name), value)
705 tagtypes = dict([(encoding.tolocal(name), value)
703 for (name, value) in tagtypes.iteritems()])
706 for (name, value) in tagtypes.iteritems()])
704 return (tags, tagtypes)
707 return (tags, tagtypes)
705
708
706 def tagtype(self, tagname):
709 def tagtype(self, tagname):
707 '''
710 '''
708 return the type of the given tag. result can be:
711 return the type of the given tag. result can be:
709
712
710 'local' : a local tag
713 'local' : a local tag
711 'global' : a global tag
714 'global' : a global tag
712 None : tag does not exist
715 None : tag does not exist
713 '''
716 '''
714
717
715 return self._tagscache.tagtypes.get(tagname)
718 return self._tagscache.tagtypes.get(tagname)
716
719
717 def tagslist(self):
720 def tagslist(self):
718 '''return a list of tags ordered by revision'''
721 '''return a list of tags ordered by revision'''
719 if not self._tagscache.tagslist:
722 if not self._tagscache.tagslist:
720 l = []
723 l = []
721 for t, n in self.tags().iteritems():
724 for t, n in self.tags().iteritems():
722 l.append((self.changelog.rev(n), t, n))
725 l.append((self.changelog.rev(n), t, n))
723 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
726 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
724
727
725 return self._tagscache.tagslist
728 return self._tagscache.tagslist
726
729
727 def nodetags(self, node):
730 def nodetags(self, node):
728 '''return the tags associated with a node'''
731 '''return the tags associated with a node'''
729 if not self._tagscache.nodetagscache:
732 if not self._tagscache.nodetagscache:
730 nodetagscache = {}
733 nodetagscache = {}
731 for t, n in self._tagscache.tags.iteritems():
734 for t, n in self._tagscache.tags.iteritems():
732 nodetagscache.setdefault(n, []).append(t)
735 nodetagscache.setdefault(n, []).append(t)
733 for tags in nodetagscache.itervalues():
736 for tags in nodetagscache.itervalues():
734 tags.sort()
737 tags.sort()
735 self._tagscache.nodetagscache = nodetagscache
738 self._tagscache.nodetagscache = nodetagscache
736 return self._tagscache.nodetagscache.get(node, [])
739 return self._tagscache.nodetagscache.get(node, [])
737
740
738 def nodebookmarks(self, node):
741 def nodebookmarks(self, node):
739 marks = []
742 marks = []
740 for bookmark, n in self._bookmarks.iteritems():
743 for bookmark, n in self._bookmarks.iteritems():
741 if n == node:
744 if n == node:
742 marks.append(bookmark)
745 marks.append(bookmark)
743 return sorted(marks)
746 return sorted(marks)
744
747
745 def branchmap(self):
748 def branchmap(self):
746 '''returns a dictionary {branch: [branchheads]} with branchheads
749 '''returns a dictionary {branch: [branchheads]} with branchheads
747 ordered by increasing revision number'''
750 ordered by increasing revision number'''
748 branchmap.updatecache(self)
751 branchmap.updatecache(self)
749 return self._branchcaches[self.filtername]
752 return self._branchcaches[self.filtername]
750
753
751 @unfilteredmethod
754 @unfilteredmethod
752 def revbranchcache(self):
755 def revbranchcache(self):
753 if not self._revbranchcache:
756 if not self._revbranchcache:
754 self._revbranchcache = branchmap.revbranchcache(self.unfiltered())
757 self._revbranchcache = branchmap.revbranchcache(self.unfiltered())
755 return self._revbranchcache
758 return self._revbranchcache
756
759
757 def branchtip(self, branch, ignoremissing=False):
760 def branchtip(self, branch, ignoremissing=False):
758 '''return the tip node for a given branch
761 '''return the tip node for a given branch
759
762
760 If ignoremissing is True, then this method will not raise an error.
763 If ignoremissing is True, then this method will not raise an error.
761 This is helpful for callers that only expect None for a missing branch
764 This is helpful for callers that only expect None for a missing branch
762 (e.g. namespace).
765 (e.g. namespace).
763
766
764 '''
767 '''
765 try:
768 try:
766 return self.branchmap().branchtip(branch)
769 return self.branchmap().branchtip(branch)
767 except KeyError:
770 except KeyError:
768 if not ignoremissing:
771 if not ignoremissing:
769 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
772 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
770 else:
773 else:
771 pass
774 pass
772
775
773 def lookup(self, key):
776 def lookup(self, key):
774 return self[key].node()
777 return self[key].node()
775
778
776 def lookupbranch(self, key, remote=None):
779 def lookupbranch(self, key, remote=None):
777 repo = remote or self
780 repo = remote or self
778 if key in repo.branchmap():
781 if key in repo.branchmap():
779 return key
782 return key
780
783
781 repo = (remote and remote.local()) and remote or self
784 repo = (remote and remote.local()) and remote or self
782 return repo[key].branch()
785 return repo[key].branch()
783
786
784 def known(self, nodes):
787 def known(self, nodes):
785 nm = self.changelog.nodemap
788 nm = self.changelog.nodemap
786 pc = self._phasecache
789 pc = self._phasecache
787 result = []
790 result = []
788 for n in nodes:
791 for n in nodes:
789 r = nm.get(n)
792 r = nm.get(n)
790 resp = not (r is None or pc.phase(self, r) >= phases.secret)
793 resp = not (r is None or pc.phase(self, r) >= phases.secret)
791 result.append(resp)
794 result.append(resp)
792 return result
795 return result
793
796
794 def local(self):
797 def local(self):
795 return self
798 return self
796
799
797 def cancopy(self):
800 def cancopy(self):
798 # so statichttprepo's override of local() works
801 # so statichttprepo's override of local() works
799 if not self.local():
802 if not self.local():
800 return False
803 return False
801 if not self.ui.configbool('phases', 'publish', True):
804 if not self.ui.configbool('phases', 'publish', True):
802 return True
805 return True
803 # if publishing we can't copy if there is filtered content
806 # if publishing we can't copy if there is filtered content
804 return not self.filtered('visible').changelog.filteredrevs
807 return not self.filtered('visible').changelog.filteredrevs
805
808
806 def shared(self):
809 def shared(self):
807 '''the type of shared repository (None if not shared)'''
810 '''the type of shared repository (None if not shared)'''
808 if self.sharedpath != self.path:
811 if self.sharedpath != self.path:
809 return 'store'
812 return 'store'
810 return None
813 return None
811
814
812 def join(self, f, *insidef):
815 def join(self, f, *insidef):
813 return self.vfs.join(os.path.join(f, *insidef))
816 return self.vfs.join(os.path.join(f, *insidef))
814
817
815 def wjoin(self, f, *insidef):
818 def wjoin(self, f, *insidef):
816 return self.vfs.reljoin(self.root, f, *insidef)
819 return self.vfs.reljoin(self.root, f, *insidef)
817
820
818 def file(self, f):
821 def file(self, f):
819 if f[0] == '/':
822 if f[0] == '/':
820 f = f[1:]
823 f = f[1:]
821 return filelog.filelog(self.svfs, f)
824 return filelog.filelog(self.svfs, f)
822
825
823 def changectx(self, changeid):
826 def changectx(self, changeid):
824 return self[changeid]
827 return self[changeid]
825
828
826 def parents(self, changeid=None):
829 def parents(self, changeid=None):
827 '''get list of changectxs for parents of changeid'''
830 '''get list of changectxs for parents of changeid'''
828 return self[changeid].parents()
831 return self[changeid].parents()
829
832
830 def setparents(self, p1, p2=nullid):
833 def setparents(self, p1, p2=nullid):
831 self.dirstate.beginparentchange()
834 self.dirstate.beginparentchange()
832 copies = self.dirstate.setparents(p1, p2)
835 copies = self.dirstate.setparents(p1, p2)
833 pctx = self[p1]
836 pctx = self[p1]
834 if copies:
837 if copies:
835 # Adjust copy records, the dirstate cannot do it, it
838 # Adjust copy records, the dirstate cannot do it, it
836 # requires access to parents manifests. Preserve them
839 # requires access to parents manifests. Preserve them
837 # only for entries added to first parent.
840 # only for entries added to first parent.
838 for f in copies:
841 for f in copies:
839 if f not in pctx and copies[f] in pctx:
842 if f not in pctx and copies[f] in pctx:
840 self.dirstate.copy(copies[f], f)
843 self.dirstate.copy(copies[f], f)
841 if p2 == nullid:
844 if p2 == nullid:
842 for f, s in sorted(self.dirstate.copies().items()):
845 for f, s in sorted(self.dirstate.copies().items()):
843 if f not in pctx and s not in pctx:
846 if f not in pctx and s not in pctx:
844 self.dirstate.copy(None, f)
847 self.dirstate.copy(None, f)
845 self.dirstate.endparentchange()
848 self.dirstate.endparentchange()
846
849
847 def filectx(self, path, changeid=None, fileid=None):
850 def filectx(self, path, changeid=None, fileid=None):
848 """changeid can be a changeset revision, node, or tag.
851 """changeid can be a changeset revision, node, or tag.
849 fileid can be a file revision or node."""
852 fileid can be a file revision or node."""
850 return context.filectx(self, path, changeid, fileid)
853 return context.filectx(self, path, changeid, fileid)
851
854
852 def getcwd(self):
855 def getcwd(self):
853 return self.dirstate.getcwd()
856 return self.dirstate.getcwd()
854
857
855 def pathto(self, f, cwd=None):
858 def pathto(self, f, cwd=None):
856 return self.dirstate.pathto(f, cwd)
859 return self.dirstate.pathto(f, cwd)
857
860
858 def wfile(self, f, mode='r'):
861 def wfile(self, f, mode='r'):
859 return self.wvfs(f, mode)
862 return self.wvfs(f, mode)
860
863
861 def _link(self, f):
864 def _link(self, f):
862 return self.wvfs.islink(f)
865 return self.wvfs.islink(f)
863
866
864 def _loadfilter(self, filter):
867 def _loadfilter(self, filter):
865 if filter not in self.filterpats:
868 if filter not in self.filterpats:
866 l = []
869 l = []
867 for pat, cmd in self.ui.configitems(filter):
870 for pat, cmd in self.ui.configitems(filter):
868 if cmd == '!':
871 if cmd == '!':
869 continue
872 continue
870 mf = matchmod.match(self.root, '', [pat])
873 mf = matchmod.match(self.root, '', [pat])
871 fn = None
874 fn = None
872 params = cmd
875 params = cmd
873 for name, filterfn in self._datafilters.iteritems():
876 for name, filterfn in self._datafilters.iteritems():
874 if cmd.startswith(name):
877 if cmd.startswith(name):
875 fn = filterfn
878 fn = filterfn
876 params = cmd[len(name):].lstrip()
879 params = cmd[len(name):].lstrip()
877 break
880 break
878 if not fn:
881 if not fn:
879 fn = lambda s, c, **kwargs: util.filter(s, c)
882 fn = lambda s, c, **kwargs: util.filter(s, c)
880 # Wrap old filters not supporting keyword arguments
883 # Wrap old filters not supporting keyword arguments
881 if not inspect.getargspec(fn)[2]:
884 if not inspect.getargspec(fn)[2]:
882 oldfn = fn
885 oldfn = fn
883 fn = lambda s, c, **kwargs: oldfn(s, c)
886 fn = lambda s, c, **kwargs: oldfn(s, c)
884 l.append((mf, fn, params))
887 l.append((mf, fn, params))
885 self.filterpats[filter] = l
888 self.filterpats[filter] = l
886 return self.filterpats[filter]
889 return self.filterpats[filter]
887
890
888 def _filter(self, filterpats, filename, data):
891 def _filter(self, filterpats, filename, data):
889 for mf, fn, cmd in filterpats:
892 for mf, fn, cmd in filterpats:
890 if mf(filename):
893 if mf(filename):
891 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
894 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
892 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
895 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
893 break
896 break
894
897
895 return data
898 return data
896
899
897 @unfilteredpropertycache
900 @unfilteredpropertycache
898 def _encodefilterpats(self):
901 def _encodefilterpats(self):
899 return self._loadfilter('encode')
902 return self._loadfilter('encode')
900
903
901 @unfilteredpropertycache
904 @unfilteredpropertycache
902 def _decodefilterpats(self):
905 def _decodefilterpats(self):
903 return self._loadfilter('decode')
906 return self._loadfilter('decode')
904
907
905 def adddatafilter(self, name, filter):
908 def adddatafilter(self, name, filter):
906 self._datafilters[name] = filter
909 self._datafilters[name] = filter
907
910
908 def wread(self, filename):
911 def wread(self, filename):
909 if self._link(filename):
912 if self._link(filename):
910 data = self.wvfs.readlink(filename)
913 data = self.wvfs.readlink(filename)
911 else:
914 else:
912 data = self.wvfs.read(filename)
915 data = self.wvfs.read(filename)
913 return self._filter(self._encodefilterpats, filename, data)
916 return self._filter(self._encodefilterpats, filename, data)
914
917
915 def wwrite(self, filename, data, flags):
918 def wwrite(self, filename, data, flags):
916 """write ``data`` into ``filename`` in the working directory
919 """write ``data`` into ``filename`` in the working directory
917
920
918 This returns length of written (maybe decoded) data.
921 This returns length of written (maybe decoded) data.
919 """
922 """
920 data = self._filter(self._decodefilterpats, filename, data)
923 data = self._filter(self._decodefilterpats, filename, data)
921 if 'l' in flags:
924 if 'l' in flags:
922 self.wvfs.symlink(data, filename)
925 self.wvfs.symlink(data, filename)
923 else:
926 else:
924 self.wvfs.write(filename, data)
927 self.wvfs.write(filename, data)
925 if 'x' in flags:
928 if 'x' in flags:
926 self.wvfs.setflags(filename, False, True)
929 self.wvfs.setflags(filename, False, True)
927 return len(data)
930 return len(data)
928
931
929 def wwritedata(self, filename, data):
932 def wwritedata(self, filename, data):
930 return self._filter(self._decodefilterpats, filename, data)
933 return self._filter(self._decodefilterpats, filename, data)
931
934
932 def currenttransaction(self):
935 def currenttransaction(self):
933 """return the current transaction or None if non exists"""
936 """return the current transaction or None if non exists"""
934 if self._transref:
937 if self._transref:
935 tr = self._transref()
938 tr = self._transref()
936 else:
939 else:
937 tr = None
940 tr = None
938
941
939 if tr and tr.running():
942 if tr and tr.running():
940 return tr
943 return tr
941 return None
944 return None
942
945
943 def transaction(self, desc, report=None):
946 def transaction(self, desc, report=None):
944 if (self.ui.configbool('devel', 'all')
947 if (self.ui.configbool('devel', 'all')
945 or self.ui.configbool('devel', 'check-locks')):
948 or self.ui.configbool('devel', 'check-locks')):
946 l = self._lockref and self._lockref()
949 l = self._lockref and self._lockref()
947 if l is None or not l.held:
950 if l is None or not l.held:
948 scmutil.develwarn(self.ui, 'transaction with no lock')
951 scmutil.develwarn(self.ui, 'transaction with no lock')
949 tr = self.currenttransaction()
952 tr = self.currenttransaction()
950 if tr is not None:
953 if tr is not None:
951 return tr.nest()
954 return tr.nest()
952
955
953 # abort here if the journal already exists
956 # abort here if the journal already exists
954 if self.svfs.exists("journal"):
957 if self.svfs.exists("journal"):
955 raise error.RepoError(
958 raise error.RepoError(
956 _("abandoned transaction found"),
959 _("abandoned transaction found"),
957 hint=_("run 'hg recover' to clean up transaction"))
960 hint=_("run 'hg recover' to clean up transaction"))
958
961
959 self.hook('pretxnopen', throw=True, txnname=desc)
962 self.hook('pretxnopen', throw=True, txnname=desc)
960
963
961 self._writejournal(desc)
964 self._writejournal(desc)
962 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
965 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
963 if report:
966 if report:
964 rp = report
967 rp = report
965 else:
968 else:
966 rp = self.ui.warn
969 rp = self.ui.warn
967 vfsmap = {'plain': self.vfs} # root of .hg/
970 vfsmap = {'plain': self.vfs} # root of .hg/
968 # we must avoid cyclic reference between repo and transaction.
971 # we must avoid cyclic reference between repo and transaction.
969 reporef = weakref.ref(self)
972 reporef = weakref.ref(self)
970 def validate(tr):
973 def validate(tr):
971 """will run pre-closing hooks"""
974 """will run pre-closing hooks"""
972 pending = lambda: tr.writepending() and self.root or ""
975 pending = lambda: tr.writepending() and self.root or ""
973 reporef().hook('pretxnclose', throw=True, pending=pending,
976 reporef().hook('pretxnclose', throw=True, pending=pending,
974 xnname=desc, **tr.hookargs)
977 xnname=desc, **tr.hookargs)
975
978
976 tr = transaction.transaction(rp, self.sopener, vfsmap,
979 tr = transaction.transaction(rp, self.sopener, vfsmap,
977 "journal",
980 "journal",
978 "undo",
981 "undo",
979 aftertrans(renames),
982 aftertrans(renames),
980 self.store.createmode,
983 self.store.createmode,
981 validator=validate)
984 validator=validate)
982
985
983 trid = 'TXN:' + util.sha1("%s#%f" % (id(tr), time.time())).hexdigest()
986 trid = 'TXN:' + util.sha1("%s#%f" % (id(tr), time.time())).hexdigest()
984 tr.hookargs['TXNID'] = trid
987 tr.hookargs['TXNID'] = trid
985 # note: writing the fncache only during finalize mean that the file is
988 # note: writing the fncache only during finalize mean that the file is
986 # outdated when running hooks. As fncache is used for streaming clone,
989 # outdated when running hooks. As fncache is used for streaming clone,
987 # this is not expected to break anything that happen during the hooks.
990 # this is not expected to break anything that happen during the hooks.
988 tr.addfinalize('flush-fncache', self.store.write)
991 tr.addfinalize('flush-fncache', self.store.write)
989 def txnclosehook(tr2):
992 def txnclosehook(tr2):
990 """To be run if transaction is successful, will schedule a hook run
993 """To be run if transaction is successful, will schedule a hook run
991 """
994 """
992 def hook():
995 def hook():
993 reporef().hook('txnclose', throw=False, txnname=desc,
996 reporef().hook('txnclose', throw=False, txnname=desc,
994 **tr2.hookargs)
997 **tr2.hookargs)
995 reporef()._afterlock(hook)
998 reporef()._afterlock(hook)
996 tr.addfinalize('txnclose-hook', txnclosehook)
999 tr.addfinalize('txnclose-hook', txnclosehook)
997 def txnaborthook(tr2):
1000 def txnaborthook(tr2):
998 """To be run if transaction is aborted
1001 """To be run if transaction is aborted
999 """
1002 """
1000 reporef().hook('txnabort', throw=False, txnname=desc,
1003 reporef().hook('txnabort', throw=False, txnname=desc,
1001 **tr2.hookargs)
1004 **tr2.hookargs)
1002 tr.addabort('txnabort-hook', txnaborthook)
1005 tr.addabort('txnabort-hook', txnaborthook)
1003 self._transref = weakref.ref(tr)
1006 self._transref = weakref.ref(tr)
1004 return tr
1007 return tr
1005
1008
1006 def _journalfiles(self):
1009 def _journalfiles(self):
1007 return ((self.svfs, 'journal'),
1010 return ((self.svfs, 'journal'),
1008 (self.vfs, 'journal.dirstate'),
1011 (self.vfs, 'journal.dirstate'),
1009 (self.vfs, 'journal.branch'),
1012 (self.vfs, 'journal.branch'),
1010 (self.vfs, 'journal.desc'),
1013 (self.vfs, 'journal.desc'),
1011 (self.vfs, 'journal.bookmarks'),
1014 (self.vfs, 'journal.bookmarks'),
1012 (self.svfs, 'journal.phaseroots'))
1015 (self.svfs, 'journal.phaseroots'))
1013
1016
1014 def undofiles(self):
1017 def undofiles(self):
1015 return [(vfs, undoname(x)) for vfs, x in self._journalfiles()]
1018 return [(vfs, undoname(x)) for vfs, x in self._journalfiles()]
1016
1019
1017 def _writejournal(self, desc):
1020 def _writejournal(self, desc):
1018 self.vfs.write("journal.dirstate",
1021 self.vfs.write("journal.dirstate",
1019 self.vfs.tryread("dirstate"))
1022 self.vfs.tryread("dirstate"))
1020 self.vfs.write("journal.branch",
1023 self.vfs.write("journal.branch",
1021 encoding.fromlocal(self.dirstate.branch()))
1024 encoding.fromlocal(self.dirstate.branch()))
1022 self.vfs.write("journal.desc",
1025 self.vfs.write("journal.desc",
1023 "%d\n%s\n" % (len(self), desc))
1026 "%d\n%s\n" % (len(self), desc))
1024 self.vfs.write("journal.bookmarks",
1027 self.vfs.write("journal.bookmarks",
1025 self.vfs.tryread("bookmarks"))
1028 self.vfs.tryread("bookmarks"))
1026 self.svfs.write("journal.phaseroots",
1029 self.svfs.write("journal.phaseroots",
1027 self.svfs.tryread("phaseroots"))
1030 self.svfs.tryread("phaseroots"))
1028
1031
1029 def recover(self):
1032 def recover(self):
1030 lock = self.lock()
1033 lock = self.lock()
1031 try:
1034 try:
1032 if self.svfs.exists("journal"):
1035 if self.svfs.exists("journal"):
1033 self.ui.status(_("rolling back interrupted transaction\n"))
1036 self.ui.status(_("rolling back interrupted transaction\n"))
1034 vfsmap = {'': self.svfs,
1037 vfsmap = {'': self.svfs,
1035 'plain': self.vfs,}
1038 'plain': self.vfs,}
1036 transaction.rollback(self.svfs, vfsmap, "journal",
1039 transaction.rollback(self.svfs, vfsmap, "journal",
1037 self.ui.warn)
1040 self.ui.warn)
1038 self.invalidate()
1041 self.invalidate()
1039 return True
1042 return True
1040 else:
1043 else:
1041 self.ui.warn(_("no interrupted transaction available\n"))
1044 self.ui.warn(_("no interrupted transaction available\n"))
1042 return False
1045 return False
1043 finally:
1046 finally:
1044 lock.release()
1047 lock.release()
1045
1048
1046 def rollback(self, dryrun=False, force=False):
1049 def rollback(self, dryrun=False, force=False):
1047 wlock = lock = None
1050 wlock = lock = None
1048 try:
1051 try:
1049 wlock = self.wlock()
1052 wlock = self.wlock()
1050 lock = self.lock()
1053 lock = self.lock()
1051 if self.svfs.exists("undo"):
1054 if self.svfs.exists("undo"):
1052 return self._rollback(dryrun, force)
1055 return self._rollback(dryrun, force)
1053 else:
1056 else:
1054 self.ui.warn(_("no rollback information available\n"))
1057 self.ui.warn(_("no rollback information available\n"))
1055 return 1
1058 return 1
1056 finally:
1059 finally:
1057 release(lock, wlock)
1060 release(lock, wlock)
1058
1061
1059 @unfilteredmethod # Until we get smarter cache management
1062 @unfilteredmethod # Until we get smarter cache management
1060 def _rollback(self, dryrun, force):
1063 def _rollback(self, dryrun, force):
1061 ui = self.ui
1064 ui = self.ui
1062 try:
1065 try:
1063 args = self.vfs.read('undo.desc').splitlines()
1066 args = self.vfs.read('undo.desc').splitlines()
1064 (oldlen, desc, detail) = (int(args[0]), args[1], None)
1067 (oldlen, desc, detail) = (int(args[0]), args[1], None)
1065 if len(args) >= 3:
1068 if len(args) >= 3:
1066 detail = args[2]
1069 detail = args[2]
1067 oldtip = oldlen - 1
1070 oldtip = oldlen - 1
1068
1071
1069 if detail and ui.verbose:
1072 if detail and ui.verbose:
1070 msg = (_('repository tip rolled back to revision %s'
1073 msg = (_('repository tip rolled back to revision %s'
1071 ' (undo %s: %s)\n')
1074 ' (undo %s: %s)\n')
1072 % (oldtip, desc, detail))
1075 % (oldtip, desc, detail))
1073 else:
1076 else:
1074 msg = (_('repository tip rolled back to revision %s'
1077 msg = (_('repository tip rolled back to revision %s'
1075 ' (undo %s)\n')
1078 ' (undo %s)\n')
1076 % (oldtip, desc))
1079 % (oldtip, desc))
1077 except IOError:
1080 except IOError:
1078 msg = _('rolling back unknown transaction\n')
1081 msg = _('rolling back unknown transaction\n')
1079 desc = None
1082 desc = None
1080
1083
1081 if not force and self['.'] != self['tip'] and desc == 'commit':
1084 if not force and self['.'] != self['tip'] and desc == 'commit':
1082 raise util.Abort(
1085 raise util.Abort(
1083 _('rollback of last commit while not checked out '
1086 _('rollback of last commit while not checked out '
1084 'may lose data'), hint=_('use -f to force'))
1087 'may lose data'), hint=_('use -f to force'))
1085
1088
1086 ui.status(msg)
1089 ui.status(msg)
1087 if dryrun:
1090 if dryrun:
1088 return 0
1091 return 0
1089
1092
1090 parents = self.dirstate.parents()
1093 parents = self.dirstate.parents()
1091 self.destroying()
1094 self.destroying()
1092 vfsmap = {'plain': self.vfs, '': self.svfs}
1095 vfsmap = {'plain': self.vfs, '': self.svfs}
1093 transaction.rollback(self.svfs, vfsmap, 'undo', ui.warn)
1096 transaction.rollback(self.svfs, vfsmap, 'undo', ui.warn)
1094 if self.vfs.exists('undo.bookmarks'):
1097 if self.vfs.exists('undo.bookmarks'):
1095 self.vfs.rename('undo.bookmarks', 'bookmarks')
1098 self.vfs.rename('undo.bookmarks', 'bookmarks')
1096 if self.svfs.exists('undo.phaseroots'):
1099 if self.svfs.exists('undo.phaseroots'):
1097 self.svfs.rename('undo.phaseroots', 'phaseroots')
1100 self.svfs.rename('undo.phaseroots', 'phaseroots')
1098 self.invalidate()
1101 self.invalidate()
1099
1102
1100 parentgone = (parents[0] not in self.changelog.nodemap or
1103 parentgone = (parents[0] not in self.changelog.nodemap or
1101 parents[1] not in self.changelog.nodemap)
1104 parents[1] not in self.changelog.nodemap)
1102 if parentgone:
1105 if parentgone:
1103 self.vfs.rename('undo.dirstate', 'dirstate')
1106 self.vfs.rename('undo.dirstate', 'dirstate')
1104 try:
1107 try:
1105 branch = self.vfs.read('undo.branch')
1108 branch = self.vfs.read('undo.branch')
1106 self.dirstate.setbranch(encoding.tolocal(branch))
1109 self.dirstate.setbranch(encoding.tolocal(branch))
1107 except IOError:
1110 except IOError:
1108 ui.warn(_('named branch could not be reset: '
1111 ui.warn(_('named branch could not be reset: '
1109 'current branch is still \'%s\'\n')
1112 'current branch is still \'%s\'\n')
1110 % self.dirstate.branch())
1113 % self.dirstate.branch())
1111
1114
1112 self.dirstate.invalidate()
1115 self.dirstate.invalidate()
1113 parents = tuple([p.rev() for p in self.parents()])
1116 parents = tuple([p.rev() for p in self.parents()])
1114 if len(parents) > 1:
1117 if len(parents) > 1:
1115 ui.status(_('working directory now based on '
1118 ui.status(_('working directory now based on '
1116 'revisions %d and %d\n') % parents)
1119 'revisions %d and %d\n') % parents)
1117 else:
1120 else:
1118 ui.status(_('working directory now based on '
1121 ui.status(_('working directory now based on '
1119 'revision %d\n') % parents)
1122 'revision %d\n') % parents)
1120 ms = mergemod.mergestate(self)
1123 ms = mergemod.mergestate(self)
1121 ms.reset(self['.'].node())
1124 ms.reset(self['.'].node())
1122
1125
1123 # TODO: if we know which new heads may result from this rollback, pass
1126 # TODO: if we know which new heads may result from this rollback, pass
1124 # them to destroy(), which will prevent the branchhead cache from being
1127 # them to destroy(), which will prevent the branchhead cache from being
1125 # invalidated.
1128 # invalidated.
1126 self.destroyed()
1129 self.destroyed()
1127 return 0
1130 return 0
1128
1131
1129 def invalidatecaches(self):
1132 def invalidatecaches(self):
1130
1133
1131 if '_tagscache' in vars(self):
1134 if '_tagscache' in vars(self):
1132 # can't use delattr on proxy
1135 # can't use delattr on proxy
1133 del self.__dict__['_tagscache']
1136 del self.__dict__['_tagscache']
1134
1137
1135 self.unfiltered()._branchcaches.clear()
1138 self.unfiltered()._branchcaches.clear()
1136 self.invalidatevolatilesets()
1139 self.invalidatevolatilesets()
1137
1140
1138 def invalidatevolatilesets(self):
1141 def invalidatevolatilesets(self):
1139 self.filteredrevcache.clear()
1142 self.filteredrevcache.clear()
1140 obsolete.clearobscaches(self)
1143 obsolete.clearobscaches(self)
1141
1144
1142 def invalidatedirstate(self):
1145 def invalidatedirstate(self):
1143 '''Invalidates the dirstate, causing the next call to dirstate
1146 '''Invalidates the dirstate, causing the next call to dirstate
1144 to check if it was modified since the last time it was read,
1147 to check if it was modified since the last time it was read,
1145 rereading it if it has.
1148 rereading it if it has.
1146
1149
1147 This is different to dirstate.invalidate() that it doesn't always
1150 This is different to dirstate.invalidate() that it doesn't always
1148 rereads the dirstate. Use dirstate.invalidate() if you want to
1151 rereads the dirstate. Use dirstate.invalidate() if you want to
1149 explicitly read the dirstate again (i.e. restoring it to a previous
1152 explicitly read the dirstate again (i.e. restoring it to a previous
1150 known good state).'''
1153 known good state).'''
1151 if hasunfilteredcache(self, 'dirstate'):
1154 if hasunfilteredcache(self, 'dirstate'):
1152 for k in self.dirstate._filecache:
1155 for k in self.dirstate._filecache:
1153 try:
1156 try:
1154 delattr(self.dirstate, k)
1157 delattr(self.dirstate, k)
1155 except AttributeError:
1158 except AttributeError:
1156 pass
1159 pass
1157 delattr(self.unfiltered(), 'dirstate')
1160 delattr(self.unfiltered(), 'dirstate')
1158
1161
1159 def invalidate(self):
1162 def invalidate(self):
1160 unfiltered = self.unfiltered() # all file caches are stored unfiltered
1163 unfiltered = self.unfiltered() # all file caches are stored unfiltered
1161 for k in self._filecache:
1164 for k in self._filecache:
1162 # dirstate is invalidated separately in invalidatedirstate()
1165 # dirstate is invalidated separately in invalidatedirstate()
1163 if k == 'dirstate':
1166 if k == 'dirstate':
1164 continue
1167 continue
1165
1168
1166 try:
1169 try:
1167 delattr(unfiltered, k)
1170 delattr(unfiltered, k)
1168 except AttributeError:
1171 except AttributeError:
1169 pass
1172 pass
1170 self.invalidatecaches()
1173 self.invalidatecaches()
1171 self.store.invalidatecaches()
1174 self.store.invalidatecaches()
1172
1175
1173 def invalidateall(self):
1176 def invalidateall(self):
1174 '''Fully invalidates both store and non-store parts, causing the
1177 '''Fully invalidates both store and non-store parts, causing the
1175 subsequent operation to reread any outside changes.'''
1178 subsequent operation to reread any outside changes.'''
1176 # extension should hook this to invalidate its caches
1179 # extension should hook this to invalidate its caches
1177 self.invalidate()
1180 self.invalidate()
1178 self.invalidatedirstate()
1181 self.invalidatedirstate()
1179
1182
1180 def _lock(self, vfs, lockname, wait, releasefn, acquirefn, desc):
1183 def _lock(self, vfs, lockname, wait, releasefn, acquirefn, desc):
1181 try:
1184 try:
1182 l = lockmod.lock(vfs, lockname, 0, releasefn, desc=desc)
1185 l = lockmod.lock(vfs, lockname, 0, releasefn, desc=desc)
1183 except error.LockHeld, inst:
1186 except error.LockHeld, inst:
1184 if not wait:
1187 if not wait:
1185 raise
1188 raise
1186 self.ui.warn(_("waiting for lock on %s held by %r\n") %
1189 self.ui.warn(_("waiting for lock on %s held by %r\n") %
1187 (desc, inst.locker))
1190 (desc, inst.locker))
1188 # default to 600 seconds timeout
1191 # default to 600 seconds timeout
1189 l = lockmod.lock(vfs, lockname,
1192 l = lockmod.lock(vfs, lockname,
1190 int(self.ui.config("ui", "timeout", "600")),
1193 int(self.ui.config("ui", "timeout", "600")),
1191 releasefn, desc=desc)
1194 releasefn, desc=desc)
1192 self.ui.warn(_("got lock after %s seconds\n") % l.delay)
1195 self.ui.warn(_("got lock after %s seconds\n") % l.delay)
1193 if acquirefn:
1196 if acquirefn:
1194 acquirefn()
1197 acquirefn()
1195 return l
1198 return l
1196
1199
1197 def _afterlock(self, callback):
1200 def _afterlock(self, callback):
1198 """add a callback to be run when the repository is fully unlocked
1201 """add a callback to be run when the repository is fully unlocked
1199
1202
1200 The callback will be executed when the outermost lock is released
1203 The callback will be executed when the outermost lock is released
1201 (with wlock being higher level than 'lock')."""
1204 (with wlock being higher level than 'lock')."""
1202 for ref in (self._wlockref, self._lockref):
1205 for ref in (self._wlockref, self._lockref):
1203 l = ref and ref()
1206 l = ref and ref()
1204 if l and l.held:
1207 if l and l.held:
1205 l.postrelease.append(callback)
1208 l.postrelease.append(callback)
1206 break
1209 break
1207 else: # no lock have been found.
1210 else: # no lock have been found.
1208 callback()
1211 callback()
1209
1212
1210 def lock(self, wait=True):
1213 def lock(self, wait=True):
1211 '''Lock the repository store (.hg/store) and return a weak reference
1214 '''Lock the repository store (.hg/store) and return a weak reference
1212 to the lock. Use this before modifying the store (e.g. committing or
1215 to the lock. Use this before modifying the store (e.g. committing or
1213 stripping). If you are opening a transaction, get a lock as well.)
1216 stripping). If you are opening a transaction, get a lock as well.)
1214
1217
1215 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
1218 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
1216 'wlock' first to avoid a dead-lock hazard.'''
1219 'wlock' first to avoid a dead-lock hazard.'''
1217 l = self._lockref and self._lockref()
1220 l = self._lockref and self._lockref()
1218 if l is not None and l.held:
1221 if l is not None and l.held:
1219 l.lock()
1222 l.lock()
1220 return l
1223 return l
1221
1224
1222 def unlock():
1225 def unlock():
1223 for k, ce in self._filecache.items():
1226 for k, ce in self._filecache.items():
1224 if k == 'dirstate' or k not in self.__dict__:
1227 if k == 'dirstate' or k not in self.__dict__:
1225 continue
1228 continue
1226 ce.refresh()
1229 ce.refresh()
1227
1230
1228 l = self._lock(self.svfs, "lock", wait, unlock,
1231 l = self._lock(self.svfs, "lock", wait, unlock,
1229 self.invalidate, _('repository %s') % self.origroot)
1232 self.invalidate, _('repository %s') % self.origroot)
1230 self._lockref = weakref.ref(l)
1233 self._lockref = weakref.ref(l)
1231 return l
1234 return l
1232
1235
1233 def wlock(self, wait=True):
1236 def wlock(self, wait=True):
1234 '''Lock the non-store parts of the repository (everything under
1237 '''Lock the non-store parts of the repository (everything under
1235 .hg except .hg/store) and return a weak reference to the lock.
1238 .hg except .hg/store) and return a weak reference to the lock.
1236
1239
1237 Use this before modifying files in .hg.
1240 Use this before modifying files in .hg.
1238
1241
1239 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
1242 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
1240 'wlock' first to avoid a dead-lock hazard.'''
1243 'wlock' first to avoid a dead-lock hazard.'''
1241 l = self._wlockref and self._wlockref()
1244 l = self._wlockref and self._wlockref()
1242 if l is not None and l.held:
1245 if l is not None and l.held:
1243 l.lock()
1246 l.lock()
1244 return l
1247 return l
1245
1248
1246 # We do not need to check for non-waiting lock aquisition. Such
1249 # We do not need to check for non-waiting lock aquisition. Such
1247 # acquisition would not cause dead-lock as they would just fail.
1250 # acquisition would not cause dead-lock as they would just fail.
1248 if wait and (self.ui.configbool('devel', 'all')
1251 if wait and (self.ui.configbool('devel', 'all')
1249 or self.ui.configbool('devel', 'check-locks')):
1252 or self.ui.configbool('devel', 'check-locks')):
1250 l = self._lockref and self._lockref()
1253 l = self._lockref and self._lockref()
1251 if l is not None and l.held:
1254 if l is not None and l.held:
1252 scmutil.develwarn(self.ui, '"wlock" acquired after "lock"')
1255 scmutil.develwarn(self.ui, '"wlock" acquired after "lock"')
1253
1256
1254 def unlock():
1257 def unlock():
1255 if self.dirstate.pendingparentchange():
1258 if self.dirstate.pendingparentchange():
1256 self.dirstate.invalidate()
1259 self.dirstate.invalidate()
1257 else:
1260 else:
1258 self.dirstate.write()
1261 self.dirstate.write()
1259
1262
1260 self._filecache['dirstate'].refresh()
1263 self._filecache['dirstate'].refresh()
1261
1264
1262 l = self._lock(self.vfs, "wlock", wait, unlock,
1265 l = self._lock(self.vfs, "wlock", wait, unlock,
1263 self.invalidatedirstate, _('working directory of %s') %
1266 self.invalidatedirstate, _('working directory of %s') %
1264 self.origroot)
1267 self.origroot)
1265 self._wlockref = weakref.ref(l)
1268 self._wlockref = weakref.ref(l)
1266 return l
1269 return l
1267
1270
1268 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1271 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1269 """
1272 """
1270 commit an individual file as part of a larger transaction
1273 commit an individual file as part of a larger transaction
1271 """
1274 """
1272
1275
1273 fname = fctx.path()
1276 fname = fctx.path()
1274 fparent1 = manifest1.get(fname, nullid)
1277 fparent1 = manifest1.get(fname, nullid)
1275 fparent2 = manifest2.get(fname, nullid)
1278 fparent2 = manifest2.get(fname, nullid)
1276 if isinstance(fctx, context.filectx):
1279 if isinstance(fctx, context.filectx):
1277 node = fctx.filenode()
1280 node = fctx.filenode()
1278 if node in [fparent1, fparent2]:
1281 if node in [fparent1, fparent2]:
1279 self.ui.debug('reusing %s filelog entry\n' % fname)
1282 self.ui.debug('reusing %s filelog entry\n' % fname)
1280 return node
1283 return node
1281
1284
1282 flog = self.file(fname)
1285 flog = self.file(fname)
1283 meta = {}
1286 meta = {}
1284 copy = fctx.renamed()
1287 copy = fctx.renamed()
1285 if copy and copy[0] != fname:
1288 if copy and copy[0] != fname:
1286 # Mark the new revision of this file as a copy of another
1289 # Mark the new revision of this file as a copy of another
1287 # file. This copy data will effectively act as a parent
1290 # file. This copy data will effectively act as a parent
1288 # of this new revision. If this is a merge, the first
1291 # of this new revision. If this is a merge, the first
1289 # parent will be the nullid (meaning "look up the copy data")
1292 # parent will be the nullid (meaning "look up the copy data")
1290 # and the second one will be the other parent. For example:
1293 # and the second one will be the other parent. For example:
1291 #
1294 #
1292 # 0 --- 1 --- 3 rev1 changes file foo
1295 # 0 --- 1 --- 3 rev1 changes file foo
1293 # \ / rev2 renames foo to bar and changes it
1296 # \ / rev2 renames foo to bar and changes it
1294 # \- 2 -/ rev3 should have bar with all changes and
1297 # \- 2 -/ rev3 should have bar with all changes and
1295 # should record that bar descends from
1298 # should record that bar descends from
1296 # bar in rev2 and foo in rev1
1299 # bar in rev2 and foo in rev1
1297 #
1300 #
1298 # this allows this merge to succeed:
1301 # this allows this merge to succeed:
1299 #
1302 #
1300 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1303 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1301 # \ / merging rev3 and rev4 should use bar@rev2
1304 # \ / merging rev3 and rev4 should use bar@rev2
1302 # \- 2 --- 4 as the merge base
1305 # \- 2 --- 4 as the merge base
1303 #
1306 #
1304
1307
1305 cfname = copy[0]
1308 cfname = copy[0]
1306 crev = manifest1.get(cfname)
1309 crev = manifest1.get(cfname)
1307 newfparent = fparent2
1310 newfparent = fparent2
1308
1311
1309 if manifest2: # branch merge
1312 if manifest2: # branch merge
1310 if fparent2 == nullid or crev is None: # copied on remote side
1313 if fparent2 == nullid or crev is None: # copied on remote side
1311 if cfname in manifest2:
1314 if cfname in manifest2:
1312 crev = manifest2[cfname]
1315 crev = manifest2[cfname]
1313 newfparent = fparent1
1316 newfparent = fparent1
1314
1317
1315 # Here, we used to search backwards through history to try to find
1318 # Here, we used to search backwards through history to try to find
1316 # where the file copy came from if the source of a copy was not in
1319 # where the file copy came from if the source of a copy was not in
1317 # the parent directory. However, this doesn't actually make sense to
1320 # the parent directory. However, this doesn't actually make sense to
1318 # do (what does a copy from something not in your working copy even
1321 # do (what does a copy from something not in your working copy even
1319 # mean?) and it causes bugs (eg, issue4476). Instead, we will warn
1322 # mean?) and it causes bugs (eg, issue4476). Instead, we will warn
1320 # the user that copy information was dropped, so if they didn't
1323 # the user that copy information was dropped, so if they didn't
1321 # expect this outcome it can be fixed, but this is the correct
1324 # expect this outcome it can be fixed, but this is the correct
1322 # behavior in this circumstance.
1325 # behavior in this circumstance.
1323
1326
1324 if crev:
1327 if crev:
1325 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1328 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1326 meta["copy"] = cfname
1329 meta["copy"] = cfname
1327 meta["copyrev"] = hex(crev)
1330 meta["copyrev"] = hex(crev)
1328 fparent1, fparent2 = nullid, newfparent
1331 fparent1, fparent2 = nullid, newfparent
1329 else:
1332 else:
1330 self.ui.warn(_("warning: can't find ancestor for '%s' "
1333 self.ui.warn(_("warning: can't find ancestor for '%s' "
1331 "copied from '%s'!\n") % (fname, cfname))
1334 "copied from '%s'!\n") % (fname, cfname))
1332
1335
1333 elif fparent1 == nullid:
1336 elif fparent1 == nullid:
1334 fparent1, fparent2 = fparent2, nullid
1337 fparent1, fparent2 = fparent2, nullid
1335 elif fparent2 != nullid:
1338 elif fparent2 != nullid:
1336 # is one parent an ancestor of the other?
1339 # is one parent an ancestor of the other?
1337 fparentancestors = flog.commonancestorsheads(fparent1, fparent2)
1340 fparentancestors = flog.commonancestorsheads(fparent1, fparent2)
1338 if fparent1 in fparentancestors:
1341 if fparent1 in fparentancestors:
1339 fparent1, fparent2 = fparent2, nullid
1342 fparent1, fparent2 = fparent2, nullid
1340 elif fparent2 in fparentancestors:
1343 elif fparent2 in fparentancestors:
1341 fparent2 = nullid
1344 fparent2 = nullid
1342
1345
1343 # is the file changed?
1346 # is the file changed?
1344 text = fctx.data()
1347 text = fctx.data()
1345 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1348 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1346 changelist.append(fname)
1349 changelist.append(fname)
1347 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1350 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1348 # are just the flags changed during merge?
1351 # are just the flags changed during merge?
1349 elif fname in manifest1 and manifest1.flags(fname) != fctx.flags():
1352 elif fname in manifest1 and manifest1.flags(fname) != fctx.flags():
1350 changelist.append(fname)
1353 changelist.append(fname)
1351
1354
1352 return fparent1
1355 return fparent1
1353
1356
1354 @unfilteredmethod
1357 @unfilteredmethod
1355 def commit(self, text="", user=None, date=None, match=None, force=False,
1358 def commit(self, text="", user=None, date=None, match=None, force=False,
1356 editor=False, extra={}):
1359 editor=False, extra={}):
1357 """Add a new revision to current repository.
1360 """Add a new revision to current repository.
1358
1361
1359 Revision information is gathered from the working directory,
1362 Revision information is gathered from the working directory,
1360 match can be used to filter the committed files. If editor is
1363 match can be used to filter the committed files. If editor is
1361 supplied, it is called to get a commit message.
1364 supplied, it is called to get a commit message.
1362 """
1365 """
1363
1366
1364 def fail(f, msg):
1367 def fail(f, msg):
1365 raise util.Abort('%s: %s' % (f, msg))
1368 raise util.Abort('%s: %s' % (f, msg))
1366
1369
1367 if not match:
1370 if not match:
1368 match = matchmod.always(self.root, '')
1371 match = matchmod.always(self.root, '')
1369
1372
1370 if not force:
1373 if not force:
1371 vdirs = []
1374 vdirs = []
1372 match.explicitdir = vdirs.append
1375 match.explicitdir = vdirs.append
1373 match.bad = fail
1376 match.bad = fail
1374
1377
1375 wlock = self.wlock()
1378 wlock = self.wlock()
1376 try:
1379 try:
1377 wctx = self[None]
1380 wctx = self[None]
1378 merge = len(wctx.parents()) > 1
1381 merge = len(wctx.parents()) > 1
1379
1382
1380 if not force and merge and match.ispartial():
1383 if not force and merge and match.ispartial():
1381 raise util.Abort(_('cannot partially commit a merge '
1384 raise util.Abort(_('cannot partially commit a merge '
1382 '(do not specify files or patterns)'))
1385 '(do not specify files or patterns)'))
1383
1386
1384 status = self.status(match=match, clean=force)
1387 status = self.status(match=match, clean=force)
1385 if force:
1388 if force:
1386 status.modified.extend(status.clean) # mq may commit clean files
1389 status.modified.extend(status.clean) # mq may commit clean files
1387
1390
1388 # check subrepos
1391 # check subrepos
1389 subs = []
1392 subs = []
1390 commitsubs = set()
1393 commitsubs = set()
1391 newstate = wctx.substate.copy()
1394 newstate = wctx.substate.copy()
1392 # only manage subrepos and .hgsubstate if .hgsub is present
1395 # only manage subrepos and .hgsubstate if .hgsub is present
1393 if '.hgsub' in wctx:
1396 if '.hgsub' in wctx:
1394 # we'll decide whether to track this ourselves, thanks
1397 # we'll decide whether to track this ourselves, thanks
1395 for c in status.modified, status.added, status.removed:
1398 for c in status.modified, status.added, status.removed:
1396 if '.hgsubstate' in c:
1399 if '.hgsubstate' in c:
1397 c.remove('.hgsubstate')
1400 c.remove('.hgsubstate')
1398
1401
1399 # compare current state to last committed state
1402 # compare current state to last committed state
1400 # build new substate based on last committed state
1403 # build new substate based on last committed state
1401 oldstate = wctx.p1().substate
1404 oldstate = wctx.p1().substate
1402 for s in sorted(newstate.keys()):
1405 for s in sorted(newstate.keys()):
1403 if not match(s):
1406 if not match(s):
1404 # ignore working copy, use old state if present
1407 # ignore working copy, use old state if present
1405 if s in oldstate:
1408 if s in oldstate:
1406 newstate[s] = oldstate[s]
1409 newstate[s] = oldstate[s]
1407 continue
1410 continue
1408 if not force:
1411 if not force:
1409 raise util.Abort(
1412 raise util.Abort(
1410 _("commit with new subrepo %s excluded") % s)
1413 _("commit with new subrepo %s excluded") % s)
1411 dirtyreason = wctx.sub(s).dirtyreason(True)
1414 dirtyreason = wctx.sub(s).dirtyreason(True)
1412 if dirtyreason:
1415 if dirtyreason:
1413 if not self.ui.configbool('ui', 'commitsubrepos'):
1416 if not self.ui.configbool('ui', 'commitsubrepos'):
1414 raise util.Abort(dirtyreason,
1417 raise util.Abort(dirtyreason,
1415 hint=_("use --subrepos for recursive commit"))
1418 hint=_("use --subrepos for recursive commit"))
1416 subs.append(s)
1419 subs.append(s)
1417 commitsubs.add(s)
1420 commitsubs.add(s)
1418 else:
1421 else:
1419 bs = wctx.sub(s).basestate()
1422 bs = wctx.sub(s).basestate()
1420 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1423 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1421 if oldstate.get(s, (None, None, None))[1] != bs:
1424 if oldstate.get(s, (None, None, None))[1] != bs:
1422 subs.append(s)
1425 subs.append(s)
1423
1426
1424 # check for removed subrepos
1427 # check for removed subrepos
1425 for p in wctx.parents():
1428 for p in wctx.parents():
1426 r = [s for s in p.substate if s not in newstate]
1429 r = [s for s in p.substate if s not in newstate]
1427 subs += [s for s in r if match(s)]
1430 subs += [s for s in r if match(s)]
1428 if subs:
1431 if subs:
1429 if (not match('.hgsub') and
1432 if (not match('.hgsub') and
1430 '.hgsub' in (wctx.modified() + wctx.added())):
1433 '.hgsub' in (wctx.modified() + wctx.added())):
1431 raise util.Abort(
1434 raise util.Abort(
1432 _("can't commit subrepos without .hgsub"))
1435 _("can't commit subrepos without .hgsub"))
1433 status.modified.insert(0, '.hgsubstate')
1436 status.modified.insert(0, '.hgsubstate')
1434
1437
1435 elif '.hgsub' in status.removed:
1438 elif '.hgsub' in status.removed:
1436 # clean up .hgsubstate when .hgsub is removed
1439 # clean up .hgsubstate when .hgsub is removed
1437 if ('.hgsubstate' in wctx and
1440 if ('.hgsubstate' in wctx and
1438 '.hgsubstate' not in (status.modified + status.added +
1441 '.hgsubstate' not in (status.modified + status.added +
1439 status.removed)):
1442 status.removed)):
1440 status.removed.insert(0, '.hgsubstate')
1443 status.removed.insert(0, '.hgsubstate')
1441
1444
1442 # make sure all explicit patterns are matched
1445 # make sure all explicit patterns are matched
1443 if not force and match.files():
1446 if not force and match.files():
1444 matched = set(status.modified + status.added + status.removed)
1447 matched = set(status.modified + status.added + status.removed)
1445
1448
1446 for f in match.files():
1449 for f in match.files():
1447 f = self.dirstate.normalize(f)
1450 f = self.dirstate.normalize(f)
1448 if f == '.' or f in matched or f in wctx.substate:
1451 if f == '.' or f in matched or f in wctx.substate:
1449 continue
1452 continue
1450 if f in status.deleted:
1453 if f in status.deleted:
1451 fail(f, _('file not found!'))
1454 fail(f, _('file not found!'))
1452 if f in vdirs: # visited directory
1455 if f in vdirs: # visited directory
1453 d = f + '/'
1456 d = f + '/'
1454 for mf in matched:
1457 for mf in matched:
1455 if mf.startswith(d):
1458 if mf.startswith(d):
1456 break
1459 break
1457 else:
1460 else:
1458 fail(f, _("no match under directory!"))
1461 fail(f, _("no match under directory!"))
1459 elif f not in self.dirstate:
1462 elif f not in self.dirstate:
1460 fail(f, _("file not tracked!"))
1463 fail(f, _("file not tracked!"))
1461
1464
1462 cctx = context.workingcommitctx(self, status,
1465 cctx = context.workingcommitctx(self, status,
1463 text, user, date, extra)
1466 text, user, date, extra)
1464
1467
1465 allowemptycommit = (wctx.branch() != wctx.p1().branch()
1468 allowemptycommit = (wctx.branch() != wctx.p1().branch()
1466 or extra.get('close') or merge or cctx.files()
1469 or extra.get('close') or merge or cctx.files()
1467 or self.ui.configbool('ui', 'allowemptycommit'))
1470 or self.ui.configbool('ui', 'allowemptycommit'))
1468 if not allowemptycommit:
1471 if not allowemptycommit:
1469 return None
1472 return None
1470
1473
1471 if merge and cctx.deleted():
1474 if merge and cctx.deleted():
1472 raise util.Abort(_("cannot commit merge with missing files"))
1475 raise util.Abort(_("cannot commit merge with missing files"))
1473
1476
1474 ms = mergemod.mergestate(self)
1477 ms = mergemod.mergestate(self)
1475 for f in status.modified:
1478 for f in status.modified:
1476 if f in ms and ms[f] == 'u':
1479 if f in ms and ms[f] == 'u':
1477 raise util.Abort(_('unresolved merge conflicts '
1480 raise util.Abort(_('unresolved merge conflicts '
1478 '(see "hg help resolve")'))
1481 '(see "hg help resolve")'))
1479
1482
1480 if editor:
1483 if editor:
1481 cctx._text = editor(self, cctx, subs)
1484 cctx._text = editor(self, cctx, subs)
1482 edited = (text != cctx._text)
1485 edited = (text != cctx._text)
1483
1486
1484 # Save commit message in case this transaction gets rolled back
1487 # Save commit message in case this transaction gets rolled back
1485 # (e.g. by a pretxncommit hook). Leave the content alone on
1488 # (e.g. by a pretxncommit hook). Leave the content alone on
1486 # the assumption that the user will use the same editor again.
1489 # the assumption that the user will use the same editor again.
1487 msgfn = self.savecommitmessage(cctx._text)
1490 msgfn = self.savecommitmessage(cctx._text)
1488
1491
1489 # commit subs and write new state
1492 # commit subs and write new state
1490 if subs:
1493 if subs:
1491 for s in sorted(commitsubs):
1494 for s in sorted(commitsubs):
1492 sub = wctx.sub(s)
1495 sub = wctx.sub(s)
1493 self.ui.status(_('committing subrepository %s\n') %
1496 self.ui.status(_('committing subrepository %s\n') %
1494 subrepo.subrelpath(sub))
1497 subrepo.subrelpath(sub))
1495 sr = sub.commit(cctx._text, user, date)
1498 sr = sub.commit(cctx._text, user, date)
1496 newstate[s] = (newstate[s][0], sr)
1499 newstate[s] = (newstate[s][0], sr)
1497 subrepo.writestate(self, newstate)
1500 subrepo.writestate(self, newstate)
1498
1501
1499 p1, p2 = self.dirstate.parents()
1502 p1, p2 = self.dirstate.parents()
1500 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1503 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1501 try:
1504 try:
1502 self.hook("precommit", throw=True, parent1=hookp1,
1505 self.hook("precommit", throw=True, parent1=hookp1,
1503 parent2=hookp2)
1506 parent2=hookp2)
1504 ret = self.commitctx(cctx, True)
1507 ret = self.commitctx(cctx, True)
1505 except: # re-raises
1508 except: # re-raises
1506 if edited:
1509 if edited:
1507 self.ui.write(
1510 self.ui.write(
1508 _('note: commit message saved in %s\n') % msgfn)
1511 _('note: commit message saved in %s\n') % msgfn)
1509 raise
1512 raise
1510
1513
1511 # update bookmarks, dirstate and mergestate
1514 # update bookmarks, dirstate and mergestate
1512 bookmarks.update(self, [p1, p2], ret)
1515 bookmarks.update(self, [p1, p2], ret)
1513 cctx.markcommitted(ret)
1516 cctx.markcommitted(ret)
1514 ms.reset()
1517 ms.reset()
1515 finally:
1518 finally:
1516 wlock.release()
1519 wlock.release()
1517
1520
1518 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1521 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1519 # hack for command that use a temporary commit (eg: histedit)
1522 # hack for command that use a temporary commit (eg: histedit)
1520 # temporary commit got stripped before hook release
1523 # temporary commit got stripped before hook release
1521 if self.changelog.hasnode(ret):
1524 if self.changelog.hasnode(ret):
1522 self.hook("commit", node=node, parent1=parent1,
1525 self.hook("commit", node=node, parent1=parent1,
1523 parent2=parent2)
1526 parent2=parent2)
1524 self._afterlock(commithook)
1527 self._afterlock(commithook)
1525 return ret
1528 return ret
1526
1529
1527 @unfilteredmethod
1530 @unfilteredmethod
1528 def commitctx(self, ctx, error=False):
1531 def commitctx(self, ctx, error=False):
1529 """Add a new revision to current repository.
1532 """Add a new revision to current repository.
1530 Revision information is passed via the context argument.
1533 Revision information is passed via the context argument.
1531 """
1534 """
1532
1535
1533 tr = None
1536 tr = None
1534 p1, p2 = ctx.p1(), ctx.p2()
1537 p1, p2 = ctx.p1(), ctx.p2()
1535 user = ctx.user()
1538 user = ctx.user()
1536
1539
1537 lock = self.lock()
1540 lock = self.lock()
1538 try:
1541 try:
1539 tr = self.transaction("commit")
1542 tr = self.transaction("commit")
1540 trp = weakref.proxy(tr)
1543 trp = weakref.proxy(tr)
1541
1544
1542 if ctx.files():
1545 if ctx.files():
1543 m1 = p1.manifest()
1546 m1 = p1.manifest()
1544 m2 = p2.manifest()
1547 m2 = p2.manifest()
1545 m = m1.copy()
1548 m = m1.copy()
1546
1549
1547 # check in files
1550 # check in files
1548 added = []
1551 added = []
1549 changed = []
1552 changed = []
1550 removed = list(ctx.removed())
1553 removed = list(ctx.removed())
1551 linkrev = len(self)
1554 linkrev = len(self)
1552 self.ui.note(_("committing files:\n"))
1555 self.ui.note(_("committing files:\n"))
1553 for f in sorted(ctx.modified() + ctx.added()):
1556 for f in sorted(ctx.modified() + ctx.added()):
1554 self.ui.note(f + "\n")
1557 self.ui.note(f + "\n")
1555 try:
1558 try:
1556 fctx = ctx[f]
1559 fctx = ctx[f]
1557 if fctx is None:
1560 if fctx is None:
1558 removed.append(f)
1561 removed.append(f)
1559 else:
1562 else:
1560 added.append(f)
1563 added.append(f)
1561 m[f] = self._filecommit(fctx, m1, m2, linkrev,
1564 m[f] = self._filecommit(fctx, m1, m2, linkrev,
1562 trp, changed)
1565 trp, changed)
1563 m.setflag(f, fctx.flags())
1566 m.setflag(f, fctx.flags())
1564 except OSError, inst:
1567 except OSError, inst:
1565 self.ui.warn(_("trouble committing %s!\n") % f)
1568 self.ui.warn(_("trouble committing %s!\n") % f)
1566 raise
1569 raise
1567 except IOError, inst:
1570 except IOError, inst:
1568 errcode = getattr(inst, 'errno', errno.ENOENT)
1571 errcode = getattr(inst, 'errno', errno.ENOENT)
1569 if error or errcode and errcode != errno.ENOENT:
1572 if error or errcode and errcode != errno.ENOENT:
1570 self.ui.warn(_("trouble committing %s!\n") % f)
1573 self.ui.warn(_("trouble committing %s!\n") % f)
1571 raise
1574 raise
1572
1575
1573 # update manifest
1576 # update manifest
1574 self.ui.note(_("committing manifest\n"))
1577 self.ui.note(_("committing manifest\n"))
1575 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1578 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1576 drop = [f for f in removed if f in m]
1579 drop = [f for f in removed if f in m]
1577 for f in drop:
1580 for f in drop:
1578 del m[f]
1581 del m[f]
1579 mn = self.manifest.add(m, trp, linkrev,
1582 mn = self.manifest.add(m, trp, linkrev,
1580 p1.manifestnode(), p2.manifestnode(),
1583 p1.manifestnode(), p2.manifestnode(),
1581 added, drop)
1584 added, drop)
1582 files = changed + removed
1585 files = changed + removed
1583 else:
1586 else:
1584 mn = p1.manifestnode()
1587 mn = p1.manifestnode()
1585 files = []
1588 files = []
1586
1589
1587 # update changelog
1590 # update changelog
1588 self.ui.note(_("committing changelog\n"))
1591 self.ui.note(_("committing changelog\n"))
1589 self.changelog.delayupdate(tr)
1592 self.changelog.delayupdate(tr)
1590 n = self.changelog.add(mn, files, ctx.description(),
1593 n = self.changelog.add(mn, files, ctx.description(),
1591 trp, p1.node(), p2.node(),
1594 trp, p1.node(), p2.node(),
1592 user, ctx.date(), ctx.extra().copy())
1595 user, ctx.date(), ctx.extra().copy())
1593 p = lambda: tr.writepending() and self.root or ""
1596 p = lambda: tr.writepending() and self.root or ""
1594 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1597 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1595 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1598 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1596 parent2=xp2, pending=p)
1599 parent2=xp2, pending=p)
1597 # set the new commit is proper phase
1600 # set the new commit is proper phase
1598 targetphase = subrepo.newcommitphase(self.ui, ctx)
1601 targetphase = subrepo.newcommitphase(self.ui, ctx)
1599 if targetphase:
1602 if targetphase:
1600 # retract boundary do not alter parent changeset.
1603 # retract boundary do not alter parent changeset.
1601 # if a parent have higher the resulting phase will
1604 # if a parent have higher the resulting phase will
1602 # be compliant anyway
1605 # be compliant anyway
1603 #
1606 #
1604 # if minimal phase was 0 we don't need to retract anything
1607 # if minimal phase was 0 we don't need to retract anything
1605 phases.retractboundary(self, tr, targetphase, [n])
1608 phases.retractboundary(self, tr, targetphase, [n])
1606 tr.close()
1609 tr.close()
1607 branchmap.updatecache(self.filtered('served'))
1610 branchmap.updatecache(self.filtered('served'))
1608 return n
1611 return n
1609 finally:
1612 finally:
1610 if tr:
1613 if tr:
1611 tr.release()
1614 tr.release()
1612 lock.release()
1615 lock.release()
1613
1616
1614 @unfilteredmethod
1617 @unfilteredmethod
1615 def destroying(self):
1618 def destroying(self):
1616 '''Inform the repository that nodes are about to be destroyed.
1619 '''Inform the repository that nodes are about to be destroyed.
1617 Intended for use by strip and rollback, so there's a common
1620 Intended for use by strip and rollback, so there's a common
1618 place for anything that has to be done before destroying history.
1621 place for anything that has to be done before destroying history.
1619
1622
1620 This is mostly useful for saving state that is in memory and waiting
1623 This is mostly useful for saving state that is in memory and waiting
1621 to be flushed when the current lock is released. Because a call to
1624 to be flushed when the current lock is released. Because a call to
1622 destroyed is imminent, the repo will be invalidated causing those
1625 destroyed is imminent, the repo will be invalidated causing those
1623 changes to stay in memory (waiting for the next unlock), or vanish
1626 changes to stay in memory (waiting for the next unlock), or vanish
1624 completely.
1627 completely.
1625 '''
1628 '''
1626 # When using the same lock to commit and strip, the phasecache is left
1629 # When using the same lock to commit and strip, the phasecache is left
1627 # dirty after committing. Then when we strip, the repo is invalidated,
1630 # dirty after committing. Then when we strip, the repo is invalidated,
1628 # causing those changes to disappear.
1631 # causing those changes to disappear.
1629 if '_phasecache' in vars(self):
1632 if '_phasecache' in vars(self):
1630 self._phasecache.write()
1633 self._phasecache.write()
1631
1634
1632 @unfilteredmethod
1635 @unfilteredmethod
1633 def destroyed(self):
1636 def destroyed(self):
1634 '''Inform the repository that nodes have been destroyed.
1637 '''Inform the repository that nodes have been destroyed.
1635 Intended for use by strip and rollback, so there's a common
1638 Intended for use by strip and rollback, so there's a common
1636 place for anything that has to be done after destroying history.
1639 place for anything that has to be done after destroying history.
1637 '''
1640 '''
1638 # When one tries to:
1641 # When one tries to:
1639 # 1) destroy nodes thus calling this method (e.g. strip)
1642 # 1) destroy nodes thus calling this method (e.g. strip)
1640 # 2) use phasecache somewhere (e.g. commit)
1643 # 2) use phasecache somewhere (e.g. commit)
1641 #
1644 #
1642 # then 2) will fail because the phasecache contains nodes that were
1645 # then 2) will fail because the phasecache contains nodes that were
1643 # removed. We can either remove phasecache from the filecache,
1646 # removed. We can either remove phasecache from the filecache,
1644 # causing it to reload next time it is accessed, or simply filter
1647 # causing it to reload next time it is accessed, or simply filter
1645 # the removed nodes now and write the updated cache.
1648 # the removed nodes now and write the updated cache.
1646 self._phasecache.filterunknown(self)
1649 self._phasecache.filterunknown(self)
1647 self._phasecache.write()
1650 self._phasecache.write()
1648
1651
1649 # update the 'served' branch cache to help read only server process
1652 # update the 'served' branch cache to help read only server process
1650 # Thanks to branchcache collaboration this is done from the nearest
1653 # Thanks to branchcache collaboration this is done from the nearest
1651 # filtered subset and it is expected to be fast.
1654 # filtered subset and it is expected to be fast.
1652 branchmap.updatecache(self.filtered('served'))
1655 branchmap.updatecache(self.filtered('served'))
1653
1656
1654 # Ensure the persistent tag cache is updated. Doing it now
1657 # Ensure the persistent tag cache is updated. Doing it now
1655 # means that the tag cache only has to worry about destroyed
1658 # means that the tag cache only has to worry about destroyed
1656 # heads immediately after a strip/rollback. That in turn
1659 # heads immediately after a strip/rollback. That in turn
1657 # guarantees that "cachetip == currenttip" (comparing both rev
1660 # guarantees that "cachetip == currenttip" (comparing both rev
1658 # and node) always means no nodes have been added or destroyed.
1661 # and node) always means no nodes have been added or destroyed.
1659
1662
1660 # XXX this is suboptimal when qrefresh'ing: we strip the current
1663 # XXX this is suboptimal when qrefresh'ing: we strip the current
1661 # head, refresh the tag cache, then immediately add a new head.
1664 # head, refresh the tag cache, then immediately add a new head.
1662 # But I think doing it this way is necessary for the "instant
1665 # But I think doing it this way is necessary for the "instant
1663 # tag cache retrieval" case to work.
1666 # tag cache retrieval" case to work.
1664 self.invalidate()
1667 self.invalidate()
1665
1668
1666 def walk(self, match, node=None):
1669 def walk(self, match, node=None):
1667 '''
1670 '''
1668 walk recursively through the directory tree or a given
1671 walk recursively through the directory tree or a given
1669 changeset, finding all files matched by the match
1672 changeset, finding all files matched by the match
1670 function
1673 function
1671 '''
1674 '''
1672 return self[node].walk(match)
1675 return self[node].walk(match)
1673
1676
1674 def status(self, node1='.', node2=None, match=None,
1677 def status(self, node1='.', node2=None, match=None,
1675 ignored=False, clean=False, unknown=False,
1678 ignored=False, clean=False, unknown=False,
1676 listsubrepos=False):
1679 listsubrepos=False):
1677 '''a convenience method that calls node1.status(node2)'''
1680 '''a convenience method that calls node1.status(node2)'''
1678 return self[node1].status(node2, match, ignored, clean, unknown,
1681 return self[node1].status(node2, match, ignored, clean, unknown,
1679 listsubrepos)
1682 listsubrepos)
1680
1683
1681 def heads(self, start=None):
1684 def heads(self, start=None):
1682 heads = self.changelog.heads(start)
1685 heads = self.changelog.heads(start)
1683 # sort the output in rev descending order
1686 # sort the output in rev descending order
1684 return sorted(heads, key=self.changelog.rev, reverse=True)
1687 return sorted(heads, key=self.changelog.rev, reverse=True)
1685
1688
1686 def branchheads(self, branch=None, start=None, closed=False):
1689 def branchheads(self, branch=None, start=None, closed=False):
1687 '''return a (possibly filtered) list of heads for the given branch
1690 '''return a (possibly filtered) list of heads for the given branch
1688
1691
1689 Heads are returned in topological order, from newest to oldest.
1692 Heads are returned in topological order, from newest to oldest.
1690 If branch is None, use the dirstate branch.
1693 If branch is None, use the dirstate branch.
1691 If start is not None, return only heads reachable from start.
1694 If start is not None, return only heads reachable from start.
1692 If closed is True, return heads that are marked as closed as well.
1695 If closed is True, return heads that are marked as closed as well.
1693 '''
1696 '''
1694 if branch is None:
1697 if branch is None:
1695 branch = self[None].branch()
1698 branch = self[None].branch()
1696 branches = self.branchmap()
1699 branches = self.branchmap()
1697 if branch not in branches:
1700 if branch not in branches:
1698 return []
1701 return []
1699 # the cache returns heads ordered lowest to highest
1702 # the cache returns heads ordered lowest to highest
1700 bheads = list(reversed(branches.branchheads(branch, closed=closed)))
1703 bheads = list(reversed(branches.branchheads(branch, closed=closed)))
1701 if start is not None:
1704 if start is not None:
1702 # filter out the heads that cannot be reached from startrev
1705 # filter out the heads that cannot be reached from startrev
1703 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1706 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1704 bheads = [h for h in bheads if h in fbheads]
1707 bheads = [h for h in bheads if h in fbheads]
1705 return bheads
1708 return bheads
1706
1709
1707 def branches(self, nodes):
1710 def branches(self, nodes):
1708 if not nodes:
1711 if not nodes:
1709 nodes = [self.changelog.tip()]
1712 nodes = [self.changelog.tip()]
1710 b = []
1713 b = []
1711 for n in nodes:
1714 for n in nodes:
1712 t = n
1715 t = n
1713 while True:
1716 while True:
1714 p = self.changelog.parents(n)
1717 p = self.changelog.parents(n)
1715 if p[1] != nullid or p[0] == nullid:
1718 if p[1] != nullid or p[0] == nullid:
1716 b.append((t, n, p[0], p[1]))
1719 b.append((t, n, p[0], p[1]))
1717 break
1720 break
1718 n = p[0]
1721 n = p[0]
1719 return b
1722 return b
1720
1723
1721 def between(self, pairs):
1724 def between(self, pairs):
1722 r = []
1725 r = []
1723
1726
1724 for top, bottom in pairs:
1727 for top, bottom in pairs:
1725 n, l, i = top, [], 0
1728 n, l, i = top, [], 0
1726 f = 1
1729 f = 1
1727
1730
1728 while n != bottom and n != nullid:
1731 while n != bottom and n != nullid:
1729 p = self.changelog.parents(n)[0]
1732 p = self.changelog.parents(n)[0]
1730 if i == f:
1733 if i == f:
1731 l.append(n)
1734 l.append(n)
1732 f = f * 2
1735 f = f * 2
1733 n = p
1736 n = p
1734 i += 1
1737 i += 1
1735
1738
1736 r.append(l)
1739 r.append(l)
1737
1740
1738 return r
1741 return r
1739
1742
1740 def checkpush(self, pushop):
1743 def checkpush(self, pushop):
1741 """Extensions can override this function if additional checks have
1744 """Extensions can override this function if additional checks have
1742 to be performed before pushing, or call it if they override push
1745 to be performed before pushing, or call it if they override push
1743 command.
1746 command.
1744 """
1747 """
1745 pass
1748 pass
1746
1749
1747 @unfilteredpropertycache
1750 @unfilteredpropertycache
1748 def prepushoutgoinghooks(self):
1751 def prepushoutgoinghooks(self):
1749 """Return util.hooks consists of "(repo, remote, outgoing)"
1752 """Return util.hooks consists of "(repo, remote, outgoing)"
1750 functions, which are called before pushing changesets.
1753 functions, which are called before pushing changesets.
1751 """
1754 """
1752 return util.hooks()
1755 return util.hooks()
1753
1756
1754 def stream_in(self, remote, remotereqs):
1757 def stream_in(self, remote, remotereqs):
1755 lock = self.lock()
1758 lock = self.lock()
1756 try:
1759 try:
1757 # Save remote branchmap. We will use it later
1760 # Save remote branchmap. We will use it later
1758 # to speed up branchcache creation
1761 # to speed up branchcache creation
1759 rbranchmap = None
1762 rbranchmap = None
1760 if remote.capable("branchmap"):
1763 if remote.capable("branchmap"):
1761 rbranchmap = remote.branchmap()
1764 rbranchmap = remote.branchmap()
1762
1765
1763 fp = remote.stream_out()
1766 fp = remote.stream_out()
1764 l = fp.readline()
1767 l = fp.readline()
1765 try:
1768 try:
1766 resp = int(l)
1769 resp = int(l)
1767 except ValueError:
1770 except ValueError:
1768 raise error.ResponseError(
1771 raise error.ResponseError(
1769 _('unexpected response from remote server:'), l)
1772 _('unexpected response from remote server:'), l)
1770 if resp == 1:
1773 if resp == 1:
1771 raise util.Abort(_('operation forbidden by server'))
1774 raise util.Abort(_('operation forbidden by server'))
1772 elif resp == 2:
1775 elif resp == 2:
1773 raise util.Abort(_('locking the remote repository failed'))
1776 raise util.Abort(_('locking the remote repository failed'))
1774 elif resp != 0:
1777 elif resp != 0:
1775 raise util.Abort(_('the server sent an unknown error code'))
1778 raise util.Abort(_('the server sent an unknown error code'))
1776 self.ui.status(_('streaming all changes\n'))
1779 self.ui.status(_('streaming all changes\n'))
1777 l = fp.readline()
1780 l = fp.readline()
1778 try:
1781 try:
1779 total_files, total_bytes = map(int, l.split(' ', 1))
1782 total_files, total_bytes = map(int, l.split(' ', 1))
1780 except (ValueError, TypeError):
1783 except (ValueError, TypeError):
1781 raise error.ResponseError(
1784 raise error.ResponseError(
1782 _('unexpected response from remote server:'), l)
1785 _('unexpected response from remote server:'), l)
1783 self.ui.status(_('%d files to transfer, %s of data\n') %
1786 self.ui.status(_('%d files to transfer, %s of data\n') %
1784 (total_files, util.bytecount(total_bytes)))
1787 (total_files, util.bytecount(total_bytes)))
1785 handled_bytes = 0
1788 handled_bytes = 0
1786 self.ui.progress(_('clone'), 0, total=total_bytes)
1789 self.ui.progress(_('clone'), 0, total=total_bytes)
1787 start = time.time()
1790 start = time.time()
1788
1791
1789 tr = self.transaction(_('clone'))
1792 tr = self.transaction(_('clone'))
1790 try:
1793 try:
1791 for i in xrange(total_files):
1794 for i in xrange(total_files):
1792 # XXX doesn't support '\n' or '\r' in filenames
1795 # XXX doesn't support '\n' or '\r' in filenames
1793 l = fp.readline()
1796 l = fp.readline()
1794 try:
1797 try:
1795 name, size = l.split('\0', 1)
1798 name, size = l.split('\0', 1)
1796 size = int(size)
1799 size = int(size)
1797 except (ValueError, TypeError):
1800 except (ValueError, TypeError):
1798 raise error.ResponseError(
1801 raise error.ResponseError(
1799 _('unexpected response from remote server:'), l)
1802 _('unexpected response from remote server:'), l)
1800 if self.ui.debugflag:
1803 if self.ui.debugflag:
1801 self.ui.debug('adding %s (%s)\n' %
1804 self.ui.debug('adding %s (%s)\n' %
1802 (name, util.bytecount(size)))
1805 (name, util.bytecount(size)))
1803 # for backwards compat, name was partially encoded
1806 # for backwards compat, name was partially encoded
1804 ofp = self.svfs(store.decodedir(name), 'w')
1807 ofp = self.svfs(store.decodedir(name), 'w')
1805 for chunk in util.filechunkiter(fp, limit=size):
1808 for chunk in util.filechunkiter(fp, limit=size):
1806 handled_bytes += len(chunk)
1809 handled_bytes += len(chunk)
1807 self.ui.progress(_('clone'), handled_bytes,
1810 self.ui.progress(_('clone'), handled_bytes,
1808 total=total_bytes)
1811 total=total_bytes)
1809 ofp.write(chunk)
1812 ofp.write(chunk)
1810 ofp.close()
1813 ofp.close()
1811 tr.close()
1814 tr.close()
1812 finally:
1815 finally:
1813 tr.release()
1816 tr.release()
1814
1817
1815 # Writing straight to files circumvented the inmemory caches
1818 # Writing straight to files circumvented the inmemory caches
1816 self.invalidate()
1819 self.invalidate()
1817
1820
1818 elapsed = time.time() - start
1821 elapsed = time.time() - start
1819 if elapsed <= 0:
1822 if elapsed <= 0:
1820 elapsed = 0.001
1823 elapsed = 0.001
1821 self.ui.progress(_('clone'), None)
1824 self.ui.progress(_('clone'), None)
1822 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1825 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1823 (util.bytecount(total_bytes), elapsed,
1826 (util.bytecount(total_bytes), elapsed,
1824 util.bytecount(total_bytes / elapsed)))
1827 util.bytecount(total_bytes / elapsed)))
1825
1828
1826 # new requirements = old non-format requirements +
1829 # new requirements = old non-format requirements +
1827 # new format-related remote requirements
1830 # new format-related remote requirements
1828 # requirements from the streamed-in repository
1831 # requirements from the streamed-in repository
1829 self.requirements = remotereqs | (
1832 self.requirements = remotereqs | (
1830 self.requirements - self.supportedformats)
1833 self.requirements - self.supportedformats)
1831 self._applyopenerreqs()
1834 self._applyopenerreqs()
1832 self._writerequirements()
1835 self._writerequirements()
1833
1836
1834 if rbranchmap:
1837 if rbranchmap:
1835 rbheads = []
1838 rbheads = []
1836 closed = []
1839 closed = []
1837 for bheads in rbranchmap.itervalues():
1840 for bheads in rbranchmap.itervalues():
1838 rbheads.extend(bheads)
1841 rbheads.extend(bheads)
1839 for h in bheads:
1842 for h in bheads:
1840 r = self.changelog.rev(h)
1843 r = self.changelog.rev(h)
1841 b, c = self.changelog.branchinfo(r)
1844 b, c = self.changelog.branchinfo(r)
1842 if c:
1845 if c:
1843 closed.append(h)
1846 closed.append(h)
1844
1847
1845 if rbheads:
1848 if rbheads:
1846 rtiprev = max((int(self.changelog.rev(node))
1849 rtiprev = max((int(self.changelog.rev(node))
1847 for node in rbheads))
1850 for node in rbheads))
1848 cache = branchmap.branchcache(rbranchmap,
1851 cache = branchmap.branchcache(rbranchmap,
1849 self[rtiprev].node(),
1852 self[rtiprev].node(),
1850 rtiprev,
1853 rtiprev,
1851 closednodes=closed)
1854 closednodes=closed)
1852 # Try to stick it as low as possible
1855 # Try to stick it as low as possible
1853 # filter above served are unlikely to be fetch from a clone
1856 # filter above served are unlikely to be fetch from a clone
1854 for candidate in ('base', 'immutable', 'served'):
1857 for candidate in ('base', 'immutable', 'served'):
1855 rview = self.filtered(candidate)
1858 rview = self.filtered(candidate)
1856 if cache.validfor(rview):
1859 if cache.validfor(rview):
1857 self._branchcaches[candidate] = cache
1860 self._branchcaches[candidate] = cache
1858 cache.write(rview)
1861 cache.write(rview)
1859 break
1862 break
1860 self.invalidate()
1863 self.invalidate()
1861 return len(self.heads()) + 1
1864 return len(self.heads()) + 1
1862 finally:
1865 finally:
1863 lock.release()
1866 lock.release()
1864
1867
1865 def clone(self, remote, heads=[], stream=None):
1868 def clone(self, remote, heads=[], stream=None):
1866 '''clone remote repository.
1869 '''clone remote repository.
1867
1870
1868 keyword arguments:
1871 keyword arguments:
1869 heads: list of revs to clone (forces use of pull)
1872 heads: list of revs to clone (forces use of pull)
1870 stream: use streaming clone if possible'''
1873 stream: use streaming clone if possible'''
1871
1874
1872 # now, all clients that can request uncompressed clones can
1875 # now, all clients that can request uncompressed clones can
1873 # read repo formats supported by all servers that can serve
1876 # read repo formats supported by all servers that can serve
1874 # them.
1877 # them.
1875
1878
1876 # if revlog format changes, client will have to check version
1879 # if revlog format changes, client will have to check version
1877 # and format flags on "stream" capability, and use
1880 # and format flags on "stream" capability, and use
1878 # uncompressed only if compatible.
1881 # uncompressed only if compatible.
1879
1882
1880 if stream is None:
1883 if stream is None:
1881 # if the server explicitly prefers to stream (for fast LANs)
1884 # if the server explicitly prefers to stream (for fast LANs)
1882 stream = remote.capable('stream-preferred')
1885 stream = remote.capable('stream-preferred')
1883
1886
1884 if stream and not heads:
1887 if stream and not heads:
1885 # 'stream' means remote revlog format is revlogv1 only
1888 # 'stream' means remote revlog format is revlogv1 only
1886 if remote.capable('stream'):
1889 if remote.capable('stream'):
1887 self.stream_in(remote, set(('revlogv1',)))
1890 self.stream_in(remote, set(('revlogv1',)))
1888 else:
1891 else:
1889 # otherwise, 'streamreqs' contains the remote revlog format
1892 # otherwise, 'streamreqs' contains the remote revlog format
1890 streamreqs = remote.capable('streamreqs')
1893 streamreqs = remote.capable('streamreqs')
1891 if streamreqs:
1894 if streamreqs:
1892 streamreqs = set(streamreqs.split(','))
1895 streamreqs = set(streamreqs.split(','))
1893 # if we support it, stream in and adjust our requirements
1896 # if we support it, stream in and adjust our requirements
1894 if not streamreqs - self.supportedformats:
1897 if not streamreqs - self.supportedformats:
1895 self.stream_in(remote, streamreqs)
1898 self.stream_in(remote, streamreqs)
1896
1899
1897 quiet = self.ui.backupconfig('ui', 'quietbookmarkmove')
1900 quiet = self.ui.backupconfig('ui', 'quietbookmarkmove')
1898 try:
1901 try:
1899 self.ui.setconfig('ui', 'quietbookmarkmove', True, 'clone')
1902 self.ui.setconfig('ui', 'quietbookmarkmove', True, 'clone')
1900 ret = exchange.pull(self, remote, heads).cgresult
1903 ret = exchange.pull(self, remote, heads).cgresult
1901 finally:
1904 finally:
1902 self.ui.restoreconfig(quiet)
1905 self.ui.restoreconfig(quiet)
1903 return ret
1906 return ret
1904
1907
1905 def pushkey(self, namespace, key, old, new):
1908 def pushkey(self, namespace, key, old, new):
1906 try:
1909 try:
1907 tr = self.currenttransaction()
1910 tr = self.currenttransaction()
1908 hookargs = {}
1911 hookargs = {}
1909 if tr is not None:
1912 if tr is not None:
1910 hookargs.update(tr.hookargs)
1913 hookargs.update(tr.hookargs)
1911 pending = lambda: tr.writepending() and self.root or ""
1914 pending = lambda: tr.writepending() and self.root or ""
1912 hookargs['pending'] = pending
1915 hookargs['pending'] = pending
1913 hookargs['namespace'] = namespace
1916 hookargs['namespace'] = namespace
1914 hookargs['key'] = key
1917 hookargs['key'] = key
1915 hookargs['old'] = old
1918 hookargs['old'] = old
1916 hookargs['new'] = new
1919 hookargs['new'] = new
1917 self.hook('prepushkey', throw=True, **hookargs)
1920 self.hook('prepushkey', throw=True, **hookargs)
1918 except error.HookAbort, exc:
1921 except error.HookAbort, exc:
1919 self.ui.write_err(_("pushkey-abort: %s\n") % exc)
1922 self.ui.write_err(_("pushkey-abort: %s\n") % exc)
1920 if exc.hint:
1923 if exc.hint:
1921 self.ui.write_err(_("(%s)\n") % exc.hint)
1924 self.ui.write_err(_("(%s)\n") % exc.hint)
1922 return False
1925 return False
1923 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
1926 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
1924 ret = pushkey.push(self, namespace, key, old, new)
1927 ret = pushkey.push(self, namespace, key, old, new)
1925 def runhook():
1928 def runhook():
1926 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
1929 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
1927 ret=ret)
1930 ret=ret)
1928 self._afterlock(runhook)
1931 self._afterlock(runhook)
1929 return ret
1932 return ret
1930
1933
1931 def listkeys(self, namespace):
1934 def listkeys(self, namespace):
1932 self.hook('prelistkeys', throw=True, namespace=namespace)
1935 self.hook('prelistkeys', throw=True, namespace=namespace)
1933 self.ui.debug('listing keys for "%s"\n' % namespace)
1936 self.ui.debug('listing keys for "%s"\n' % namespace)
1934 values = pushkey.list(self, namespace)
1937 values = pushkey.list(self, namespace)
1935 self.hook('listkeys', namespace=namespace, values=values)
1938 self.hook('listkeys', namespace=namespace, values=values)
1936 return values
1939 return values
1937
1940
1938 def debugwireargs(self, one, two, three=None, four=None, five=None):
1941 def debugwireargs(self, one, two, three=None, four=None, five=None):
1939 '''used to test argument passing over the wire'''
1942 '''used to test argument passing over the wire'''
1940 return "%s %s %s %s %s" % (one, two, three, four, five)
1943 return "%s %s %s %s %s" % (one, two, three, four, five)
1941
1944
1942 def savecommitmessage(self, text):
1945 def savecommitmessage(self, text):
1943 fp = self.vfs('last-message.txt', 'wb')
1946 fp = self.vfs('last-message.txt', 'wb')
1944 try:
1947 try:
1945 fp.write(text)
1948 fp.write(text)
1946 finally:
1949 finally:
1947 fp.close()
1950 fp.close()
1948 return self.pathto(fp.name[len(self.root) + 1:])
1951 return self.pathto(fp.name[len(self.root) + 1:])
1949
1952
1950 # used to avoid circular references so destructors work
1953 # used to avoid circular references so destructors work
1951 def aftertrans(files):
1954 def aftertrans(files):
1952 renamefiles = [tuple(t) for t in files]
1955 renamefiles = [tuple(t) for t in files]
1953 def a():
1956 def a():
1954 for vfs, src, dest in renamefiles:
1957 for vfs, src, dest in renamefiles:
1955 try:
1958 try:
1956 vfs.rename(src, dest)
1959 vfs.rename(src, dest)
1957 except OSError: # journal file does not yet exist
1960 except OSError: # journal file does not yet exist
1958 pass
1961 pass
1959 return a
1962 return a
1960
1963
1961 def undoname(fn):
1964 def undoname(fn):
1962 base, name = os.path.split(fn)
1965 base, name = os.path.split(fn)
1963 assert name.startswith('journal')
1966 assert name.startswith('journal')
1964 return os.path.join(base, name.replace('journal', 'undo', 1))
1967 return os.path.join(base, name.replace('journal', 'undo', 1))
1965
1968
1966 def instance(ui, path, create):
1969 def instance(ui, path, create):
1967 return localrepository(ui, util.urllocalpath(path), create)
1970 return localrepository(ui, util.urllocalpath(path), create)
1968
1971
1969 def islocal(path):
1972 def islocal(path):
1970 return True
1973 return True
@@ -1,944 +1,946 b''
1 # manifest.py - manifest revision class for mercurial
1 # manifest.py - manifest revision class 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 i18n import _
8 from i18n import _
9 import mdiff, parsers, error, revlog, util
9 import mdiff, parsers, error, revlog, util
10 import array, struct
10 import array, struct
11 import os
11 import os
12
12
13 propertycache = util.propertycache
13 propertycache = util.propertycache
14
14
15 def _parsev1(data):
15 def _parsev1(data):
16 # This method does a little bit of excessive-looking
16 # This method does a little bit of excessive-looking
17 # precondition checking. This is so that the behavior of this
17 # precondition checking. This is so that the behavior of this
18 # class exactly matches its C counterpart to try and help
18 # class exactly matches its C counterpart to try and help
19 # prevent surprise breakage for anyone that develops against
19 # prevent surprise breakage for anyone that develops against
20 # the pure version.
20 # the pure version.
21 if data and data[-1] != '\n':
21 if data and data[-1] != '\n':
22 raise ValueError('Manifest did not end in a newline.')
22 raise ValueError('Manifest did not end in a newline.')
23 prev = None
23 prev = None
24 for l in data.splitlines():
24 for l in data.splitlines():
25 if prev is not None and prev > l:
25 if prev is not None and prev > l:
26 raise ValueError('Manifest lines not in sorted order.')
26 raise ValueError('Manifest lines not in sorted order.')
27 prev = l
27 prev = l
28 f, n = l.split('\0')
28 f, n = l.split('\0')
29 if len(n) > 40:
29 if len(n) > 40:
30 yield f, revlog.bin(n[:40]), n[40:]
30 yield f, revlog.bin(n[:40]), n[40:]
31 else:
31 else:
32 yield f, revlog.bin(n), ''
32 yield f, revlog.bin(n), ''
33
33
34 def _parsev2(data):
34 def _parsev2(data):
35 metadataend = data.find('\n')
35 metadataend = data.find('\n')
36 # Just ignore metadata for now
36 # Just ignore metadata for now
37 pos = metadataend + 1
37 pos = metadataend + 1
38 prevf = ''
38 prevf = ''
39 while pos < len(data):
39 while pos < len(data):
40 end = data.find('\n', pos + 1) # +1 to skip stem length byte
40 end = data.find('\n', pos + 1) # +1 to skip stem length byte
41 if end == -1:
41 if end == -1:
42 raise ValueError('Manifest ended with incomplete file entry.')
42 raise ValueError('Manifest ended with incomplete file entry.')
43 stemlen = ord(data[pos])
43 stemlen = ord(data[pos])
44 items = data[pos + 1:end].split('\0')
44 items = data[pos + 1:end].split('\0')
45 f = prevf[:stemlen] + items[0]
45 f = prevf[:stemlen] + items[0]
46 if prevf > f:
46 if prevf > f:
47 raise ValueError('Manifest entries not in sorted order.')
47 raise ValueError('Manifest entries not in sorted order.')
48 fl = items[1]
48 fl = items[1]
49 # Just ignore metadata (items[2:] for now)
49 # Just ignore metadata (items[2:] for now)
50 n = data[end + 1:end + 21]
50 n = data[end + 1:end + 21]
51 yield f, n, fl
51 yield f, n, fl
52 pos = end + 22
52 pos = end + 22
53 prevf = f
53 prevf = f
54
54
55 def _parse(data):
55 def _parse(data):
56 """Generates (path, node, flags) tuples from a manifest text"""
56 """Generates (path, node, flags) tuples from a manifest text"""
57 if data.startswith('\0'):
57 if data.startswith('\0'):
58 return iter(_parsev2(data))
58 return iter(_parsev2(data))
59 else:
59 else:
60 return iter(_parsev1(data))
60 return iter(_parsev1(data))
61
61
62 def _text(it, usemanifestv2):
62 def _text(it, usemanifestv2):
63 """Given an iterator over (path, node, flags) tuples, returns a manifest
63 """Given an iterator over (path, node, flags) tuples, returns a manifest
64 text"""
64 text"""
65 if usemanifestv2:
65 if usemanifestv2:
66 return _textv2(it)
66 return _textv2(it)
67 else:
67 else:
68 return _textv1(it)
68 return _textv1(it)
69
69
70 def _textv1(it):
70 def _textv1(it):
71 files = []
71 files = []
72 lines = []
72 lines = []
73 _hex = revlog.hex
73 _hex = revlog.hex
74 for f, n, fl in it:
74 for f, n, fl in it:
75 files.append(f)
75 files.append(f)
76 # if this is changed to support newlines in filenames,
76 # if this is changed to support newlines in filenames,
77 # be sure to check the templates/ dir again (especially *-raw.tmpl)
77 # be sure to check the templates/ dir again (especially *-raw.tmpl)
78 lines.append("%s\0%s%s\n" % (f, _hex(n), fl))
78 lines.append("%s\0%s%s\n" % (f, _hex(n), fl))
79
79
80 _checkforbidden(files)
80 _checkforbidden(files)
81 return ''.join(lines)
81 return ''.join(lines)
82
82
83 def _textv2(it):
83 def _textv2(it):
84 files = []
84 files = []
85 lines = ['\0\n']
85 lines = ['\0\n']
86 prevf = ''
86 prevf = ''
87 for f, n, fl in it:
87 for f, n, fl in it:
88 files.append(f)
88 files.append(f)
89 stem = os.path.commonprefix([prevf, f])
89 stem = os.path.commonprefix([prevf, f])
90 stemlen = min(len(stem), 255)
90 stemlen = min(len(stem), 255)
91 lines.append("%c%s\0%s\n%s\n" % (stemlen, f[stemlen:], fl, n))
91 lines.append("%c%s\0%s\n%s\n" % (stemlen, f[stemlen:], fl, n))
92 prevf = f
92 prevf = f
93 _checkforbidden(files)
93 _checkforbidden(files)
94 return ''.join(lines)
94 return ''.join(lines)
95
95
96 class _lazymanifest(dict):
96 class _lazymanifest(dict):
97 """This is the pure implementation of lazymanifest.
97 """This is the pure implementation of lazymanifest.
98
98
99 It has not been optimized *at all* and is not lazy.
99 It has not been optimized *at all* and is not lazy.
100 """
100 """
101
101
102 def __init__(self, data):
102 def __init__(self, data):
103 dict.__init__(self)
103 dict.__init__(self)
104 for f, n, fl in _parse(data):
104 for f, n, fl in _parse(data):
105 self[f] = n, fl
105 self[f] = n, fl
106
106
107 def __setitem__(self, k, v):
107 def __setitem__(self, k, v):
108 node, flag = v
108 node, flag = v
109 assert node is not None
109 assert node is not None
110 if len(node) > 21:
110 if len(node) > 21:
111 node = node[:21] # match c implementation behavior
111 node = node[:21] # match c implementation behavior
112 dict.__setitem__(self, k, (node, flag))
112 dict.__setitem__(self, k, (node, flag))
113
113
114 def __iter__(self):
114 def __iter__(self):
115 return iter(sorted(dict.keys(self)))
115 return iter(sorted(dict.keys(self)))
116
116
117 def iterkeys(self):
117 def iterkeys(self):
118 return iter(sorted(dict.keys(self)))
118 return iter(sorted(dict.keys(self)))
119
119
120 def iterentries(self):
120 def iterentries(self):
121 return ((f, e[0], e[1]) for f, e in sorted(self.iteritems()))
121 return ((f, e[0], e[1]) for f, e in sorted(self.iteritems()))
122
122
123 def copy(self):
123 def copy(self):
124 c = _lazymanifest('')
124 c = _lazymanifest('')
125 c.update(self)
125 c.update(self)
126 return c
126 return c
127
127
128 def diff(self, m2, clean=False):
128 def diff(self, m2, clean=False):
129 '''Finds changes between the current manifest and m2.'''
129 '''Finds changes between the current manifest and m2.'''
130 diff = {}
130 diff = {}
131
131
132 for fn, e1 in self.iteritems():
132 for fn, e1 in self.iteritems():
133 if fn not in m2:
133 if fn not in m2:
134 diff[fn] = e1, (None, '')
134 diff[fn] = e1, (None, '')
135 else:
135 else:
136 e2 = m2[fn]
136 e2 = m2[fn]
137 if e1 != e2:
137 if e1 != e2:
138 diff[fn] = e1, e2
138 diff[fn] = e1, e2
139 elif clean:
139 elif clean:
140 diff[fn] = None
140 diff[fn] = None
141
141
142 for fn, e2 in m2.iteritems():
142 for fn, e2 in m2.iteritems():
143 if fn not in self:
143 if fn not in self:
144 diff[fn] = (None, ''), e2
144 diff[fn] = (None, ''), e2
145
145
146 return diff
146 return diff
147
147
148 def filtercopy(self, filterfn):
148 def filtercopy(self, filterfn):
149 c = _lazymanifest('')
149 c = _lazymanifest('')
150 for f, n, fl in self.iterentries():
150 for f, n, fl in self.iterentries():
151 if filterfn(f):
151 if filterfn(f):
152 c[f] = n, fl
152 c[f] = n, fl
153 return c
153 return c
154
154
155 def text(self):
155 def text(self):
156 """Get the full data of this manifest as a bytestring."""
156 """Get the full data of this manifest as a bytestring."""
157 return _textv1(self.iterentries())
157 return _textv1(self.iterentries())
158
158
159 try:
159 try:
160 _lazymanifest = parsers.lazymanifest
160 _lazymanifest = parsers.lazymanifest
161 except AttributeError:
161 except AttributeError:
162 pass
162 pass
163
163
164 class manifestdict(object):
164 class manifestdict(object):
165 def __init__(self, data=''):
165 def __init__(self, data=''):
166 if data.startswith('\0'):
166 if data.startswith('\0'):
167 #_lazymanifest can not parse v2
167 #_lazymanifest can not parse v2
168 self._lm = _lazymanifest('')
168 self._lm = _lazymanifest('')
169 for f, n, fl in _parsev2(data):
169 for f, n, fl in _parsev2(data):
170 self._lm[f] = n, fl
170 self._lm[f] = n, fl
171 else:
171 else:
172 self._lm = _lazymanifest(data)
172 self._lm = _lazymanifest(data)
173
173
174 def __getitem__(self, key):
174 def __getitem__(self, key):
175 return self._lm[key][0]
175 return self._lm[key][0]
176
176
177 def find(self, key):
177 def find(self, key):
178 return self._lm[key]
178 return self._lm[key]
179
179
180 def __len__(self):
180 def __len__(self):
181 return len(self._lm)
181 return len(self._lm)
182
182
183 def __setitem__(self, key, node):
183 def __setitem__(self, key, node):
184 self._lm[key] = node, self.flags(key, '')
184 self._lm[key] = node, self.flags(key, '')
185
185
186 def __contains__(self, key):
186 def __contains__(self, key):
187 return key in self._lm
187 return key in self._lm
188
188
189 def __delitem__(self, key):
189 def __delitem__(self, key):
190 del self._lm[key]
190 del self._lm[key]
191
191
192 def __iter__(self):
192 def __iter__(self):
193 return self._lm.__iter__()
193 return self._lm.__iter__()
194
194
195 def iterkeys(self):
195 def iterkeys(self):
196 return self._lm.iterkeys()
196 return self._lm.iterkeys()
197
197
198 def keys(self):
198 def keys(self):
199 return list(self.iterkeys())
199 return list(self.iterkeys())
200
200
201 def filesnotin(self, m2):
201 def filesnotin(self, m2):
202 '''Set of files in this manifest that are not in the other'''
202 '''Set of files in this manifest that are not in the other'''
203 files = set(self)
203 files = set(self)
204 files.difference_update(m2)
204 files.difference_update(m2)
205 return files
205 return files
206
206
207 @propertycache
207 @propertycache
208 def _dirs(self):
208 def _dirs(self):
209 return util.dirs(self)
209 return util.dirs(self)
210
210
211 def dirs(self):
211 def dirs(self):
212 return self._dirs
212 return self._dirs
213
213
214 def hasdir(self, dir):
214 def hasdir(self, dir):
215 return dir in self._dirs
215 return dir in self._dirs
216
216
217 def _filesfastpath(self, match):
217 def _filesfastpath(self, match):
218 '''Checks whether we can correctly and quickly iterate over matcher
218 '''Checks whether we can correctly and quickly iterate over matcher
219 files instead of over manifest files.'''
219 files instead of over manifest files.'''
220 files = match.files()
220 files = match.files()
221 return (len(files) < 100 and (match.isexact() or
221 return (len(files) < 100 and (match.isexact() or
222 (not match.anypats() and util.all(fn in self for fn in files))))
222 (not match.anypats() and util.all(fn in self for fn in files))))
223
223
224 def walk(self, match):
224 def walk(self, match):
225 '''Generates matching file names.
225 '''Generates matching file names.
226
226
227 Equivalent to manifest.matches(match).iterkeys(), but without creating
227 Equivalent to manifest.matches(match).iterkeys(), but without creating
228 an entirely new manifest.
228 an entirely new manifest.
229
229
230 It also reports nonexistent files by marking them bad with match.bad().
230 It also reports nonexistent files by marking them bad with match.bad().
231 '''
231 '''
232 if match.always():
232 if match.always():
233 for f in iter(self):
233 for f in iter(self):
234 yield f
234 yield f
235 return
235 return
236
236
237 fset = set(match.files())
237 fset = set(match.files())
238
238
239 # avoid the entire walk if we're only looking for specific files
239 # avoid the entire walk if we're only looking for specific files
240 if self._filesfastpath(match):
240 if self._filesfastpath(match):
241 for fn in sorted(fset):
241 for fn in sorted(fset):
242 yield fn
242 yield fn
243 return
243 return
244
244
245 for fn in self:
245 for fn in self:
246 if fn in fset:
246 if fn in fset:
247 # specified pattern is the exact name
247 # specified pattern is the exact name
248 fset.remove(fn)
248 fset.remove(fn)
249 if match(fn):
249 if match(fn):
250 yield fn
250 yield fn
251
251
252 # for dirstate.walk, files=['.'] means "walk the whole tree".
252 # for dirstate.walk, files=['.'] means "walk the whole tree".
253 # follow that here, too
253 # follow that here, too
254 fset.discard('.')
254 fset.discard('.')
255
255
256 for fn in sorted(fset):
256 for fn in sorted(fset):
257 if not self.hasdir(fn):
257 if not self.hasdir(fn):
258 match.bad(fn, None)
258 match.bad(fn, None)
259
259
260 def matches(self, match):
260 def matches(self, match):
261 '''generate a new manifest filtered by the match argument'''
261 '''generate a new manifest filtered by the match argument'''
262 if match.always():
262 if match.always():
263 return self.copy()
263 return self.copy()
264
264
265 if self._filesfastpath(match):
265 if self._filesfastpath(match):
266 m = manifestdict()
266 m = manifestdict()
267 lm = self._lm
267 lm = self._lm
268 for fn in match.files():
268 for fn in match.files():
269 if fn in lm:
269 if fn in lm:
270 m._lm[fn] = lm[fn]
270 m._lm[fn] = lm[fn]
271 return m
271 return m
272
272
273 m = manifestdict()
273 m = manifestdict()
274 m._lm = self._lm.filtercopy(match)
274 m._lm = self._lm.filtercopy(match)
275 return m
275 return m
276
276
277 def diff(self, m2, clean=False):
277 def diff(self, m2, clean=False):
278 '''Finds changes between the current manifest and m2.
278 '''Finds changes between the current manifest and m2.
279
279
280 Args:
280 Args:
281 m2: the manifest to which this manifest should be compared.
281 m2: the manifest to which this manifest should be compared.
282 clean: if true, include files unchanged between these manifests
282 clean: if true, include files unchanged between these manifests
283 with a None value in the returned dictionary.
283 with a None value in the returned dictionary.
284
284
285 The result is returned as a dict with filename as key and
285 The result is returned as a dict with filename as key and
286 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
286 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
287 nodeid in the current/other manifest and fl1/fl2 is the flag
287 nodeid in the current/other manifest and fl1/fl2 is the flag
288 in the current/other manifest. Where the file does not exist,
288 in the current/other manifest. Where the file does not exist,
289 the nodeid will be None and the flags will be the empty
289 the nodeid will be None and the flags will be the empty
290 string.
290 string.
291 '''
291 '''
292 return self._lm.diff(m2._lm, clean)
292 return self._lm.diff(m2._lm, clean)
293
293
294 def setflag(self, key, flag):
294 def setflag(self, key, flag):
295 self._lm[key] = self[key], flag
295 self._lm[key] = self[key], flag
296
296
297 def get(self, key, default=None):
297 def get(self, key, default=None):
298 try:
298 try:
299 return self._lm[key][0]
299 return self._lm[key][0]
300 except KeyError:
300 except KeyError:
301 return default
301 return default
302
302
303 def flags(self, key, default=''):
303 def flags(self, key, default=''):
304 try:
304 try:
305 return self._lm[key][1]
305 return self._lm[key][1]
306 except KeyError:
306 except KeyError:
307 return default
307 return default
308
308
309 def copy(self):
309 def copy(self):
310 c = manifestdict()
310 c = manifestdict()
311 c._lm = self._lm.copy()
311 c._lm = self._lm.copy()
312 return c
312 return c
313
313
314 def iteritems(self):
314 def iteritems(self):
315 return (x[:2] for x in self._lm.iterentries())
315 return (x[:2] for x in self._lm.iterentries())
316
316
317 def text(self, usemanifestv2=False):
317 def text(self, usemanifestv2=False):
318 if usemanifestv2:
318 if usemanifestv2:
319 return _textv2(self._lm.iterentries())
319 return _textv2(self._lm.iterentries())
320 else:
320 else:
321 # use (probably) native version for v1
321 # use (probably) native version for v1
322 return self._lm.text()
322 return self._lm.text()
323
323
324 def fastdelta(self, base, changes):
324 def fastdelta(self, base, changes):
325 """Given a base manifest text as an array.array and a list of changes
325 """Given a base manifest text as an array.array and a list of changes
326 relative to that text, compute a delta that can be used by revlog.
326 relative to that text, compute a delta that can be used by revlog.
327 """
327 """
328 delta = []
328 delta = []
329 dstart = None
329 dstart = None
330 dend = None
330 dend = None
331 dline = [""]
331 dline = [""]
332 start = 0
332 start = 0
333 # zero copy representation of base as a buffer
333 # zero copy representation of base as a buffer
334 addbuf = util.buffer(base)
334 addbuf = util.buffer(base)
335
335
336 # start with a readonly loop that finds the offset of
336 # start with a readonly loop that finds the offset of
337 # each line and creates the deltas
337 # each line and creates the deltas
338 for f, todelete in changes:
338 for f, todelete in changes:
339 # bs will either be the index of the item or the insert point
339 # bs will either be the index of the item or the insert point
340 start, end = _msearch(addbuf, f, start)
340 start, end = _msearch(addbuf, f, start)
341 if not todelete:
341 if not todelete:
342 h, fl = self._lm[f]
342 h, fl = self._lm[f]
343 l = "%s\0%s%s\n" % (f, revlog.hex(h), fl)
343 l = "%s\0%s%s\n" % (f, revlog.hex(h), fl)
344 else:
344 else:
345 if start == end:
345 if start == end:
346 # item we want to delete was not found, error out
346 # item we want to delete was not found, error out
347 raise AssertionError(
347 raise AssertionError(
348 _("failed to remove %s from manifest") % f)
348 _("failed to remove %s from manifest") % f)
349 l = ""
349 l = ""
350 if dstart is not None and dstart <= start and dend >= start:
350 if dstart is not None and dstart <= start and dend >= start:
351 if dend < end:
351 if dend < end:
352 dend = end
352 dend = end
353 if l:
353 if l:
354 dline.append(l)
354 dline.append(l)
355 else:
355 else:
356 if dstart is not None:
356 if dstart is not None:
357 delta.append([dstart, dend, "".join(dline)])
357 delta.append([dstart, dend, "".join(dline)])
358 dstart = start
358 dstart = start
359 dend = end
359 dend = end
360 dline = [l]
360 dline = [l]
361
361
362 if dstart is not None:
362 if dstart is not None:
363 delta.append([dstart, dend, "".join(dline)])
363 delta.append([dstart, dend, "".join(dline)])
364 # apply the delta to the base, and get a delta for addrevision
364 # apply the delta to the base, and get a delta for addrevision
365 deltatext, arraytext = _addlistdelta(base, delta)
365 deltatext, arraytext = _addlistdelta(base, delta)
366 return arraytext, deltatext
366 return arraytext, deltatext
367
367
368 def _msearch(m, s, lo=0, hi=None):
368 def _msearch(m, s, lo=0, hi=None):
369 '''return a tuple (start, end) that says where to find s within m.
369 '''return a tuple (start, end) that says where to find s within m.
370
370
371 If the string is found m[start:end] are the line containing
371 If the string is found m[start:end] are the line containing
372 that string. If start == end the string was not found and
372 that string. If start == end the string was not found and
373 they indicate the proper sorted insertion point.
373 they indicate the proper sorted insertion point.
374
374
375 m should be a buffer or a string
375 m should be a buffer or a string
376 s is a string'''
376 s is a string'''
377 def advance(i, c):
377 def advance(i, c):
378 while i < lenm and m[i] != c:
378 while i < lenm and m[i] != c:
379 i += 1
379 i += 1
380 return i
380 return i
381 if not s:
381 if not s:
382 return (lo, lo)
382 return (lo, lo)
383 lenm = len(m)
383 lenm = len(m)
384 if not hi:
384 if not hi:
385 hi = lenm
385 hi = lenm
386 while lo < hi:
386 while lo < hi:
387 mid = (lo + hi) // 2
387 mid = (lo + hi) // 2
388 start = mid
388 start = mid
389 while start > 0 and m[start - 1] != '\n':
389 while start > 0 and m[start - 1] != '\n':
390 start -= 1
390 start -= 1
391 end = advance(start, '\0')
391 end = advance(start, '\0')
392 if m[start:end] < s:
392 if m[start:end] < s:
393 # we know that after the null there are 40 bytes of sha1
393 # we know that after the null there are 40 bytes of sha1
394 # this translates to the bisect lo = mid + 1
394 # this translates to the bisect lo = mid + 1
395 lo = advance(end + 40, '\n') + 1
395 lo = advance(end + 40, '\n') + 1
396 else:
396 else:
397 # this translates to the bisect hi = mid
397 # this translates to the bisect hi = mid
398 hi = start
398 hi = start
399 end = advance(lo, '\0')
399 end = advance(lo, '\0')
400 found = m[lo:end]
400 found = m[lo:end]
401 if s == found:
401 if s == found:
402 # we know that after the null there are 40 bytes of sha1
402 # we know that after the null there are 40 bytes of sha1
403 end = advance(end + 40, '\n')
403 end = advance(end + 40, '\n')
404 return (lo, end + 1)
404 return (lo, end + 1)
405 else:
405 else:
406 return (lo, lo)
406 return (lo, lo)
407
407
408 def _checkforbidden(l):
408 def _checkforbidden(l):
409 """Check filenames for illegal characters."""
409 """Check filenames for illegal characters."""
410 for f in l:
410 for f in l:
411 if '\n' in f or '\r' in f:
411 if '\n' in f or '\r' in f:
412 raise error.RevlogError(
412 raise error.RevlogError(
413 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
413 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
414
414
415
415
416 # apply the changes collected during the bisect loop to our addlist
416 # apply the changes collected during the bisect loop to our addlist
417 # return a delta suitable for addrevision
417 # return a delta suitable for addrevision
418 def _addlistdelta(addlist, x):
418 def _addlistdelta(addlist, x):
419 # for large addlist arrays, building a new array is cheaper
419 # for large addlist arrays, building a new array is cheaper
420 # than repeatedly modifying the existing one
420 # than repeatedly modifying the existing one
421 currentposition = 0
421 currentposition = 0
422 newaddlist = array.array('c')
422 newaddlist = array.array('c')
423
423
424 for start, end, content in x:
424 for start, end, content in x:
425 newaddlist += addlist[currentposition:start]
425 newaddlist += addlist[currentposition:start]
426 if content:
426 if content:
427 newaddlist += array.array('c', content)
427 newaddlist += array.array('c', content)
428
428
429 currentposition = end
429 currentposition = end
430
430
431 newaddlist += addlist[currentposition:]
431 newaddlist += addlist[currentposition:]
432
432
433 deltatext = "".join(struct.pack(">lll", start, end, len(content))
433 deltatext = "".join(struct.pack(">lll", start, end, len(content))
434 + content for start, end, content in x)
434 + content for start, end, content in x)
435 return deltatext, newaddlist
435 return deltatext, newaddlist
436
436
437 def _splittopdir(f):
437 def _splittopdir(f):
438 if '/' in f:
438 if '/' in f:
439 dir, subpath = f.split('/', 1)
439 dir, subpath = f.split('/', 1)
440 return dir + '/', subpath
440 return dir + '/', subpath
441 else:
441 else:
442 return '', f
442 return '', f
443
443
444 class treemanifest(object):
444 class treemanifest(object):
445 def __init__(self, dir='', text=''):
445 def __init__(self, dir='', text=''):
446 self._dir = dir
446 self._dir = dir
447 self._node = revlog.nullid
447 self._node = revlog.nullid
448 self._dirs = {}
448 self._dirs = {}
449 # Using _lazymanifest here is a little slower than plain old dicts
449 # Using _lazymanifest here is a little slower than plain old dicts
450 self._files = {}
450 self._files = {}
451 self._flags = {}
451 self._flags = {}
452 def readsubtree(subdir, subm):
452 def readsubtree(subdir, subm):
453 raise AssertionError('treemanifest constructor only accepts '
453 raise AssertionError('treemanifest constructor only accepts '
454 'flat manifests')
454 'flat manifests')
455 self.parse(text, readsubtree)
455 self.parse(text, readsubtree)
456
456
457 def _subpath(self, path):
457 def _subpath(self, path):
458 return self._dir + path
458 return self._dir + path
459
459
460 def __len__(self):
460 def __len__(self):
461 size = len(self._files)
461 size = len(self._files)
462 for m in self._dirs.values():
462 for m in self._dirs.values():
463 size += m.__len__()
463 size += m.__len__()
464 return size
464 return size
465
465
466 def _isempty(self):
466 def _isempty(self):
467 return (not self._files and (not self._dirs or
467 return (not self._files and (not self._dirs or
468 util.all(m._isempty() for m in self._dirs.values())))
468 util.all(m._isempty() for m in self._dirs.values())))
469
469
470 def __str__(self):
470 def __str__(self):
471 return ('<treemanifest dir=%s, node=%s>' %
471 return ('<treemanifest dir=%s, node=%s>' %
472 (self._dir, revlog.hex(self._node)))
472 (self._dir, revlog.hex(self._node)))
473
473
474 def dir(self):
474 def dir(self):
475 '''The directory that this tree manifest represents, including a
475 '''The directory that this tree manifest represents, including a
476 trailing '/'. Empty string for the repo root directory.'''
476 trailing '/'. Empty string for the repo root directory.'''
477 return self._dir
477 return self._dir
478
478
479 def node(self):
479 def node(self):
480 '''This node of this instance. nullid for unsaved instances. Should
480 '''This node of this instance. nullid for unsaved instances. Should
481 be updated when the instance is read or written from a revlog.
481 be updated when the instance is read or written from a revlog.
482 '''
482 '''
483 return self._node
483 return self._node
484
484
485 def setnode(self, node):
485 def setnode(self, node):
486 self._node = node
486 self._node = node
487
487
488 def iteritems(self):
488 def iteritems(self):
489 for p, n in sorted(self._dirs.items() + self._files.items()):
489 for p, n in sorted(self._dirs.items() + self._files.items()):
490 if p in self._files:
490 if p in self._files:
491 yield self._subpath(p), n
491 yield self._subpath(p), n
492 else:
492 else:
493 for f, sn in n.iteritems():
493 for f, sn in n.iteritems():
494 yield f, sn
494 yield f, sn
495
495
496 def iterkeys(self):
496 def iterkeys(self):
497 for p in sorted(self._dirs.keys() + self._files.keys()):
497 for p in sorted(self._dirs.keys() + self._files.keys()):
498 if p in self._files:
498 if p in self._files:
499 yield self._subpath(p)
499 yield self._subpath(p)
500 else:
500 else:
501 for f in self._dirs[p].iterkeys():
501 for f in self._dirs[p].iterkeys():
502 yield f
502 yield f
503
503
504 def keys(self):
504 def keys(self):
505 return list(self.iterkeys())
505 return list(self.iterkeys())
506
506
507 def __iter__(self):
507 def __iter__(self):
508 return self.iterkeys()
508 return self.iterkeys()
509
509
510 def __contains__(self, f):
510 def __contains__(self, f):
511 if f is None:
511 if f is None:
512 return False
512 return False
513 dir, subpath = _splittopdir(f)
513 dir, subpath = _splittopdir(f)
514 if dir:
514 if dir:
515 if dir not in self._dirs:
515 if dir not in self._dirs:
516 return False
516 return False
517 return self._dirs[dir].__contains__(subpath)
517 return self._dirs[dir].__contains__(subpath)
518 else:
518 else:
519 return f in self._files
519 return f in self._files
520
520
521 def get(self, f, default=None):
521 def get(self, f, default=None):
522 dir, subpath = _splittopdir(f)
522 dir, subpath = _splittopdir(f)
523 if dir:
523 if dir:
524 if dir not in self._dirs:
524 if dir not in self._dirs:
525 return default
525 return default
526 return self._dirs[dir].get(subpath, default)
526 return self._dirs[dir].get(subpath, default)
527 else:
527 else:
528 return self._files.get(f, default)
528 return self._files.get(f, default)
529
529
530 def __getitem__(self, f):
530 def __getitem__(self, f):
531 dir, subpath = _splittopdir(f)
531 dir, subpath = _splittopdir(f)
532 if dir:
532 if dir:
533 return self._dirs[dir].__getitem__(subpath)
533 return self._dirs[dir].__getitem__(subpath)
534 else:
534 else:
535 return self._files[f]
535 return self._files[f]
536
536
537 def flags(self, f):
537 def flags(self, f):
538 dir, subpath = _splittopdir(f)
538 dir, subpath = _splittopdir(f)
539 if dir:
539 if dir:
540 if dir not in self._dirs:
540 if dir not in self._dirs:
541 return ''
541 return ''
542 return self._dirs[dir].flags(subpath)
542 return self._dirs[dir].flags(subpath)
543 else:
543 else:
544 if f in self._dirs:
544 if f in self._dirs:
545 return ''
545 return ''
546 return self._flags.get(f, '')
546 return self._flags.get(f, '')
547
547
548 def find(self, f):
548 def find(self, f):
549 dir, subpath = _splittopdir(f)
549 dir, subpath = _splittopdir(f)
550 if dir:
550 if dir:
551 return self._dirs[dir].find(subpath)
551 return self._dirs[dir].find(subpath)
552 else:
552 else:
553 return self._files[f], self._flags.get(f, '')
553 return self._files[f], self._flags.get(f, '')
554
554
555 def __delitem__(self, f):
555 def __delitem__(self, f):
556 dir, subpath = _splittopdir(f)
556 dir, subpath = _splittopdir(f)
557 if dir:
557 if dir:
558 self._dirs[dir].__delitem__(subpath)
558 self._dirs[dir].__delitem__(subpath)
559 # If the directory is now empty, remove it
559 # If the directory is now empty, remove it
560 if self._dirs[dir]._isempty():
560 if self._dirs[dir]._isempty():
561 del self._dirs[dir]
561 del self._dirs[dir]
562 else:
562 else:
563 del self._files[f]
563 del self._files[f]
564 if f in self._flags:
564 if f in self._flags:
565 del self._flags[f]
565 del self._flags[f]
566
566
567 def __setitem__(self, f, n):
567 def __setitem__(self, f, n):
568 assert n is not None
568 assert n is not None
569 dir, subpath = _splittopdir(f)
569 dir, subpath = _splittopdir(f)
570 if dir:
570 if dir:
571 if dir not in self._dirs:
571 if dir not in self._dirs:
572 self._dirs[dir] = treemanifest(self._subpath(dir))
572 self._dirs[dir] = treemanifest(self._subpath(dir))
573 self._dirs[dir].__setitem__(subpath, n)
573 self._dirs[dir].__setitem__(subpath, n)
574 else:
574 else:
575 self._files[f] = n[:21] # to match manifestdict's behavior
575 self._files[f] = n[:21] # to match manifestdict's behavior
576
576
577 def setflag(self, f, flags):
577 def setflag(self, f, flags):
578 """Set the flags (symlink, executable) for path f."""
578 """Set the flags (symlink, executable) for path f."""
579 assert 'd' not in flags
579 assert 'd' not in flags
580 dir, subpath = _splittopdir(f)
580 dir, subpath = _splittopdir(f)
581 if dir:
581 if dir:
582 if dir not in self._dirs:
582 if dir not in self._dirs:
583 self._dirs[dir] = treemanifest(self._subpath(dir))
583 self._dirs[dir] = treemanifest(self._subpath(dir))
584 self._dirs[dir].setflag(subpath, flags)
584 self._dirs[dir].setflag(subpath, flags)
585 else:
585 else:
586 self._flags[f] = flags
586 self._flags[f] = flags
587
587
588 def copy(self):
588 def copy(self):
589 copy = treemanifest(self._dir)
589 copy = treemanifest(self._dir)
590 copy._node = self._node
590 copy._node = self._node
591 for d in self._dirs:
591 for d in self._dirs:
592 copy._dirs[d] = self._dirs[d].copy()
592 copy._dirs[d] = self._dirs[d].copy()
593 copy._files = dict.copy(self._files)
593 copy._files = dict.copy(self._files)
594 copy._flags = dict.copy(self._flags)
594 copy._flags = dict.copy(self._flags)
595 return copy
595 return copy
596
596
597 def filesnotin(self, m2):
597 def filesnotin(self, m2):
598 '''Set of files in this manifest that are not in the other'''
598 '''Set of files in this manifest that are not in the other'''
599 files = set()
599 files = set()
600 def _filesnotin(t1, t2):
600 def _filesnotin(t1, t2):
601 for d, m1 in t1._dirs.iteritems():
601 for d, m1 in t1._dirs.iteritems():
602 if d in t2._dirs:
602 if d in t2._dirs:
603 m2 = t2._dirs[d]
603 m2 = t2._dirs[d]
604 _filesnotin(m1, m2)
604 _filesnotin(m1, m2)
605 else:
605 else:
606 files.update(m1.iterkeys())
606 files.update(m1.iterkeys())
607
607
608 for fn in t1._files.iterkeys():
608 for fn in t1._files.iterkeys():
609 if fn not in t2._files:
609 if fn not in t2._files:
610 files.add(t1._subpath(fn))
610 files.add(t1._subpath(fn))
611
611
612 _filesnotin(self, m2)
612 _filesnotin(self, m2)
613 return files
613 return files
614
614
615 @propertycache
615 @propertycache
616 def _alldirs(self):
616 def _alldirs(self):
617 return util.dirs(self)
617 return util.dirs(self)
618
618
619 def dirs(self):
619 def dirs(self):
620 return self._alldirs
620 return self._alldirs
621
621
622 def hasdir(self, dir):
622 def hasdir(self, dir):
623 topdir, subdir = _splittopdir(dir)
623 topdir, subdir = _splittopdir(dir)
624 if topdir:
624 if topdir:
625 if topdir in self._dirs:
625 if topdir in self._dirs:
626 return self._dirs[topdir].hasdir(subdir)
626 return self._dirs[topdir].hasdir(subdir)
627 return False
627 return False
628 return (dir + '/') in self._dirs
628 return (dir + '/') in self._dirs
629
629
630 def walk(self, match):
630 def walk(self, match):
631 '''Generates matching file names.
631 '''Generates matching file names.
632
632
633 Equivalent to manifest.matches(match).iterkeys(), but without creating
633 Equivalent to manifest.matches(match).iterkeys(), but without creating
634 an entirely new manifest.
634 an entirely new manifest.
635
635
636 It also reports nonexistent files by marking them bad with match.bad().
636 It also reports nonexistent files by marking them bad with match.bad().
637 '''
637 '''
638 if match.always():
638 if match.always():
639 for f in iter(self):
639 for f in iter(self):
640 yield f
640 yield f
641 return
641 return
642
642
643 fset = set(match.files())
643 fset = set(match.files())
644
644
645 for fn in self._walk(match):
645 for fn in self._walk(match):
646 if fn in fset:
646 if fn in fset:
647 # specified pattern is the exact name
647 # specified pattern is the exact name
648 fset.remove(fn)
648 fset.remove(fn)
649 yield fn
649 yield fn
650
650
651 # for dirstate.walk, files=['.'] means "walk the whole tree".
651 # for dirstate.walk, files=['.'] means "walk the whole tree".
652 # follow that here, too
652 # follow that here, too
653 fset.discard('.')
653 fset.discard('.')
654
654
655 for fn in sorted(fset):
655 for fn in sorted(fset):
656 if not self.hasdir(fn):
656 if not self.hasdir(fn):
657 match.bad(fn, None)
657 match.bad(fn, None)
658
658
659 def _walk(self, match, alldirs=False):
659 def _walk(self, match, alldirs=False):
660 '''Recursively generates matching file names for walk().
660 '''Recursively generates matching file names for walk().
661
661
662 Will visit all subdirectories if alldirs is True, otherwise it will
662 Will visit all subdirectories if alldirs is True, otherwise it will
663 only visit subdirectories for which match.visitdir is True.'''
663 only visit subdirectories for which match.visitdir is True.'''
664
664
665 if not alldirs:
665 if not alldirs:
666 # substring to strip trailing slash
666 # substring to strip trailing slash
667 visit = match.visitdir(self._dir[:-1] or '.')
667 visit = match.visitdir(self._dir[:-1] or '.')
668 if not visit:
668 if not visit:
669 return
669 return
670 alldirs = (visit == 'all')
670 alldirs = (visit == 'all')
671
671
672 # yield this dir's files and walk its submanifests
672 # yield this dir's files and walk its submanifests
673 for p in sorted(self._dirs.keys() + self._files.keys()):
673 for p in sorted(self._dirs.keys() + self._files.keys()):
674 if p in self._files:
674 if p in self._files:
675 fullp = self._subpath(p)
675 fullp = self._subpath(p)
676 if match(fullp):
676 if match(fullp):
677 yield fullp
677 yield fullp
678 else:
678 else:
679 for f in self._dirs[p]._walk(match, alldirs):
679 for f in self._dirs[p]._walk(match, alldirs):
680 yield f
680 yield f
681
681
682 def matches(self, match):
682 def matches(self, match):
683 '''generate a new manifest filtered by the match argument'''
683 '''generate a new manifest filtered by the match argument'''
684 if match.always():
684 if match.always():
685 return self.copy()
685 return self.copy()
686
686
687 return self._matches(match)
687 return self._matches(match)
688
688
689 def _matches(self, match, alldirs=False):
689 def _matches(self, match, alldirs=False):
690 '''recursively generate a new manifest filtered by the match argument.
690 '''recursively generate a new manifest filtered by the match argument.
691
691
692 Will visit all subdirectories if alldirs is True, otherwise it will
692 Will visit all subdirectories if alldirs is True, otherwise it will
693 only visit subdirectories for which match.visitdir is True.'''
693 only visit subdirectories for which match.visitdir is True.'''
694
694
695 ret = treemanifest(self._dir)
695 ret = treemanifest(self._dir)
696 if not alldirs:
696 if not alldirs:
697 # substring to strip trailing slash
697 # substring to strip trailing slash
698 visit = match.visitdir(self._dir[:-1] or '.')
698 visit = match.visitdir(self._dir[:-1] or '.')
699 if not visit:
699 if not visit:
700 return ret
700 return ret
701 alldirs = (visit == 'all')
701 alldirs = (visit == 'all')
702
702
703 for fn in self._files:
703 for fn in self._files:
704 fullp = self._subpath(fn)
704 fullp = self._subpath(fn)
705 if not match(fullp):
705 if not match(fullp):
706 continue
706 continue
707 ret._files[fn] = self._files[fn]
707 ret._files[fn] = self._files[fn]
708 if fn in self._flags:
708 if fn in self._flags:
709 ret._flags[fn] = self._flags[fn]
709 ret._flags[fn] = self._flags[fn]
710
710
711 for dir, subm in self._dirs.iteritems():
711 for dir, subm in self._dirs.iteritems():
712 m = subm._matches(match, alldirs)
712 m = subm._matches(match, alldirs)
713 if not m._isempty():
713 if not m._isempty():
714 ret._dirs[dir] = m
714 ret._dirs[dir] = m
715
715
716 return ret
716 return ret
717
717
718 def diff(self, m2, clean=False):
718 def diff(self, m2, clean=False):
719 '''Finds changes between the current manifest and m2.
719 '''Finds changes between the current manifest and m2.
720
720
721 Args:
721 Args:
722 m2: the manifest to which this manifest should be compared.
722 m2: the manifest to which this manifest should be compared.
723 clean: if true, include files unchanged between these manifests
723 clean: if true, include files unchanged between these manifests
724 with a None value in the returned dictionary.
724 with a None value in the returned dictionary.
725
725
726 The result is returned as a dict with filename as key and
726 The result is returned as a dict with filename as key and
727 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
727 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
728 nodeid in the current/other manifest and fl1/fl2 is the flag
728 nodeid in the current/other manifest and fl1/fl2 is the flag
729 in the current/other manifest. Where the file does not exist,
729 in the current/other manifest. Where the file does not exist,
730 the nodeid will be None and the flags will be the empty
730 the nodeid will be None and the flags will be the empty
731 string.
731 string.
732 '''
732 '''
733 result = {}
733 result = {}
734 emptytree = treemanifest()
734 emptytree = treemanifest()
735 def _diff(t1, t2):
735 def _diff(t1, t2):
736 for d, m1 in t1._dirs.iteritems():
736 for d, m1 in t1._dirs.iteritems():
737 m2 = t2._dirs.get(d, emptytree)
737 m2 = t2._dirs.get(d, emptytree)
738 _diff(m1, m2)
738 _diff(m1, m2)
739
739
740 for d, m2 in t2._dirs.iteritems():
740 for d, m2 in t2._dirs.iteritems():
741 if d not in t1._dirs:
741 if d not in t1._dirs:
742 _diff(emptytree, m2)
742 _diff(emptytree, m2)
743
743
744 for fn, n1 in t1._files.iteritems():
744 for fn, n1 in t1._files.iteritems():
745 fl1 = t1._flags.get(fn, '')
745 fl1 = t1._flags.get(fn, '')
746 n2 = t2._files.get(fn, None)
746 n2 = t2._files.get(fn, None)
747 fl2 = t2._flags.get(fn, '')
747 fl2 = t2._flags.get(fn, '')
748 if n1 != n2 or fl1 != fl2:
748 if n1 != n2 or fl1 != fl2:
749 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
749 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
750 elif clean:
750 elif clean:
751 result[t1._subpath(fn)] = None
751 result[t1._subpath(fn)] = None
752
752
753 for fn, n2 in t2._files.iteritems():
753 for fn, n2 in t2._files.iteritems():
754 if fn not in t1._files:
754 if fn not in t1._files:
755 fl2 = t2._flags.get(fn, '')
755 fl2 = t2._flags.get(fn, '')
756 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
756 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
757
757
758 _diff(self, m2)
758 _diff(self, m2)
759 return result
759 return result
760
760
761 def parse(self, text, readsubtree):
761 def parse(self, text, readsubtree):
762 for f, n, fl in _parse(text):
762 for f, n, fl in _parse(text):
763 if fl == 'd':
763 if fl == 'd':
764 f = f + '/'
764 f = f + '/'
765 self._dirs[f] = readsubtree(self._subpath(f), n)
765 self._dirs[f] = readsubtree(self._subpath(f), n)
766 else:
766 else:
767 # Use __setitem__ and setflag rather than assigning directly
767 # Use __setitem__ and setflag rather than assigning directly
768 # to _files and _flags, thereby letting us parse flat manifests
768 # to _files and _flags, thereby letting us parse flat manifests
769 # as well as tree manifests.
769 # as well as tree manifests.
770 self[f] = n
770 self[f] = n
771 if fl:
771 if fl:
772 self.setflag(f, fl)
772 self.setflag(f, fl)
773
773
774 def text(self, usemanifestv2=False):
774 def text(self, usemanifestv2=False):
775 """Get the full data of this manifest as a bytestring."""
775 """Get the full data of this manifest as a bytestring."""
776 flags = self.flags
776 flags = self.flags
777 return _text(((f, self[f], flags(f)) for f in self.keys()),
777 return _text(((f, self[f], flags(f)) for f in self.keys()),
778 usemanifestv2)
778 usemanifestv2)
779
779
780 def dirtext(self, usemanifestv2=False):
780 def dirtext(self, usemanifestv2=False):
781 """Get the full data of this directory as a bytestring. Make sure that
781 """Get the full data of this directory as a bytestring. Make sure that
782 any submanifests have been written first, so their nodeids are correct.
782 any submanifests have been written first, so their nodeids are correct.
783 """
783 """
784 flags = self.flags
784 flags = self.flags
785 dirs = [(d[:-1], self._dirs[d]._node, 'd') for d in self._dirs]
785 dirs = [(d[:-1], self._dirs[d]._node, 'd') for d in self._dirs]
786 files = [(f, self._files[f], flags(f)) for f in self._files]
786 files = [(f, self._files[f], flags(f)) for f in self._files]
787 return _text(sorted(dirs + files), usemanifestv2)
787 return _text(sorted(dirs + files), usemanifestv2)
788
788
789 def writesubtrees(self, m1, m2, writesubtree):
789 def writesubtrees(self, m1, m2, writesubtree):
790 emptytree = treemanifest()
790 emptytree = treemanifest()
791 for d, subm in self._dirs.iteritems():
791 for d, subm in self._dirs.iteritems():
792 subp1 = m1._dirs.get(d, emptytree)._node
792 subp1 = m1._dirs.get(d, emptytree)._node
793 subp2 = m2._dirs.get(d, emptytree)._node
793 subp2 = m2._dirs.get(d, emptytree)._node
794 if subp1 == revlog.nullid:
794 if subp1 == revlog.nullid:
795 subp1, subp2 = subp2, subp1
795 subp1, subp2 = subp2, subp1
796 writesubtree(subm, subp1, subp2)
796 writesubtree(subm, subp1, subp2)
797
797
798 class manifest(revlog.revlog):
798 class manifest(revlog.revlog):
799 def __init__(self, opener, dir=''):
799 def __init__(self, opener, dir=''):
800 # During normal operations, we expect to deal with not more than four
800 # During normal operations, we expect to deal with not more than four
801 # revs at a time (such as during commit --amend). When rebasing large
801 # revs at a time (such as during commit --amend). When rebasing large
802 # stacks of commits, the number can go up, hence the config knob below.
802 # stacks of commits, the number can go up, hence the config knob below.
803 cachesize = 4
803 cachesize = 4
804 usetreemanifest = False
804 usetreemanifest = False
805 usemanifestv2 = False
805 usemanifestv2 = False
806 opts = getattr(opener, 'options', None)
806 opts = getattr(opener, 'options', None)
807 if opts is not None:
807 if opts is not None:
808 cachesize = opts.get('manifestcachesize', cachesize)
808 cachesize = opts.get('manifestcachesize', cachesize)
809 usetreemanifest = opts.get('treemanifest', usetreemanifest)
809 usetreemanifest = opts.get('treemanifest', usetreemanifest)
810 usemanifestv2 = opts.get('manifestv2', usemanifestv2)
810 usemanifestv2 = opts.get('manifestv2', usemanifestv2)
811 self._mancache = util.lrucachedict(cachesize)
811 self._mancache = util.lrucachedict(cachesize)
812 self._treeinmem = usetreemanifest
812 self._treeinmem = usetreemanifest
813 self._treeondisk = usetreemanifest
813 self._treeondisk = usetreemanifest
814 self._usemanifestv2 = usemanifestv2
814 self._usemanifestv2 = usemanifestv2
815 indexfile = "00manifest.i"
815 indexfile = "00manifest.i"
816 if dir:
816 if dir:
817 assert self._treeondisk
817 assert self._treeondisk
818 if not dir.endswith('/'):
819 dir = dir + '/'
818 indexfile = "meta/" + dir + "00manifest.i"
820 indexfile = "meta/" + dir + "00manifest.i"
819 revlog.revlog.__init__(self, opener, indexfile)
821 revlog.revlog.__init__(self, opener, indexfile)
820 self._dir = dir
822 self._dir = dir
821
823
822 def _newmanifest(self, data=''):
824 def _newmanifest(self, data=''):
823 if self._treeinmem:
825 if self._treeinmem:
824 return treemanifest(self._dir, data)
826 return treemanifest(self._dir, data)
825 return manifestdict(data)
827 return manifestdict(data)
826
828
827 def _slowreaddelta(self, node):
829 def _slowreaddelta(self, node):
828 r0 = self.deltaparent(self.rev(node))
830 r0 = self.deltaparent(self.rev(node))
829 m0 = self.read(self.node(r0))
831 m0 = self.read(self.node(r0))
830 m1 = self.read(node)
832 m1 = self.read(node)
831 md = self._newmanifest()
833 md = self._newmanifest()
832 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
834 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
833 if n1:
835 if n1:
834 md[f] = n1
836 md[f] = n1
835 if fl1:
837 if fl1:
836 md.setflag(f, fl1)
838 md.setflag(f, fl1)
837 return md
839 return md
838
840
839 def readdelta(self, node):
841 def readdelta(self, node):
840 if self._usemanifestv2 or self._treeondisk:
842 if self._usemanifestv2 or self._treeondisk:
841 return self._slowreaddelta(node)
843 return self._slowreaddelta(node)
842 r = self.rev(node)
844 r = self.rev(node)
843 d = mdiff.patchtext(self.revdiff(self.deltaparent(r), r))
845 d = mdiff.patchtext(self.revdiff(self.deltaparent(r), r))
844 return self._newmanifest(d)
846 return self._newmanifest(d)
845
847
846 def readfast(self, node):
848 def readfast(self, node):
847 '''use the faster of readdelta or read
849 '''use the faster of readdelta or read
848
850
849 This will return a manifest which is either only the files
851 This will return a manifest which is either only the files
850 added/modified relative to p1, or all files in the
852 added/modified relative to p1, or all files in the
851 manifest. Which one is returned depends on the codepath used
853 manifest. Which one is returned depends on the codepath used
852 to retrieve the data.
854 to retrieve the data.
853 '''
855 '''
854 r = self.rev(node)
856 r = self.rev(node)
855 deltaparent = self.deltaparent(r)
857 deltaparent = self.deltaparent(r)
856 if deltaparent != revlog.nullrev and deltaparent in self.parentrevs(r):
858 if deltaparent != revlog.nullrev and deltaparent in self.parentrevs(r):
857 return self.readdelta(node)
859 return self.readdelta(node)
858 return self.read(node)
860 return self.read(node)
859
861
860 def read(self, node):
862 def read(self, node):
861 if node == revlog.nullid:
863 if node == revlog.nullid:
862 return self._newmanifest() # don't upset local cache
864 return self._newmanifest() # don't upset local cache
863 if node in self._mancache:
865 if node in self._mancache:
864 return self._mancache[node][0]
866 return self._mancache[node][0]
865 text = self.revision(node)
867 text = self.revision(node)
866 if self._treeondisk:
868 if self._treeondisk:
867 def readsubtree(dir, subm):
869 def readsubtree(dir, subm):
868 sublog = manifest(self.opener, dir)
870 sublog = manifest(self.opener, dir)
869 return sublog.read(subm)
871 return sublog.read(subm)
870 m = self._newmanifest()
872 m = self._newmanifest()
871 m.parse(text, readsubtree)
873 m.parse(text, readsubtree)
872 m.setnode(node)
874 m.setnode(node)
873 arraytext = None
875 arraytext = None
874 else:
876 else:
875 m = self._newmanifest(text)
877 m = self._newmanifest(text)
876 arraytext = array.array('c', text)
878 arraytext = array.array('c', text)
877 self._mancache[node] = (m, arraytext)
879 self._mancache[node] = (m, arraytext)
878 return m
880 return m
879
881
880 def find(self, node, f):
882 def find(self, node, f):
881 '''look up entry for a single file efficiently.
883 '''look up entry for a single file efficiently.
882 return (node, flags) pair if found, (None, None) if not.'''
884 return (node, flags) pair if found, (None, None) if not.'''
883 m = self.read(node)
885 m = self.read(node)
884 try:
886 try:
885 return m.find(f)
887 return m.find(f)
886 except KeyError:
888 except KeyError:
887 return None, None
889 return None, None
888
890
889 def add(self, m, transaction, link, p1, p2, added, removed):
891 def add(self, m, transaction, link, p1, p2, added, removed):
890 if (p1 in self._mancache and not self._treeinmem
892 if (p1 in self._mancache and not self._treeinmem
891 and not self._usemanifestv2):
893 and not self._usemanifestv2):
892 # If our first parent is in the manifest cache, we can
894 # If our first parent is in the manifest cache, we can
893 # compute a delta here using properties we know about the
895 # compute a delta here using properties we know about the
894 # manifest up-front, which may save time later for the
896 # manifest up-front, which may save time later for the
895 # revlog layer.
897 # revlog layer.
896
898
897 _checkforbidden(added)
899 _checkforbidden(added)
898 # combine the changed lists into one list for sorting
900 # combine the changed lists into one list for sorting
899 work = [(x, False) for x in added]
901 work = [(x, False) for x in added]
900 work.extend((x, True) for x in removed)
902 work.extend((x, True) for x in removed)
901 # this could use heapq.merge() (from Python 2.6+) or equivalent
903 # this could use heapq.merge() (from Python 2.6+) or equivalent
902 # since the lists are already sorted
904 # since the lists are already sorted
903 work.sort()
905 work.sort()
904
906
905 arraytext, deltatext = m.fastdelta(self._mancache[p1][1], work)
907 arraytext, deltatext = m.fastdelta(self._mancache[p1][1], work)
906 cachedelta = self.rev(p1), deltatext
908 cachedelta = self.rev(p1), deltatext
907 text = util.buffer(arraytext)
909 text = util.buffer(arraytext)
908 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
910 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
909 else:
911 else:
910 # The first parent manifest isn't already loaded, so we'll
912 # The first parent manifest isn't already loaded, so we'll
911 # just encode a fulltext of the manifest and pass that
913 # just encode a fulltext of the manifest and pass that
912 # through to the revlog layer, and let it handle the delta
914 # through to the revlog layer, and let it handle the delta
913 # process.
915 # process.
914 if self._treeondisk:
916 if self._treeondisk:
915 m1 = self.read(p1)
917 m1 = self.read(p1)
916 m2 = self.read(p2)
918 m2 = self.read(p2)
917 n = self._addtree(m, transaction, link, m1, m2)
919 n = self._addtree(m, transaction, link, m1, m2)
918 arraytext = None
920 arraytext = None
919 else:
921 else:
920 text = m.text(self._usemanifestv2)
922 text = m.text(self._usemanifestv2)
921 n = self.addrevision(text, transaction, link, p1, p2)
923 n = self.addrevision(text, transaction, link, p1, p2)
922 arraytext = array.array('c', text)
924 arraytext = array.array('c', text)
923
925
924 self._mancache[n] = (m, arraytext)
926 self._mancache[n] = (m, arraytext)
925
927
926 return n
928 return n
927
929
928 def _addtree(self, m, transaction, link, m1, m2):
930 def _addtree(self, m, transaction, link, m1, m2):
929 def writesubtree(subm, subp1, subp2):
931 def writesubtree(subm, subp1, subp2):
930 sublog = manifest(self.opener, subm.dir())
932 sublog = manifest(self.opener, subm.dir())
931 sublog.add(subm, transaction, link, subp1, subp2, None, None)
933 sublog.add(subm, transaction, link, subp1, subp2, None, None)
932 m.writesubtrees(m1, m2, writesubtree)
934 m.writesubtrees(m1, m2, writesubtree)
933 text = m.dirtext(self._usemanifestv2)
935 text = m.dirtext(self._usemanifestv2)
934 # If the manifest is unchanged compared to one parent,
936 # If the manifest is unchanged compared to one parent,
935 # don't write a new revision
937 # don't write a new revision
936 if text == m1.dirtext(self._usemanifestv2):
938 if text == m1.dirtext(self._usemanifestv2):
937 n = m1.node()
939 n = m1.node()
938 elif text == m2.dirtext(self._usemanifestv2):
940 elif text == m2.dirtext(self._usemanifestv2):
939 n = m2.node()
941 n = m2.node()
940 else:
942 else:
941 n = self.addrevision(text, transaction, link, m1.node(), m2.node())
943 n = self.addrevision(text, transaction, link, m1.node(), m2.node())
942 # Save nodeid so parent manifest can calculate its nodeid
944 # Save nodeid so parent manifest can calculate its nodeid
943 m.setnode(n)
945 m.setnode(n)
944 return n
946 return n
@@ -1,338 +1,338 b''
1 Show all commands except debug commands
1 Show all commands except debug commands
2 $ hg debugcomplete
2 $ hg debugcomplete
3 add
3 add
4 addremove
4 addremove
5 annotate
5 annotate
6 archive
6 archive
7 backout
7 backout
8 bisect
8 bisect
9 bookmarks
9 bookmarks
10 branch
10 branch
11 branches
11 branches
12 bundle
12 bundle
13 cat
13 cat
14 clone
14 clone
15 commit
15 commit
16 config
16 config
17 copy
17 copy
18 diff
18 diff
19 export
19 export
20 files
20 files
21 forget
21 forget
22 graft
22 graft
23 grep
23 grep
24 heads
24 heads
25 help
25 help
26 identify
26 identify
27 import
27 import
28 incoming
28 incoming
29 init
29 init
30 locate
30 locate
31 log
31 log
32 manifest
32 manifest
33 merge
33 merge
34 outgoing
34 outgoing
35 parents
35 parents
36 paths
36 paths
37 phase
37 phase
38 pull
38 pull
39 push
39 push
40 recover
40 recover
41 remove
41 remove
42 rename
42 rename
43 resolve
43 resolve
44 revert
44 revert
45 rollback
45 rollback
46 root
46 root
47 serve
47 serve
48 status
48 status
49 summary
49 summary
50 tag
50 tag
51 tags
51 tags
52 tip
52 tip
53 unbundle
53 unbundle
54 update
54 update
55 verify
55 verify
56 version
56 version
57
57
58 Show all commands that start with "a"
58 Show all commands that start with "a"
59 $ hg debugcomplete a
59 $ hg debugcomplete a
60 add
60 add
61 addremove
61 addremove
62 annotate
62 annotate
63 archive
63 archive
64
64
65 Do not show debug commands if there are other candidates
65 Do not show debug commands if there are other candidates
66 $ hg debugcomplete d
66 $ hg debugcomplete d
67 diff
67 diff
68
68
69 Show debug commands if there are no other candidates
69 Show debug commands if there are no other candidates
70 $ hg debugcomplete debug
70 $ hg debugcomplete debug
71 debugancestor
71 debugancestor
72 debugbuilddag
72 debugbuilddag
73 debugbundle
73 debugbundle
74 debugcheckstate
74 debugcheckstate
75 debugcommands
75 debugcommands
76 debugcomplete
76 debugcomplete
77 debugconfig
77 debugconfig
78 debugdag
78 debugdag
79 debugdata
79 debugdata
80 debugdate
80 debugdate
81 debugdirstate
81 debugdirstate
82 debugdiscovery
82 debugdiscovery
83 debugfileset
83 debugfileset
84 debugfsinfo
84 debugfsinfo
85 debuggetbundle
85 debuggetbundle
86 debugignore
86 debugignore
87 debugindex
87 debugindex
88 debugindexdot
88 debugindexdot
89 debuginstall
89 debuginstall
90 debugknown
90 debugknown
91 debuglabelcomplete
91 debuglabelcomplete
92 debuglocks
92 debuglocks
93 debugnamecomplete
93 debugnamecomplete
94 debugobsolete
94 debugobsolete
95 debugpathcomplete
95 debugpathcomplete
96 debugpushkey
96 debugpushkey
97 debugpvec
97 debugpvec
98 debugrebuilddirstate
98 debugrebuilddirstate
99 debugrename
99 debugrename
100 debugrevlog
100 debugrevlog
101 debugrevspec
101 debugrevspec
102 debugsetparents
102 debugsetparents
103 debugsub
103 debugsub
104 debugsuccessorssets
104 debugsuccessorssets
105 debugwalk
105 debugwalk
106 debugwireargs
106 debugwireargs
107
107
108 Do not show the alias of a debug command if there are other candidates
108 Do not show the alias of a debug command if there are other candidates
109 (this should hide rawcommit)
109 (this should hide rawcommit)
110 $ hg debugcomplete r
110 $ hg debugcomplete r
111 recover
111 recover
112 remove
112 remove
113 rename
113 rename
114 resolve
114 resolve
115 revert
115 revert
116 rollback
116 rollback
117 root
117 root
118 Show the alias of a debug command if there are no other candidates
118 Show the alias of a debug command if there are no other candidates
119 $ hg debugcomplete rawc
119 $ hg debugcomplete rawc
120
120
121
121
122 Show the global options
122 Show the global options
123 $ hg debugcomplete --options | sort
123 $ hg debugcomplete --options | sort
124 --config
124 --config
125 --cwd
125 --cwd
126 --debug
126 --debug
127 --debugger
127 --debugger
128 --encoding
128 --encoding
129 --encodingmode
129 --encodingmode
130 --help
130 --help
131 --hidden
131 --hidden
132 --noninteractive
132 --noninteractive
133 --profile
133 --profile
134 --quiet
134 --quiet
135 --repository
135 --repository
136 --time
136 --time
137 --traceback
137 --traceback
138 --verbose
138 --verbose
139 --version
139 --version
140 -R
140 -R
141 -h
141 -h
142 -q
142 -q
143 -v
143 -v
144 -y
144 -y
145
145
146 Show the options for the "serve" command
146 Show the options for the "serve" command
147 $ hg debugcomplete --options serve | sort
147 $ hg debugcomplete --options serve | sort
148 --accesslog
148 --accesslog
149 --address
149 --address
150 --certificate
150 --certificate
151 --cmdserver
151 --cmdserver
152 --config
152 --config
153 --cwd
153 --cwd
154 --daemon
154 --daemon
155 --daemon-pipefds
155 --daemon-pipefds
156 --debug
156 --debug
157 --debugger
157 --debugger
158 --encoding
158 --encoding
159 --encodingmode
159 --encodingmode
160 --errorlog
160 --errorlog
161 --help
161 --help
162 --hidden
162 --hidden
163 --ipv6
163 --ipv6
164 --name
164 --name
165 --noninteractive
165 --noninteractive
166 --pid-file
166 --pid-file
167 --port
167 --port
168 --prefix
168 --prefix
169 --profile
169 --profile
170 --quiet
170 --quiet
171 --repository
171 --repository
172 --stdio
172 --stdio
173 --style
173 --style
174 --templates
174 --templates
175 --time
175 --time
176 --traceback
176 --traceback
177 --verbose
177 --verbose
178 --version
178 --version
179 --web-conf
179 --web-conf
180 -6
180 -6
181 -A
181 -A
182 -E
182 -E
183 -R
183 -R
184 -a
184 -a
185 -d
185 -d
186 -h
186 -h
187 -n
187 -n
188 -p
188 -p
189 -q
189 -q
190 -t
190 -t
191 -v
191 -v
192 -y
192 -y
193
193
194 Show an error if we use --options with an ambiguous abbreviation
194 Show an error if we use --options with an ambiguous abbreviation
195 $ hg debugcomplete --options s
195 $ hg debugcomplete --options s
196 hg: command 's' is ambiguous:
196 hg: command 's' is ambiguous:
197 serve showconfig status summary
197 serve showconfig status summary
198 [255]
198 [255]
199
199
200 Show all commands + options
200 Show all commands + options
201 $ hg debugcommands
201 $ hg debugcommands
202 add: include, exclude, subrepos, dry-run
202 add: include, exclude, subrepos, dry-run
203 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, ignore-all-space, ignore-space-change, ignore-blank-lines, include, exclude, template
203 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, ignore-all-space, ignore-space-change, ignore-blank-lines, include, exclude, template
204 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
204 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
205 commit: addremove, close-branch, amend, secret, edit, interactive, include, exclude, message, logfile, date, user, subrepos
205 commit: addremove, close-branch, amend, secret, edit, interactive, include, exclude, message, logfile, date, user, subrepos
206 diff: rev, change, text, git, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, root, include, exclude, subrepos
206 diff: rev, change, text, git, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, root, include, exclude, subrepos
207 export: output, switch-parent, rev, text, git, nodates
207 export: output, switch-parent, rev, text, git, nodates
208 forget: include, exclude
208 forget: include, exclude
209 init: ssh, remotecmd, insecure
209 init: ssh, remotecmd, insecure
210 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
210 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
211 merge: force, rev, preview, tool
211 merge: force, rev, preview, tool
212 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
212 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
213 push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
213 push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
214 remove: after, force, subrepos, include, exclude
214 remove: after, force, subrepos, include, exclude
215 serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate
215 serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate
216 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos, template
216 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos, template
217 summary: remote
217 summary: remote
218 update: clean, check, date, rev, tool
218 update: clean, check, date, rev, tool
219 addremove: similarity, subrepos, include, exclude, dry-run
219 addremove: similarity, subrepos, include, exclude, dry-run
220 archive: no-decode, prefix, rev, type, subrepos, include, exclude
220 archive: no-decode, prefix, rev, type, subrepos, include, exclude
221 backout: merge, commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
221 backout: merge, commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
222 bisect: reset, good, bad, skip, extend, command, noupdate
222 bisect: reset, good, bad, skip, extend, command, noupdate
223 bookmarks: force, rev, delete, rename, inactive, template
223 bookmarks: force, rev, delete, rename, inactive, template
224 branch: force, clean
224 branch: force, clean
225 branches: active, closed, template
225 branches: active, closed, template
226 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
226 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
227 cat: output, rev, decode, include, exclude
227 cat: output, rev, decode, include, exclude
228 config: untrusted, edit, local, global
228 config: untrusted, edit, local, global
229 copy: after, force, include, exclude, dry-run
229 copy: after, force, include, exclude, dry-run
230 debugancestor:
230 debugancestor:
231 debugbuilddag: mergeable-file, overwritten-file, new-file
231 debugbuilddag: mergeable-file, overwritten-file, new-file
232 debugbundle: all
232 debugbundle: all
233 debugcheckstate:
233 debugcheckstate:
234 debugcommands:
234 debugcommands:
235 debugcomplete: options
235 debugcomplete: options
236 debugdag: tags, branches, dots, spaces
236 debugdag: tags, branches, dots, spaces
237 debugdata: changelog, manifest
237 debugdata: changelog, manifest, dir
238 debugdate: extended
238 debugdate: extended
239 debugdirstate: nodates, datesort
239 debugdirstate: nodates, datesort
240 debugdiscovery: old, nonheads, ssh, remotecmd, insecure
240 debugdiscovery: old, nonheads, ssh, remotecmd, insecure
241 debugfileset: rev
241 debugfileset: rev
242 debugfsinfo:
242 debugfsinfo:
243 debuggetbundle: head, common, type
243 debuggetbundle: head, common, type
244 debugignore:
244 debugignore:
245 debugindex: changelog, manifest, format
245 debugindex: changelog, manifest, dir, format
246 debugindexdot:
246 debugindexdot:
247 debuginstall:
247 debuginstall:
248 debugknown:
248 debugknown:
249 debuglabelcomplete:
249 debuglabelcomplete:
250 debuglocks: force-lock, force-wlock
250 debuglocks: force-lock, force-wlock
251 debugnamecomplete:
251 debugnamecomplete:
252 debugobsolete: flags, record-parents, rev, date, user
252 debugobsolete: flags, record-parents, rev, date, user
253 debugpathcomplete: full, normal, added, removed
253 debugpathcomplete: full, normal, added, removed
254 debugpushkey:
254 debugpushkey:
255 debugpvec:
255 debugpvec:
256 debugrebuilddirstate: rev
256 debugrebuilddirstate: rev
257 debugrename: rev
257 debugrename: rev
258 debugrevlog: changelog, manifest, dump
258 debugrevlog: changelog, manifest, dir, dump
259 debugrevspec: optimize
259 debugrevspec: optimize
260 debugsetparents:
260 debugsetparents:
261 debugsub: rev
261 debugsub: rev
262 debugsuccessorssets:
262 debugsuccessorssets:
263 debugwalk: include, exclude
263 debugwalk: include, exclude
264 debugwireargs: three, four, five, ssh, remotecmd, insecure
264 debugwireargs: three, four, five, ssh, remotecmd, insecure
265 files: rev, print0, include, exclude, template, subrepos
265 files: rev, print0, include, exclude, template, subrepos
266 graft: rev, continue, edit, log, force, currentdate, currentuser, date, user, tool, dry-run
266 graft: rev, continue, edit, log, force, currentdate, currentuser, date, user, tool, dry-run
267 grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
267 grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
268 heads: rev, topo, active, closed, style, template
268 heads: rev, topo, active, closed, style, template
269 help: extension, command, keyword
269 help: extension, command, keyword
270 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure
270 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure
271 import: strip, base, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
271 import: strip, base, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
272 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
272 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
273 locate: rev, print0, fullpath, include, exclude
273 locate: rev, print0, fullpath, include, exclude
274 manifest: rev, all, template
274 manifest: rev, all, template
275 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
275 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
276 parents: rev, style, template
276 parents: rev, style, template
277 paths:
277 paths:
278 phase: public, draft, secret, force, rev
278 phase: public, draft, secret, force, rev
279 recover:
279 recover:
280 rename: after, force, include, exclude, dry-run
280 rename: after, force, include, exclude, dry-run
281 resolve: all, list, mark, unmark, no-status, tool, include, exclude, template
281 resolve: all, list, mark, unmark, no-status, tool, include, exclude, template
282 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
282 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
283 rollback: dry-run, force
283 rollback: dry-run, force
284 root:
284 root:
285 tag: force, local, rev, remove, edit, message, date, user
285 tag: force, local, rev, remove, edit, message, date, user
286 tags: template
286 tags: template
287 tip: patch, git, style, template
287 tip: patch, git, style, template
288 unbundle: update
288 unbundle: update
289 verify:
289 verify:
290 version:
290 version:
291
291
292 $ hg init a
292 $ hg init a
293 $ cd a
293 $ cd a
294 $ echo fee > fee
294 $ echo fee > fee
295 $ hg ci -q -Amfee
295 $ hg ci -q -Amfee
296 $ hg tag fee
296 $ hg tag fee
297 $ mkdir fie
297 $ mkdir fie
298 $ echo dead > fie/dead
298 $ echo dead > fie/dead
299 $ echo live > fie/live
299 $ echo live > fie/live
300 $ hg bookmark fo
300 $ hg bookmark fo
301 $ hg branch -q fie
301 $ hg branch -q fie
302 $ hg ci -q -Amfie
302 $ hg ci -q -Amfie
303 $ echo fo > fo
303 $ echo fo > fo
304 $ hg branch -qf default
304 $ hg branch -qf default
305 $ hg ci -q -Amfo
305 $ hg ci -q -Amfo
306 $ echo Fum > Fum
306 $ echo Fum > Fum
307 $ hg ci -q -AmFum
307 $ hg ci -q -AmFum
308 $ hg bookmark Fum
308 $ hg bookmark Fum
309
309
310 Test debugpathcomplete
310 Test debugpathcomplete
311
311
312 $ hg debugpathcomplete f
312 $ hg debugpathcomplete f
313 fee
313 fee
314 fie
314 fie
315 fo
315 fo
316 $ hg debugpathcomplete -f f
316 $ hg debugpathcomplete -f f
317 fee
317 fee
318 fie/dead
318 fie/dead
319 fie/live
319 fie/live
320 fo
320 fo
321
321
322 $ hg rm Fum
322 $ hg rm Fum
323 $ hg debugpathcomplete -r F
323 $ hg debugpathcomplete -r F
324 Fum
324 Fum
325
325
326 Test debugnamecomplete
326 Test debugnamecomplete
327
327
328 $ hg debugnamecomplete
328 $ hg debugnamecomplete
329 Fum
329 Fum
330 default
330 default
331 fee
331 fee
332 fie
332 fie
333 fo
333 fo
334 tip
334 tip
335 $ hg debugnamecomplete f
335 $ hg debugnamecomplete f
336 fee
336 fee
337 fie
337 fie
338 fo
338 fo
@@ -1,278 +1,278 b''
1
1
2 Set up repo
2 Set up repo
3
3
4 $ hg --config experimental.treemanifest=True init repo
4 $ hg --config experimental.treemanifest=True init repo
5 $ cd repo
5 $ cd repo
6
6
7 Requirements get set on init
7 Requirements get set on init
8
8
9 $ grep treemanifest .hg/requires
9 $ grep treemanifest .hg/requires
10 treemanifest
10 treemanifest
11
11
12 Without directories, looks like any other repo
12 Without directories, looks like any other repo
13
13
14 $ echo 0 > a
14 $ echo 0 > a
15 $ echo 0 > b
15 $ echo 0 > b
16 $ hg ci -Aqm initial
16 $ hg ci -Aqm initial
17 $ hg debugdata -m 0
17 $ hg debugdata -m 0
18 a\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
18 a\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
19 b\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
19 b\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
20
20
21 Submanifest is stored in separate revlog
21 Submanifest is stored in separate revlog
22
22
23 $ mkdir dir1
23 $ mkdir dir1
24 $ echo 1 > dir1/a
24 $ echo 1 > dir1/a
25 $ echo 1 > dir1/b
25 $ echo 1 > dir1/b
26 $ echo 1 > e
26 $ echo 1 > e
27 $ hg ci -Aqm 'add dir1'
27 $ hg ci -Aqm 'add dir1'
28 $ hg debugdata -m 1
28 $ hg debugdata -m 1
29 a\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
29 a\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
30 b\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
30 b\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
31 dir1\x008b3ffd73f901e83304c83d33132c8e774ceac44ed (esc)
31 dir1\x008b3ffd73f901e83304c83d33132c8e774ceac44ed (esc)
32 e\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
32 e\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
33 $ hg debugdata .hg/store/meta/dir1/00manifest.i 0
33 $ hg debugdata --dir dir1 0
34 a\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
34 a\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
35 b\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
35 b\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
36
36
37 Can add nested directories
37 Can add nested directories
38
38
39 $ mkdir dir1/dir1
39 $ mkdir dir1/dir1
40 $ echo 2 > dir1/dir1/a
40 $ echo 2 > dir1/dir1/a
41 $ echo 2 > dir1/dir1/b
41 $ echo 2 > dir1/dir1/b
42 $ mkdir dir1/dir2
42 $ mkdir dir1/dir2
43 $ echo 2 > dir1/dir2/a
43 $ echo 2 > dir1/dir2/a
44 $ echo 2 > dir1/dir2/b
44 $ echo 2 > dir1/dir2/b
45 $ hg ci -Aqm 'add dir1/dir1'
45 $ hg ci -Aqm 'add dir1/dir1'
46 $ hg files -r .
46 $ hg files -r .
47 a
47 a
48 b
48 b
49 dir1/a
49 dir1/a
50 dir1/b
50 dir1/b
51 dir1/dir1/a
51 dir1/dir1/a
52 dir1/dir1/b
52 dir1/dir1/b
53 dir1/dir2/a
53 dir1/dir2/a
54 dir1/dir2/b
54 dir1/dir2/b
55 e
55 e
56
56
57 Revision is not created for unchanged directory
57 Revision is not created for unchanged directory
58
58
59 $ mkdir dir2
59 $ mkdir dir2
60 $ echo 3 > dir2/a
60 $ echo 3 > dir2/a
61 $ hg add dir2
61 $ hg add dir2
62 adding dir2/a
62 adding dir2/a
63 $ hg debugindex .hg/store/meta/dir1/00manifest.i > before
63 $ hg debugindex --dir dir1 > before
64 $ hg ci -qm 'add dir2'
64 $ hg ci -qm 'add dir2'
65 $ hg debugindex .hg/store/meta/dir1/00manifest.i > after
65 $ hg debugindex --dir dir1 > after
66 $ diff before after
66 $ diff before after
67 $ rm before after
67 $ rm before after
68
68
69 Removing directory does not create an revlog entry
69 Removing directory does not create an revlog entry
70
70
71 $ hg rm dir1/dir1
71 $ hg rm dir1/dir1
72 removing dir1/dir1/a
72 removing dir1/dir1/a
73 removing dir1/dir1/b
73 removing dir1/dir1/b
74 $ hg debugindex .hg/store/meta/dir1/dir1/00manifest.i > before
74 $ hg debugindex --dir dir1/dir1 > before
75 $ hg ci -qm 'remove dir1/dir1'
75 $ hg ci -qm 'remove dir1/dir1'
76 $ hg debugindex .hg/store/meta/dir1/dir1/00manifest.i > after
76 $ hg debugindex --dir dir1/dir1 > after
77 $ diff before after
77 $ diff before after
78 $ rm before after
78 $ rm before after
79
79
80 Check that hg files (calls treemanifest.walk()) works
80 Check that hg files (calls treemanifest.walk()) works
81
81
82 $ hg co 'desc("add dir2")'
82 $ hg co 'desc("add dir2")'
83 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
83 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 $ hg files -r . dir1
84 $ hg files -r . dir1
85 dir1/a
85 dir1/a
86 dir1/b
86 dir1/b
87 dir1/dir1/a
87 dir1/dir1/a
88 dir1/dir1/b
88 dir1/dir1/b
89 dir1/dir2/a
89 dir1/dir2/a
90 dir1/dir2/b
90 dir1/dir2/b
91
91
92 Check that status between revisions works (calls treemanifest.matches())
92 Check that status between revisions works (calls treemanifest.matches())
93
93
94 $ hg status --rev 'desc("add dir1")' --rev . dir1
94 $ hg status --rev 'desc("add dir1")' --rev . dir1
95 A dir1/dir1/a
95 A dir1/dir1/a
96 A dir1/dir1/b
96 A dir1/dir1/b
97 A dir1/dir2/a
97 A dir1/dir2/a
98 A dir1/dir2/b
98 A dir1/dir2/b
99
99
100 Merge creates 2-parent revision of directory revlog
100 Merge creates 2-parent revision of directory revlog
101
101
102 $ echo 5 > dir1/a
102 $ echo 5 > dir1/a
103 $ hg ci -Aqm 'modify dir1/a'
103 $ hg ci -Aqm 'modify dir1/a'
104 $ hg co '.^'
104 $ hg co '.^'
105 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
105 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
106 $ echo 6 > dir1/b
106 $ echo 6 > dir1/b
107 $ hg ci -Aqm 'modify dir1/b'
107 $ hg ci -Aqm 'modify dir1/b'
108 $ hg merge 'desc("modify dir1/a")'
108 $ hg merge 'desc("modify dir1/a")'
109 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
110 (branch merge, don't forget to commit)
110 (branch merge, don't forget to commit)
111 $ hg ci -m 'conflict-free merge involving dir1/'
111 $ hg ci -m 'conflict-free merge involving dir1/'
112 $ cat dir1/a
112 $ cat dir1/a
113 5
113 5
114 $ cat dir1/b
114 $ cat dir1/b
115 6
115 6
116 $ hg debugindex .hg/store/meta/dir1/00manifest.i
116 $ hg debugindex --dir dir1
117 rev offset length base linkrev nodeid p1 p2
117 rev offset length base linkrev nodeid p1 p2
118 0 0 54 0 1 8b3ffd73f901 000000000000 000000000000
118 0 0 54 0 1 8b3ffd73f901 000000000000 000000000000
119 1 54 68 0 2 b66d046c644f 8b3ffd73f901 000000000000
119 1 54 68 0 2 b66d046c644f 8b3ffd73f901 000000000000
120 2 122 12 0 4 b87265673c8a b66d046c644f 000000000000
120 2 122 12 0 4 b87265673c8a b66d046c644f 000000000000
121 3 134 95 0 5 aa5d3adcec72 b66d046c644f 000000000000
121 3 134 95 0 5 aa5d3adcec72 b66d046c644f 000000000000
122 4 229 81 0 6 e29b066b91ad b66d046c644f 000000000000
122 4 229 81 0 6 e29b066b91ad b66d046c644f 000000000000
123 5 310 107 5 7 a120ce2b83f5 e29b066b91ad aa5d3adcec72
123 5 310 107 5 7 a120ce2b83f5 e29b066b91ad aa5d3adcec72
124
124
125 Merge keeping directory from parent 1 does not create revlog entry. (Note that
125 Merge keeping directory from parent 1 does not create revlog entry. (Note that
126 dir1's manifest does change, but only because dir1/a's filelog changes.)
126 dir1's manifest does change, but only because dir1/a's filelog changes.)
127
127
128 $ hg co 'desc("add dir2")'
128 $ hg co 'desc("add dir2")'
129 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
129 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
130 $ echo 8 > dir2/a
130 $ echo 8 > dir2/a
131 $ hg ci -m 'modify dir2/a'
131 $ hg ci -m 'modify dir2/a'
132 created new head
132 created new head
133
133
134 $ hg debugindex .hg/store/meta/dir2/00manifest.i > before
134 $ hg debugindex --dir dir2 > before
135 $ hg merge 'desc("modify dir1/a")'
135 $ hg merge 'desc("modify dir1/a")'
136 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
136 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
137 (branch merge, don't forget to commit)
137 (branch merge, don't forget to commit)
138 $ hg revert -r 'desc("modify dir2/a")' .
138 $ hg revert -r 'desc("modify dir2/a")' .
139 reverting dir1/a (glob)
139 reverting dir1/a (glob)
140 $ hg ci -m 'merge, keeping parent 1'
140 $ hg ci -m 'merge, keeping parent 1'
141 $ hg debugindex .hg/store/meta/dir2/00manifest.i > after
141 $ hg debugindex --dir dir2 > after
142 $ diff before after
142 $ diff before after
143 $ rm before after
143 $ rm before after
144
144
145 Merge keeping directory from parent 2 does not create revlog entry. (Note that
145 Merge keeping directory from parent 2 does not create revlog entry. (Note that
146 dir2's manifest does change, but only because dir2/a's filelog changes.)
146 dir2's manifest does change, but only because dir2/a's filelog changes.)
147
147
148 $ hg co 'desc("modify dir2/a")'
148 $ hg co 'desc("modify dir2/a")'
149 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
149 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 $ hg debugindex .hg/store/meta/dir1/00manifest.i > before
150 $ hg debugindex --dir dir1 > before
151 $ hg merge 'desc("modify dir1/a")'
151 $ hg merge 'desc("modify dir1/a")'
152 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
152 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
153 (branch merge, don't forget to commit)
153 (branch merge, don't forget to commit)
154 $ hg revert -r 'desc("modify dir1/a")' .
154 $ hg revert -r 'desc("modify dir1/a")' .
155 reverting dir2/a (glob)
155 reverting dir2/a (glob)
156 $ hg ci -m 'merge, keeping parent 2'
156 $ hg ci -m 'merge, keeping parent 2'
157 created new head
157 created new head
158 $ hg debugindex .hg/store/meta/dir1/00manifest.i > after
158 $ hg debugindex --dir dir1 > after
159 $ diff before after
159 $ diff before after
160 $ rm before after
160 $ rm before after
161
161
162 Create flat source repo for tests with mixed flat/tree manifests
162 Create flat source repo for tests with mixed flat/tree manifests
163
163
164 $ cd ..
164 $ cd ..
165 $ hg init repo-flat
165 $ hg init repo-flat
166 $ cd repo-flat
166 $ cd repo-flat
167
167
168 Create a few commits with flat manifest
168 Create a few commits with flat manifest
169
169
170 $ echo 0 > a
170 $ echo 0 > a
171 $ echo 0 > b
171 $ echo 0 > b
172 $ echo 0 > e
172 $ echo 0 > e
173 $ for d in dir1 dir1/dir1 dir1/dir2 dir2
173 $ for d in dir1 dir1/dir1 dir1/dir2 dir2
174 > do
174 > do
175 > mkdir $d
175 > mkdir $d
176 > echo 0 > $d/a
176 > echo 0 > $d/a
177 > echo 0 > $d/b
177 > echo 0 > $d/b
178 > done
178 > done
179 $ hg ci -Aqm initial
179 $ hg ci -Aqm initial
180
180
181 $ echo 1 > a
181 $ echo 1 > a
182 $ echo 1 > dir1/a
182 $ echo 1 > dir1/a
183 $ echo 1 > dir1/dir1/a
183 $ echo 1 > dir1/dir1/a
184 $ hg ci -Aqm 'modify on branch 1'
184 $ hg ci -Aqm 'modify on branch 1'
185
185
186 $ hg co 0
186 $ hg co 0
187 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
187 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
188 $ echo 2 > b
188 $ echo 2 > b
189 $ echo 2 > dir1/b
189 $ echo 2 > dir1/b
190 $ echo 2 > dir1/dir1/b
190 $ echo 2 > dir1/dir1/b
191 $ hg ci -Aqm 'modify on branch 2'
191 $ hg ci -Aqm 'modify on branch 2'
192
192
193 $ hg merge 1
193 $ hg merge 1
194 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
194 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
195 (branch merge, don't forget to commit)
195 (branch merge, don't forget to commit)
196 $ hg ci -m 'merge of flat manifests to new flat manifest'
196 $ hg ci -m 'merge of flat manifests to new flat manifest'
197
197
198 Create clone with tree manifests enabled
198 Create clone with tree manifests enabled
199
199
200 $ cd ..
200 $ cd ..
201 $ hg clone --pull --config experimental.treemanifest=1 repo-flat repo-mixed
201 $ hg clone --pull --config experimental.treemanifest=1 repo-flat repo-mixed
202 requesting all changes
202 requesting all changes
203 adding changesets
203 adding changesets
204 adding manifests
204 adding manifests
205 adding file changes
205 adding file changes
206 added 4 changesets with 17 changes to 11 files
206 added 4 changesets with 17 changes to 11 files
207 updating to branch default
207 updating to branch default
208 11 files updated, 0 files merged, 0 files removed, 0 files unresolved
208 11 files updated, 0 files merged, 0 files removed, 0 files unresolved
209 $ cd repo-mixed
209 $ cd repo-mixed
210 $ test -f .hg/store/meta
210 $ test -f .hg/store/meta
211 [1]
211 [1]
212 $ grep treemanifest .hg/requires
212 $ grep treemanifest .hg/requires
213 treemanifest
213 treemanifest
214
214
215 Commit should store revlog per directory
215 Commit should store revlog per directory
216
216
217 $ hg co 1
217 $ hg co 1
218 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
218 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
219 $ echo 3 > a
219 $ echo 3 > a
220 $ echo 3 > dir1/a
220 $ echo 3 > dir1/a
221 $ echo 3 > dir1/dir1/a
221 $ echo 3 > dir1/dir1/a
222 $ hg ci -m 'first tree'
222 $ hg ci -m 'first tree'
223 created new head
223 created new head
224 $ find .hg/store/meta | sort
224 $ find .hg/store/meta | sort
225 .hg/store/meta
225 .hg/store/meta
226 .hg/store/meta/dir1
226 .hg/store/meta/dir1
227 .hg/store/meta/dir1/00manifest.i
227 .hg/store/meta/dir1/00manifest.i
228 .hg/store/meta/dir1/dir1
228 .hg/store/meta/dir1/dir1
229 .hg/store/meta/dir1/dir1/00manifest.i
229 .hg/store/meta/dir1/dir1/00manifest.i
230 .hg/store/meta/dir1/dir2
230 .hg/store/meta/dir1/dir2
231 .hg/store/meta/dir1/dir2/00manifest.i
231 .hg/store/meta/dir1/dir2/00manifest.i
232 .hg/store/meta/dir2
232 .hg/store/meta/dir2
233 .hg/store/meta/dir2/00manifest.i
233 .hg/store/meta/dir2/00manifest.i
234
234
235 Merge of two trees
235 Merge of two trees
236
236
237 $ hg co 2
237 $ hg co 2
238 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
238 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
239 $ hg merge 1
239 $ hg merge 1
240 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
240 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
241 (branch merge, don't forget to commit)
241 (branch merge, don't forget to commit)
242 $ hg ci -m 'merge of flat manifests to new tree manifest'
242 $ hg ci -m 'merge of flat manifests to new tree manifest'
243 created new head
243 created new head
244 $ hg diff -r 3
244 $ hg diff -r 3
245
245
246 Parent of tree root manifest should be flat manifest, and two for merge
246 Parent of tree root manifest should be flat manifest, and two for merge
247
247
248 $ hg debugindex -m
248 $ hg debugindex -m
249 rev offset length base linkrev nodeid p1 p2
249 rev offset length base linkrev nodeid p1 p2
250 0 0 80 0 0 40536115ed9e 000000000000 000000000000
250 0 0 80 0 0 40536115ed9e 000000000000 000000000000
251 1 80 83 0 1 f3376063c255 40536115ed9e 000000000000
251 1 80 83 0 1 f3376063c255 40536115ed9e 000000000000
252 2 163 103 0 2 5d9b9da231a2 40536115ed9e 000000000000
252 2 163 103 0 2 5d9b9da231a2 40536115ed9e 000000000000
253 3 266 83 0 3 d17d663cbd8a 5d9b9da231a2 f3376063c255
253 3 266 83 0 3 d17d663cbd8a 5d9b9da231a2 f3376063c255
254 4 349 132 4 4 c05a51345f86 f3376063c255 000000000000
254 4 349 132 4 4 c05a51345f86 f3376063c255 000000000000
255 5 481 110 4 5 82594b1f557d 5d9b9da231a2 f3376063c255
255 5 481 110 4 5 82594b1f557d 5d9b9da231a2 f3376063c255
256
256
257
257
258 Status across flat/tree boundary should work
258 Status across flat/tree boundary should work
259
259
260 $ hg status --rev '.^' --rev .
260 $ hg status --rev '.^' --rev .
261 M a
261 M a
262 M dir1/a
262 M dir1/a
263 M dir1/dir1/a
263 M dir1/dir1/a
264
264
265
265
266 Turning off treemanifest config has no effect
266 Turning off treemanifest config has no effect
267
267
268 $ hg debugindex .hg/store/meta/dir1/00manifest.i
268 $ hg debugindex .hg/store/meta/dir1/00manifest.i
269 rev offset length base linkrev nodeid p1 p2
269 rev offset length base linkrev nodeid p1 p2
270 0 0 125 0 4 63c9c0557d24 000000000000 000000000000
270 0 0 125 0 4 63c9c0557d24 000000000000 000000000000
271 1 125 109 0 5 23d12a1f6e0e 000000000000 000000000000
271 1 125 109 0 5 23d12a1f6e0e 000000000000 000000000000
272 $ echo 2 > dir1/a
272 $ echo 2 > dir1/a
273 $ hg --config experimental.treemanifest=False ci -qm 'modify dir1/a'
273 $ hg --config experimental.treemanifest=False ci -qm 'modify dir1/a'
274 $ hg debugindex .hg/store/meta/dir1/00manifest.i
274 $ hg debugindex .hg/store/meta/dir1/00manifest.i
275 rev offset length base linkrev nodeid p1 p2
275 rev offset length base linkrev nodeid p1 p2
276 0 0 125 0 4 63c9c0557d24 000000000000 000000000000
276 0 0 125 0 4 63c9c0557d24 000000000000 000000000000
277 1 125 109 0 5 23d12a1f6e0e 000000000000 000000000000
277 1 125 109 0 5 23d12a1f6e0e 000000000000 000000000000
278 2 234 55 0 6 3cb2d87b4250 23d12a1f6e0e 000000000000
278 2 234 55 0 6 3cb2d87b4250 23d12a1f6e0e 000000000000
General Comments 0
You need to be logged in to leave comments. Login now