##// END OF EJS Templates
subrepo: add basic support to hgsubrepo for the files command...
Matt Harbison -
r24413:a8595176 default
parent child Browse files
Show More
@@ -1,3231 +1,3242 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 import crecord as crecordmod
19 import crecord as crecordmod
20
20
21 def parsealiases(cmd):
21 def parsealiases(cmd):
22 return cmd.lstrip("^").split("|")
22 return cmd.lstrip("^").split("|")
23
23
24 def setupwrapcolorwrite(ui):
24 def setupwrapcolorwrite(ui):
25 # wrap ui.write so diff output can be labeled/colorized
25 # wrap ui.write so diff output can be labeled/colorized
26 def wrapwrite(orig, *args, **kw):
26 def wrapwrite(orig, *args, **kw):
27 label = kw.pop('label', '')
27 label = kw.pop('label', '')
28 for chunk, l in patch.difflabel(lambda: args):
28 for chunk, l in patch.difflabel(lambda: args):
29 orig(chunk, label=label + l)
29 orig(chunk, label=label + l)
30
30
31 oldwrite = ui.write
31 oldwrite = ui.write
32 def wrap(*args, **kwargs):
32 def wrap(*args, **kwargs):
33 return wrapwrite(oldwrite, *args, **kwargs)
33 return wrapwrite(oldwrite, *args, **kwargs)
34 setattr(ui, 'write', wrap)
34 setattr(ui, 'write', wrap)
35 return oldwrite
35 return oldwrite
36
36
37 def filterchunks(ui, originalhunks, usecurses, testfile):
37 def filterchunks(ui, originalhunks, usecurses, testfile):
38 if usecurses:
38 if usecurses:
39 if testfile:
39 if testfile:
40 recordfn = crecordmod.testdecorator(testfile,
40 recordfn = crecordmod.testdecorator(testfile,
41 crecordmod.testchunkselector)
41 crecordmod.testchunkselector)
42 else:
42 else:
43 recordfn = crecordmod.chunkselector
43 recordfn = crecordmod.chunkselector
44
44
45 return crecordmod.filterpatch(ui, originalhunks, recordfn)
45 return crecordmod.filterpatch(ui, originalhunks, recordfn)
46
46
47 else:
47 else:
48 return patch.filterpatch(ui, originalhunks)
48 return patch.filterpatch(ui, originalhunks)
49
49
50 def recordfilter(ui, originalhunks):
50 def recordfilter(ui, originalhunks):
51 usecurses = ui.configbool('experimental', 'crecord', False)
51 usecurses = ui.configbool('experimental', 'crecord', False)
52 testfile = ui.config('experimental', 'crecordtest', None)
52 testfile = ui.config('experimental', 'crecordtest', None)
53 oldwrite = setupwrapcolorwrite(ui)
53 oldwrite = setupwrapcolorwrite(ui)
54 try:
54 try:
55 newchunks = filterchunks(ui, originalhunks, usecurses, testfile)
55 newchunks = filterchunks(ui, originalhunks, usecurses, testfile)
56 finally:
56 finally:
57 ui.write = oldwrite
57 ui.write = oldwrite
58 return newchunks
58 return newchunks
59
59
60 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
60 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
61 filterfn, *pats, **opts):
61 filterfn, *pats, **opts):
62 import merge as mergemod
62 import merge as mergemod
63 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
63 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
64 ishunk = lambda x: isinstance(x, hunkclasses)
64 ishunk = lambda x: isinstance(x, hunkclasses)
65
65
66 if not ui.interactive():
66 if not ui.interactive():
67 raise util.Abort(_('running non-interactively, use %s instead') %
67 raise util.Abort(_('running non-interactively, use %s instead') %
68 cmdsuggest)
68 cmdsuggest)
69
69
70 # make sure username is set before going interactive
70 # make sure username is set before going interactive
71 if not opts.get('user'):
71 if not opts.get('user'):
72 ui.username() # raise exception, username not provided
72 ui.username() # raise exception, username not provided
73
73
74 def recordfunc(ui, repo, message, match, opts):
74 def recordfunc(ui, repo, message, match, opts):
75 """This is generic record driver.
75 """This is generic record driver.
76
76
77 Its job is to interactively filter local changes, and
77 Its job is to interactively filter local changes, and
78 accordingly prepare working directory into a state in which the
78 accordingly prepare working directory into a state in which the
79 job can be delegated to a non-interactive commit command such as
79 job can be delegated to a non-interactive commit command such as
80 'commit' or 'qrefresh'.
80 'commit' or 'qrefresh'.
81
81
82 After the actual job is done by non-interactive command, the
82 After the actual job is done by non-interactive command, the
83 working directory is restored to its original state.
83 working directory is restored to its original state.
84
84
85 In the end we'll record interesting changes, and everything else
85 In the end we'll record interesting changes, and everything else
86 will be left in place, so the user can continue working.
86 will be left in place, so the user can continue working.
87 """
87 """
88
88
89 checkunfinished(repo, commit=True)
89 checkunfinished(repo, commit=True)
90 merge = len(repo[None].parents()) > 1
90 merge = len(repo[None].parents()) > 1
91 if merge:
91 if merge:
92 raise util.Abort(_('cannot partially commit a merge '
92 raise util.Abort(_('cannot partially commit a merge '
93 '(use "hg commit" instead)'))
93 '(use "hg commit" instead)'))
94
94
95 status = repo.status(match=match)
95 status = repo.status(match=match)
96 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
96 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
97 diffopts.nodates = True
97 diffopts.nodates = True
98 diffopts.git = True
98 diffopts.git = True
99 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
99 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
100 originalchunks = patch.parsepatch(originaldiff)
100 originalchunks = patch.parsepatch(originaldiff)
101
101
102 # 1. filter patch, so we have intending-to apply subset of it
102 # 1. filter patch, so we have intending-to apply subset of it
103 try:
103 try:
104 chunks = filterfn(ui, originalchunks)
104 chunks = filterfn(ui, originalchunks)
105 except patch.PatchError, err:
105 except patch.PatchError, err:
106 raise util.Abort(_('error parsing patch: %s') % err)
106 raise util.Abort(_('error parsing patch: %s') % err)
107
107
108 contenders = set()
108 contenders = set()
109 for h in chunks:
109 for h in chunks:
110 try:
110 try:
111 contenders.update(set(h.files()))
111 contenders.update(set(h.files()))
112 except AttributeError:
112 except AttributeError:
113 pass
113 pass
114
114
115 changed = status.modified + status.added + status.removed
115 changed = status.modified + status.added + status.removed
116 newfiles = [f for f in changed if f in contenders]
116 newfiles = [f for f in changed if f in contenders]
117 if not newfiles:
117 if not newfiles:
118 ui.status(_('no changes to record\n'))
118 ui.status(_('no changes to record\n'))
119 return 0
119 return 0
120
120
121 newandmodifiedfiles = set()
121 newandmodifiedfiles = set()
122 for h in chunks:
122 for h in chunks:
123 isnew = h.filename() in status.added
123 isnew = h.filename() in status.added
124 if ishunk(h) and isnew and not h in originalchunks:
124 if ishunk(h) and isnew and not h in originalchunks:
125 newandmodifiedfiles.add(h.filename())
125 newandmodifiedfiles.add(h.filename())
126
126
127 modified = set(status.modified)
127 modified = set(status.modified)
128
128
129 # 2. backup changed files, so we can restore them in the end
129 # 2. backup changed files, so we can restore them in the end
130
130
131 if backupall:
131 if backupall:
132 tobackup = changed
132 tobackup = changed
133 else:
133 else:
134 tobackup = [f for f in newfiles
134 tobackup = [f for f in newfiles
135 if f in modified or f in newandmodifiedfiles]
135 if f in modified or f in newandmodifiedfiles]
136
136
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 or fname in newandmodifiedfiles:
159 if fname in backups or fname in newandmodifiedfiles:
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(c) for c in newandmodifiedfiles]
164 [os.unlink(c) for c in newandmodifiedfiles]
165
165
166 # 3a. apply filtered patch to clean repo (clean)
166 # 3a. apply filtered patch to clean repo (clean)
167 if backups:
167 if backups:
168 # Equivalent to hg.revert
168 # Equivalent to hg.revert
169 choices = lambda key: key in backups
169 choices = lambda key: key in backups
170 mergemod.update(repo, repo.dirstate.p1(),
170 mergemod.update(repo, repo.dirstate.p1(),
171 False, True, choices)
171 False, True, choices)
172
172
173
173
174 # 3b. (apply)
174 # 3b. (apply)
175 if dopatch:
175 if dopatch:
176 try:
176 try:
177 ui.debug('applying patch\n')
177 ui.debug('applying patch\n')
178 ui.debug(fp.getvalue())
178 ui.debug(fp.getvalue())
179 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
179 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
180 except patch.PatchError, err:
180 except patch.PatchError, err:
181 raise util.Abort(str(err))
181 raise util.Abort(str(err))
182 del fp
182 del fp
183
183
184 # 4. We prepared working directory according to filtered
184 # 4. We prepared working directory according to filtered
185 # patch. Now is the time to delegate the job to
185 # patch. Now is the time to delegate the job to
186 # commit/qrefresh or the like!
186 # commit/qrefresh or the like!
187
187
188 # Make all of the pathnames absolute.
188 # Make all of the pathnames absolute.
189 newfiles = [repo.wjoin(nf) for nf in newfiles]
189 newfiles = [repo.wjoin(nf) for nf in newfiles]
190 commitfunc(ui, repo, *newfiles, **opts)
190 commitfunc(ui, repo, *newfiles, **opts)
191
191
192 return 0
192 return 0
193 finally:
193 finally:
194 # 5. finally restore backed-up files
194 # 5. finally restore backed-up files
195 try:
195 try:
196 for realname, tmpname in backups.iteritems():
196 for realname, tmpname in backups.iteritems():
197 ui.debug('restoring %r to %r\n' % (tmpname, realname))
197 ui.debug('restoring %r to %r\n' % (tmpname, realname))
198 util.copyfile(tmpname, repo.wjoin(realname))
198 util.copyfile(tmpname, repo.wjoin(realname))
199 # Our calls to copystat() here and above are a
199 # Our calls to copystat() here and above are a
200 # hack to trick any editors that have f open that
200 # hack to trick any editors that have f open that
201 # we haven't modified them.
201 # we haven't modified them.
202 #
202 #
203 # Also note that this racy as an editor could
203 # Also note that this racy as an editor could
204 # notice the file's mtime before we've finished
204 # notice the file's mtime before we've finished
205 # writing it.
205 # writing it.
206 shutil.copystat(tmpname, repo.wjoin(realname))
206 shutil.copystat(tmpname, repo.wjoin(realname))
207 os.unlink(tmpname)
207 os.unlink(tmpname)
208 if tobackup:
208 if tobackup:
209 os.rmdir(backupdir)
209 os.rmdir(backupdir)
210 except OSError:
210 except OSError:
211 pass
211 pass
212
212
213 return commit(ui, repo, recordfunc, pats, opts)
213 return commit(ui, repo, recordfunc, pats, opts)
214
214
215 def findpossible(cmd, table, strict=False):
215 def findpossible(cmd, table, strict=False):
216 """
216 """
217 Return cmd -> (aliases, command table entry)
217 Return cmd -> (aliases, command table entry)
218 for each matching command.
218 for each matching command.
219 Return debug commands (or their aliases) only if no normal command matches.
219 Return debug commands (or their aliases) only if no normal command matches.
220 """
220 """
221 choice = {}
221 choice = {}
222 debugchoice = {}
222 debugchoice = {}
223
223
224 if cmd in table:
224 if cmd in table:
225 # short-circuit exact matches, "log" alias beats "^log|history"
225 # short-circuit exact matches, "log" alias beats "^log|history"
226 keys = [cmd]
226 keys = [cmd]
227 else:
227 else:
228 keys = table.keys()
228 keys = table.keys()
229
229
230 allcmds = []
230 allcmds = []
231 for e in keys:
231 for e in keys:
232 aliases = parsealiases(e)
232 aliases = parsealiases(e)
233 allcmds.extend(aliases)
233 allcmds.extend(aliases)
234 found = None
234 found = None
235 if cmd in aliases:
235 if cmd in aliases:
236 found = cmd
236 found = cmd
237 elif not strict:
237 elif not strict:
238 for a in aliases:
238 for a in aliases:
239 if a.startswith(cmd):
239 if a.startswith(cmd):
240 found = a
240 found = a
241 break
241 break
242 if found is not None:
242 if found is not None:
243 if aliases[0].startswith("debug") or found.startswith("debug"):
243 if aliases[0].startswith("debug") or found.startswith("debug"):
244 debugchoice[found] = (aliases, table[e])
244 debugchoice[found] = (aliases, table[e])
245 else:
245 else:
246 choice[found] = (aliases, table[e])
246 choice[found] = (aliases, table[e])
247
247
248 if not choice and debugchoice:
248 if not choice and debugchoice:
249 choice = debugchoice
249 choice = debugchoice
250
250
251 return choice, allcmds
251 return choice, allcmds
252
252
253 def findcmd(cmd, table, strict=True):
253 def findcmd(cmd, table, strict=True):
254 """Return (aliases, command table entry) for command string."""
254 """Return (aliases, command table entry) for command string."""
255 choice, allcmds = findpossible(cmd, table, strict)
255 choice, allcmds = findpossible(cmd, table, strict)
256
256
257 if cmd in choice:
257 if cmd in choice:
258 return choice[cmd]
258 return choice[cmd]
259
259
260 if len(choice) > 1:
260 if len(choice) > 1:
261 clist = choice.keys()
261 clist = choice.keys()
262 clist.sort()
262 clist.sort()
263 raise error.AmbiguousCommand(cmd, clist)
263 raise error.AmbiguousCommand(cmd, clist)
264
264
265 if choice:
265 if choice:
266 return choice.values()[0]
266 return choice.values()[0]
267
267
268 raise error.UnknownCommand(cmd, allcmds)
268 raise error.UnknownCommand(cmd, allcmds)
269
269
270 def findrepo(p):
270 def findrepo(p):
271 while not os.path.isdir(os.path.join(p, ".hg")):
271 while not os.path.isdir(os.path.join(p, ".hg")):
272 oldp, p = p, os.path.dirname(p)
272 oldp, p = p, os.path.dirname(p)
273 if p == oldp:
273 if p == oldp:
274 return None
274 return None
275
275
276 return p
276 return p
277
277
278 def bailifchanged(repo):
278 def bailifchanged(repo):
279 if repo.dirstate.p2() != nullid:
279 if repo.dirstate.p2() != nullid:
280 raise util.Abort(_('outstanding uncommitted merge'))
280 raise util.Abort(_('outstanding uncommitted merge'))
281 modified, added, removed, deleted = repo.status()[:4]
281 modified, added, removed, deleted = repo.status()[:4]
282 if modified or added or removed or deleted:
282 if modified or added or removed or deleted:
283 raise util.Abort(_('uncommitted changes'))
283 raise util.Abort(_('uncommitted changes'))
284 ctx = repo[None]
284 ctx = repo[None]
285 for s in sorted(ctx.substate):
285 for s in sorted(ctx.substate):
286 if ctx.sub(s).dirty():
286 if ctx.sub(s).dirty():
287 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
287 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
288
288
289 def logmessage(ui, opts):
289 def logmessage(ui, opts):
290 """ get the log message according to -m and -l option """
290 """ get the log message according to -m and -l option """
291 message = opts.get('message')
291 message = opts.get('message')
292 logfile = opts.get('logfile')
292 logfile = opts.get('logfile')
293
293
294 if message and logfile:
294 if message and logfile:
295 raise util.Abort(_('options --message and --logfile are mutually '
295 raise util.Abort(_('options --message and --logfile are mutually '
296 'exclusive'))
296 'exclusive'))
297 if not message and logfile:
297 if not message and logfile:
298 try:
298 try:
299 if logfile == '-':
299 if logfile == '-':
300 message = ui.fin.read()
300 message = ui.fin.read()
301 else:
301 else:
302 message = '\n'.join(util.readfile(logfile).splitlines())
302 message = '\n'.join(util.readfile(logfile).splitlines())
303 except IOError, inst:
303 except IOError, inst:
304 raise util.Abort(_("can't read commit message '%s': %s") %
304 raise util.Abort(_("can't read commit message '%s': %s") %
305 (logfile, inst.strerror))
305 (logfile, inst.strerror))
306 return message
306 return message
307
307
308 def mergeeditform(ctxorbool, baseformname):
308 def mergeeditform(ctxorbool, baseformname):
309 """return appropriate editform name (referencing a committemplate)
309 """return appropriate editform name (referencing a committemplate)
310
310
311 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
311 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
312 merging is committed.
312 merging is committed.
313
313
314 This returns baseformname with '.merge' appended if it is a merge,
314 This returns baseformname with '.merge' appended if it is a merge,
315 otherwise '.normal' is appended.
315 otherwise '.normal' is appended.
316 """
316 """
317 if isinstance(ctxorbool, bool):
317 if isinstance(ctxorbool, bool):
318 if ctxorbool:
318 if ctxorbool:
319 return baseformname + ".merge"
319 return baseformname + ".merge"
320 elif 1 < len(ctxorbool.parents()):
320 elif 1 < len(ctxorbool.parents()):
321 return baseformname + ".merge"
321 return baseformname + ".merge"
322
322
323 return baseformname + ".normal"
323 return baseformname + ".normal"
324
324
325 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
325 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
326 editform='', **opts):
326 editform='', **opts):
327 """get appropriate commit message editor according to '--edit' option
327 """get appropriate commit message editor according to '--edit' option
328
328
329 'finishdesc' is a function to be called with edited commit message
329 'finishdesc' is a function to be called with edited commit message
330 (= 'description' of the new changeset) just after editing, but
330 (= 'description' of the new changeset) just after editing, but
331 before checking empty-ness. It should return actual text to be
331 before checking empty-ness. It should return actual text to be
332 stored into history. This allows to change description before
332 stored into history. This allows to change description before
333 storing.
333 storing.
334
334
335 'extramsg' is a extra message to be shown in the editor instead of
335 'extramsg' is a extra message to be shown in the editor instead of
336 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
336 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
337 is automatically added.
337 is automatically added.
338
338
339 'editform' is a dot-separated list of names, to distinguish
339 'editform' is a dot-separated list of names, to distinguish
340 the purpose of commit text editing.
340 the purpose of commit text editing.
341
341
342 'getcommiteditor' returns 'commitforceeditor' regardless of
342 'getcommiteditor' returns 'commitforceeditor' regardless of
343 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
343 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
344 they are specific for usage in MQ.
344 they are specific for usage in MQ.
345 """
345 """
346 if edit or finishdesc or extramsg:
346 if edit or finishdesc or extramsg:
347 return lambda r, c, s: commitforceeditor(r, c, s,
347 return lambda r, c, s: commitforceeditor(r, c, s,
348 finishdesc=finishdesc,
348 finishdesc=finishdesc,
349 extramsg=extramsg,
349 extramsg=extramsg,
350 editform=editform)
350 editform=editform)
351 elif editform:
351 elif editform:
352 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
352 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
353 else:
353 else:
354 return commiteditor
354 return commiteditor
355
355
356 def loglimit(opts):
356 def loglimit(opts):
357 """get the log limit according to option -l/--limit"""
357 """get the log limit according to option -l/--limit"""
358 limit = opts.get('limit')
358 limit = opts.get('limit')
359 if limit:
359 if limit:
360 try:
360 try:
361 limit = int(limit)
361 limit = int(limit)
362 except ValueError:
362 except ValueError:
363 raise util.Abort(_('limit must be a positive integer'))
363 raise util.Abort(_('limit must be a positive integer'))
364 if limit <= 0:
364 if limit <= 0:
365 raise util.Abort(_('limit must be positive'))
365 raise util.Abort(_('limit must be positive'))
366 else:
366 else:
367 limit = None
367 limit = None
368 return limit
368 return limit
369
369
370 def makefilename(repo, pat, node, desc=None,
370 def makefilename(repo, pat, node, desc=None,
371 total=None, seqno=None, revwidth=None, pathname=None):
371 total=None, seqno=None, revwidth=None, pathname=None):
372 node_expander = {
372 node_expander = {
373 'H': lambda: hex(node),
373 'H': lambda: hex(node),
374 'R': lambda: str(repo.changelog.rev(node)),
374 'R': lambda: str(repo.changelog.rev(node)),
375 'h': lambda: short(node),
375 'h': lambda: short(node),
376 'm': lambda: re.sub('[^\w]', '_', str(desc))
376 'm': lambda: re.sub('[^\w]', '_', str(desc))
377 }
377 }
378 expander = {
378 expander = {
379 '%': lambda: '%',
379 '%': lambda: '%',
380 'b': lambda: os.path.basename(repo.root),
380 'b': lambda: os.path.basename(repo.root),
381 }
381 }
382
382
383 try:
383 try:
384 if node:
384 if node:
385 expander.update(node_expander)
385 expander.update(node_expander)
386 if node:
386 if node:
387 expander['r'] = (lambda:
387 expander['r'] = (lambda:
388 str(repo.changelog.rev(node)).zfill(revwidth or 0))
388 str(repo.changelog.rev(node)).zfill(revwidth or 0))
389 if total is not None:
389 if total is not None:
390 expander['N'] = lambda: str(total)
390 expander['N'] = lambda: str(total)
391 if seqno is not None:
391 if seqno is not None:
392 expander['n'] = lambda: str(seqno)
392 expander['n'] = lambda: str(seqno)
393 if total is not None and seqno is not None:
393 if total is not None and seqno is not None:
394 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
394 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
395 if pathname is not None:
395 if pathname is not None:
396 expander['s'] = lambda: os.path.basename(pathname)
396 expander['s'] = lambda: os.path.basename(pathname)
397 expander['d'] = lambda: os.path.dirname(pathname) or '.'
397 expander['d'] = lambda: os.path.dirname(pathname) or '.'
398 expander['p'] = lambda: pathname
398 expander['p'] = lambda: pathname
399
399
400 newname = []
400 newname = []
401 patlen = len(pat)
401 patlen = len(pat)
402 i = 0
402 i = 0
403 while i < patlen:
403 while i < patlen:
404 c = pat[i]
404 c = pat[i]
405 if c == '%':
405 if c == '%':
406 i += 1
406 i += 1
407 c = pat[i]
407 c = pat[i]
408 c = expander[c]()
408 c = expander[c]()
409 newname.append(c)
409 newname.append(c)
410 i += 1
410 i += 1
411 return ''.join(newname)
411 return ''.join(newname)
412 except KeyError, inst:
412 except KeyError, inst:
413 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
413 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
414 inst.args[0])
414 inst.args[0])
415
415
416 def makefileobj(repo, pat, node=None, desc=None, total=None,
416 def makefileobj(repo, pat, node=None, desc=None, total=None,
417 seqno=None, revwidth=None, mode='wb', modemap=None,
417 seqno=None, revwidth=None, mode='wb', modemap=None,
418 pathname=None):
418 pathname=None):
419
419
420 writable = mode not in ('r', 'rb')
420 writable = mode not in ('r', 'rb')
421
421
422 if not pat or pat == '-':
422 if not pat or pat == '-':
423 if writable:
423 if writable:
424 fp = repo.ui.fout
424 fp = repo.ui.fout
425 else:
425 else:
426 fp = repo.ui.fin
426 fp = repo.ui.fin
427 if util.safehasattr(fp, 'fileno'):
427 if util.safehasattr(fp, 'fileno'):
428 return os.fdopen(os.dup(fp.fileno()), mode)
428 return os.fdopen(os.dup(fp.fileno()), mode)
429 else:
429 else:
430 # if this fp can't be duped properly, return
430 # if this fp can't be duped properly, return
431 # a dummy object that can be closed
431 # a dummy object that can be closed
432 class wrappedfileobj(object):
432 class wrappedfileobj(object):
433 noop = lambda x: None
433 noop = lambda x: None
434 def __init__(self, f):
434 def __init__(self, f):
435 self.f = f
435 self.f = f
436 def __getattr__(self, attr):
436 def __getattr__(self, attr):
437 if attr == 'close':
437 if attr == 'close':
438 return self.noop
438 return self.noop
439 else:
439 else:
440 return getattr(self.f, attr)
440 return getattr(self.f, attr)
441
441
442 return wrappedfileobj(fp)
442 return wrappedfileobj(fp)
443 if util.safehasattr(pat, 'write') and writable:
443 if util.safehasattr(pat, 'write') and writable:
444 return pat
444 return pat
445 if util.safehasattr(pat, 'read') and 'r' in mode:
445 if util.safehasattr(pat, 'read') and 'r' in mode:
446 return pat
446 return pat
447 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
447 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
448 if modemap is not None:
448 if modemap is not None:
449 mode = modemap.get(fn, mode)
449 mode = modemap.get(fn, mode)
450 if mode == 'wb':
450 if mode == 'wb':
451 modemap[fn] = 'ab'
451 modemap[fn] = 'ab'
452 return open(fn, mode)
452 return open(fn, mode)
453
453
454 def openrevlog(repo, cmd, file_, opts):
454 def openrevlog(repo, cmd, file_, opts):
455 """opens the changelog, manifest, a filelog or a given revlog"""
455 """opens the changelog, manifest, a filelog or a given revlog"""
456 cl = opts['changelog']
456 cl = opts['changelog']
457 mf = opts['manifest']
457 mf = opts['manifest']
458 msg = None
458 msg = None
459 if cl and mf:
459 if cl and mf:
460 msg = _('cannot specify --changelog and --manifest at the same time')
460 msg = _('cannot specify --changelog and --manifest at the same time')
461 elif cl or mf:
461 elif cl or mf:
462 if file_:
462 if file_:
463 msg = _('cannot specify filename with --changelog or --manifest')
463 msg = _('cannot specify filename with --changelog or --manifest')
464 elif not repo:
464 elif not repo:
465 msg = _('cannot specify --changelog or --manifest '
465 msg = _('cannot specify --changelog or --manifest '
466 'without a repository')
466 'without a repository')
467 if msg:
467 if msg:
468 raise util.Abort(msg)
468 raise util.Abort(msg)
469
469
470 r = None
470 r = None
471 if repo:
471 if repo:
472 if cl:
472 if cl:
473 r = repo.unfiltered().changelog
473 r = repo.unfiltered().changelog
474 elif mf:
474 elif mf:
475 r = repo.manifest
475 r = repo.manifest
476 elif file_:
476 elif file_:
477 filelog = repo.file(file_)
477 filelog = repo.file(file_)
478 if len(filelog):
478 if len(filelog):
479 r = filelog
479 r = filelog
480 if not r:
480 if not r:
481 if not file_:
481 if not file_:
482 raise error.CommandError(cmd, _('invalid arguments'))
482 raise error.CommandError(cmd, _('invalid arguments'))
483 if not os.path.isfile(file_):
483 if not os.path.isfile(file_):
484 raise util.Abort(_("revlog '%s' not found") % file_)
484 raise util.Abort(_("revlog '%s' not found") % file_)
485 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
485 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
486 file_[:-2] + ".i")
486 file_[:-2] + ".i")
487 return r
487 return r
488
488
489 def copy(ui, repo, pats, opts, rename=False):
489 def copy(ui, repo, pats, opts, rename=False):
490 # called with the repo lock held
490 # called with the repo lock held
491 #
491 #
492 # hgsep => pathname that uses "/" to separate directories
492 # hgsep => pathname that uses "/" to separate directories
493 # ossep => pathname that uses os.sep to separate directories
493 # ossep => pathname that uses os.sep to separate directories
494 cwd = repo.getcwd()
494 cwd = repo.getcwd()
495 targets = {}
495 targets = {}
496 after = opts.get("after")
496 after = opts.get("after")
497 dryrun = opts.get("dry_run")
497 dryrun = opts.get("dry_run")
498 wctx = repo[None]
498 wctx = repo[None]
499
499
500 def walkpat(pat):
500 def walkpat(pat):
501 srcs = []
501 srcs = []
502 if after:
502 if after:
503 badstates = '?'
503 badstates = '?'
504 else:
504 else:
505 badstates = '?r'
505 badstates = '?r'
506 m = scmutil.match(repo[None], [pat], opts, globbed=True)
506 m = scmutil.match(repo[None], [pat], opts, globbed=True)
507 for abs in repo.walk(m):
507 for abs in repo.walk(m):
508 state = repo.dirstate[abs]
508 state = repo.dirstate[abs]
509 rel = m.rel(abs)
509 rel = m.rel(abs)
510 exact = m.exact(abs)
510 exact = m.exact(abs)
511 if state in badstates:
511 if state in badstates:
512 if exact and state == '?':
512 if exact and state == '?':
513 ui.warn(_('%s: not copying - file is not managed\n') % rel)
513 ui.warn(_('%s: not copying - file is not managed\n') % rel)
514 if exact and state == 'r':
514 if exact and state == 'r':
515 ui.warn(_('%s: not copying - file has been marked for'
515 ui.warn(_('%s: not copying - file has been marked for'
516 ' remove\n') % rel)
516 ' remove\n') % rel)
517 continue
517 continue
518 # abs: hgsep
518 # abs: hgsep
519 # rel: ossep
519 # rel: ossep
520 srcs.append((abs, rel, exact))
520 srcs.append((abs, rel, exact))
521 return srcs
521 return srcs
522
522
523 # abssrc: hgsep
523 # abssrc: hgsep
524 # relsrc: ossep
524 # relsrc: ossep
525 # otarget: ossep
525 # otarget: ossep
526 def copyfile(abssrc, relsrc, otarget, exact):
526 def copyfile(abssrc, relsrc, otarget, exact):
527 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
527 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
528 if '/' in abstarget:
528 if '/' in abstarget:
529 # We cannot normalize abstarget itself, this would prevent
529 # We cannot normalize abstarget itself, this would prevent
530 # case only renames, like a => A.
530 # case only renames, like a => A.
531 abspath, absname = abstarget.rsplit('/', 1)
531 abspath, absname = abstarget.rsplit('/', 1)
532 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
532 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
533 reltarget = repo.pathto(abstarget, cwd)
533 reltarget = repo.pathto(abstarget, cwd)
534 target = repo.wjoin(abstarget)
534 target = repo.wjoin(abstarget)
535 src = repo.wjoin(abssrc)
535 src = repo.wjoin(abssrc)
536 state = repo.dirstate[abstarget]
536 state = repo.dirstate[abstarget]
537
537
538 scmutil.checkportable(ui, abstarget)
538 scmutil.checkportable(ui, abstarget)
539
539
540 # check for collisions
540 # check for collisions
541 prevsrc = targets.get(abstarget)
541 prevsrc = targets.get(abstarget)
542 if prevsrc is not None:
542 if prevsrc is not None:
543 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
543 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
544 (reltarget, repo.pathto(abssrc, cwd),
544 (reltarget, repo.pathto(abssrc, cwd),
545 repo.pathto(prevsrc, cwd)))
545 repo.pathto(prevsrc, cwd)))
546 return
546 return
547
547
548 # check for overwrites
548 # check for overwrites
549 exists = os.path.lexists(target)
549 exists = os.path.lexists(target)
550 samefile = False
550 samefile = False
551 if exists and abssrc != abstarget:
551 if exists and abssrc != abstarget:
552 if (repo.dirstate.normalize(abssrc) ==
552 if (repo.dirstate.normalize(abssrc) ==
553 repo.dirstate.normalize(abstarget)):
553 repo.dirstate.normalize(abstarget)):
554 if not rename:
554 if not rename:
555 ui.warn(_("%s: can't copy - same file\n") % reltarget)
555 ui.warn(_("%s: can't copy - same file\n") % reltarget)
556 return
556 return
557 exists = False
557 exists = False
558 samefile = True
558 samefile = True
559
559
560 if not after and exists or after and state in 'mn':
560 if not after and exists or after and state in 'mn':
561 if not opts['force']:
561 if not opts['force']:
562 ui.warn(_('%s: not overwriting - file exists\n') %
562 ui.warn(_('%s: not overwriting - file exists\n') %
563 reltarget)
563 reltarget)
564 return
564 return
565
565
566 if after:
566 if after:
567 if not exists:
567 if not exists:
568 if rename:
568 if rename:
569 ui.warn(_('%s: not recording move - %s does not exist\n') %
569 ui.warn(_('%s: not recording move - %s does not exist\n') %
570 (relsrc, reltarget))
570 (relsrc, reltarget))
571 else:
571 else:
572 ui.warn(_('%s: not recording copy - %s does not exist\n') %
572 ui.warn(_('%s: not recording copy - %s does not exist\n') %
573 (relsrc, reltarget))
573 (relsrc, reltarget))
574 return
574 return
575 elif not dryrun:
575 elif not dryrun:
576 try:
576 try:
577 if exists:
577 if exists:
578 os.unlink(target)
578 os.unlink(target)
579 targetdir = os.path.dirname(target) or '.'
579 targetdir = os.path.dirname(target) or '.'
580 if not os.path.isdir(targetdir):
580 if not os.path.isdir(targetdir):
581 os.makedirs(targetdir)
581 os.makedirs(targetdir)
582 if samefile:
582 if samefile:
583 tmp = target + "~hgrename"
583 tmp = target + "~hgrename"
584 os.rename(src, tmp)
584 os.rename(src, tmp)
585 os.rename(tmp, target)
585 os.rename(tmp, target)
586 else:
586 else:
587 util.copyfile(src, target)
587 util.copyfile(src, target)
588 srcexists = True
588 srcexists = True
589 except IOError, inst:
589 except IOError, inst:
590 if inst.errno == errno.ENOENT:
590 if inst.errno == errno.ENOENT:
591 ui.warn(_('%s: deleted in working directory\n') % relsrc)
591 ui.warn(_('%s: deleted in working directory\n') % relsrc)
592 srcexists = False
592 srcexists = False
593 else:
593 else:
594 ui.warn(_('%s: cannot copy - %s\n') %
594 ui.warn(_('%s: cannot copy - %s\n') %
595 (relsrc, inst.strerror))
595 (relsrc, inst.strerror))
596 return True # report a failure
596 return True # report a failure
597
597
598 if ui.verbose or not exact:
598 if ui.verbose or not exact:
599 if rename:
599 if rename:
600 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
600 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
601 else:
601 else:
602 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
602 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
603
603
604 targets[abstarget] = abssrc
604 targets[abstarget] = abssrc
605
605
606 # fix up dirstate
606 # fix up dirstate
607 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
607 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
608 dryrun=dryrun, cwd=cwd)
608 dryrun=dryrun, cwd=cwd)
609 if rename and not dryrun:
609 if rename and not dryrun:
610 if not after and srcexists and not samefile:
610 if not after and srcexists and not samefile:
611 util.unlinkpath(repo.wjoin(abssrc))
611 util.unlinkpath(repo.wjoin(abssrc))
612 wctx.forget([abssrc])
612 wctx.forget([abssrc])
613
613
614 # pat: ossep
614 # pat: ossep
615 # dest ossep
615 # dest ossep
616 # srcs: list of (hgsep, hgsep, ossep, bool)
616 # srcs: list of (hgsep, hgsep, ossep, bool)
617 # return: function that takes hgsep and returns ossep
617 # return: function that takes hgsep and returns ossep
618 def targetpathfn(pat, dest, srcs):
618 def targetpathfn(pat, dest, srcs):
619 if os.path.isdir(pat):
619 if os.path.isdir(pat):
620 abspfx = pathutil.canonpath(repo.root, cwd, pat)
620 abspfx = pathutil.canonpath(repo.root, cwd, pat)
621 abspfx = util.localpath(abspfx)
621 abspfx = util.localpath(abspfx)
622 if destdirexists:
622 if destdirexists:
623 striplen = len(os.path.split(abspfx)[0])
623 striplen = len(os.path.split(abspfx)[0])
624 else:
624 else:
625 striplen = len(abspfx)
625 striplen = len(abspfx)
626 if striplen:
626 if striplen:
627 striplen += len(os.sep)
627 striplen += len(os.sep)
628 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
628 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
629 elif destdirexists:
629 elif destdirexists:
630 res = lambda p: os.path.join(dest,
630 res = lambda p: os.path.join(dest,
631 os.path.basename(util.localpath(p)))
631 os.path.basename(util.localpath(p)))
632 else:
632 else:
633 res = lambda p: dest
633 res = lambda p: dest
634 return res
634 return res
635
635
636 # pat: ossep
636 # pat: ossep
637 # dest ossep
637 # dest ossep
638 # srcs: list of (hgsep, hgsep, ossep, bool)
638 # srcs: list of (hgsep, hgsep, ossep, bool)
639 # return: function that takes hgsep and returns ossep
639 # return: function that takes hgsep and returns ossep
640 def targetpathafterfn(pat, dest, srcs):
640 def targetpathafterfn(pat, dest, srcs):
641 if matchmod.patkind(pat):
641 if matchmod.patkind(pat):
642 # a mercurial pattern
642 # a mercurial pattern
643 res = lambda p: os.path.join(dest,
643 res = lambda p: os.path.join(dest,
644 os.path.basename(util.localpath(p)))
644 os.path.basename(util.localpath(p)))
645 else:
645 else:
646 abspfx = pathutil.canonpath(repo.root, cwd, pat)
646 abspfx = pathutil.canonpath(repo.root, cwd, pat)
647 if len(abspfx) < len(srcs[0][0]):
647 if len(abspfx) < len(srcs[0][0]):
648 # A directory. Either the target path contains the last
648 # A directory. Either the target path contains the last
649 # component of the source path or it does not.
649 # component of the source path or it does not.
650 def evalpath(striplen):
650 def evalpath(striplen):
651 score = 0
651 score = 0
652 for s in srcs:
652 for s in srcs:
653 t = os.path.join(dest, util.localpath(s[0])[striplen:])
653 t = os.path.join(dest, util.localpath(s[0])[striplen:])
654 if os.path.lexists(t):
654 if os.path.lexists(t):
655 score += 1
655 score += 1
656 return score
656 return score
657
657
658 abspfx = util.localpath(abspfx)
658 abspfx = util.localpath(abspfx)
659 striplen = len(abspfx)
659 striplen = len(abspfx)
660 if striplen:
660 if striplen:
661 striplen += len(os.sep)
661 striplen += len(os.sep)
662 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
662 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
663 score = evalpath(striplen)
663 score = evalpath(striplen)
664 striplen1 = len(os.path.split(abspfx)[0])
664 striplen1 = len(os.path.split(abspfx)[0])
665 if striplen1:
665 if striplen1:
666 striplen1 += len(os.sep)
666 striplen1 += len(os.sep)
667 if evalpath(striplen1) > score:
667 if evalpath(striplen1) > score:
668 striplen = striplen1
668 striplen = striplen1
669 res = lambda p: os.path.join(dest,
669 res = lambda p: os.path.join(dest,
670 util.localpath(p)[striplen:])
670 util.localpath(p)[striplen:])
671 else:
671 else:
672 # a file
672 # a file
673 if destdirexists:
673 if destdirexists:
674 res = lambda p: os.path.join(dest,
674 res = lambda p: os.path.join(dest,
675 os.path.basename(util.localpath(p)))
675 os.path.basename(util.localpath(p)))
676 else:
676 else:
677 res = lambda p: dest
677 res = lambda p: dest
678 return res
678 return res
679
679
680
680
681 pats = scmutil.expandpats(pats)
681 pats = scmutil.expandpats(pats)
682 if not pats:
682 if not pats:
683 raise util.Abort(_('no source or destination specified'))
683 raise util.Abort(_('no source or destination specified'))
684 if len(pats) == 1:
684 if len(pats) == 1:
685 raise util.Abort(_('no destination specified'))
685 raise util.Abort(_('no destination specified'))
686 dest = pats.pop()
686 dest = pats.pop()
687 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
687 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
688 if not destdirexists:
688 if not destdirexists:
689 if len(pats) > 1 or matchmod.patkind(pats[0]):
689 if len(pats) > 1 or matchmod.patkind(pats[0]):
690 raise util.Abort(_('with multiple sources, destination must be an '
690 raise util.Abort(_('with multiple sources, destination must be an '
691 'existing directory'))
691 'existing directory'))
692 if util.endswithsep(dest):
692 if util.endswithsep(dest):
693 raise util.Abort(_('destination %s is not a directory') % dest)
693 raise util.Abort(_('destination %s is not a directory') % dest)
694
694
695 tfn = targetpathfn
695 tfn = targetpathfn
696 if after:
696 if after:
697 tfn = targetpathafterfn
697 tfn = targetpathafterfn
698 copylist = []
698 copylist = []
699 for pat in pats:
699 for pat in pats:
700 srcs = walkpat(pat)
700 srcs = walkpat(pat)
701 if not srcs:
701 if not srcs:
702 continue
702 continue
703 copylist.append((tfn(pat, dest, srcs), srcs))
703 copylist.append((tfn(pat, dest, srcs), srcs))
704 if not copylist:
704 if not copylist:
705 raise util.Abort(_('no files to copy'))
705 raise util.Abort(_('no files to copy'))
706
706
707 errors = 0
707 errors = 0
708 for targetpath, srcs in copylist:
708 for targetpath, srcs in copylist:
709 for abssrc, relsrc, exact in srcs:
709 for abssrc, relsrc, exact in srcs:
710 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
710 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
711 errors += 1
711 errors += 1
712
712
713 if errors:
713 if errors:
714 ui.warn(_('(consider using --after)\n'))
714 ui.warn(_('(consider using --after)\n'))
715
715
716 return errors != 0
716 return errors != 0
717
717
718 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
718 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
719 runargs=None, appendpid=False):
719 runargs=None, appendpid=False):
720 '''Run a command as a service.'''
720 '''Run a command as a service.'''
721
721
722 def writepid(pid):
722 def writepid(pid):
723 if opts['pid_file']:
723 if opts['pid_file']:
724 if appendpid:
724 if appendpid:
725 mode = 'a'
725 mode = 'a'
726 else:
726 else:
727 mode = 'w'
727 mode = 'w'
728 fp = open(opts['pid_file'], mode)
728 fp = open(opts['pid_file'], mode)
729 fp.write(str(pid) + '\n')
729 fp.write(str(pid) + '\n')
730 fp.close()
730 fp.close()
731
731
732 if opts['daemon'] and not opts['daemon_pipefds']:
732 if opts['daemon'] and not opts['daemon_pipefds']:
733 # Signal child process startup with file removal
733 # Signal child process startup with file removal
734 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
734 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
735 os.close(lockfd)
735 os.close(lockfd)
736 try:
736 try:
737 if not runargs:
737 if not runargs:
738 runargs = util.hgcmd() + sys.argv[1:]
738 runargs = util.hgcmd() + sys.argv[1:]
739 runargs.append('--daemon-pipefds=%s' % lockpath)
739 runargs.append('--daemon-pipefds=%s' % lockpath)
740 # Don't pass --cwd to the child process, because we've already
740 # Don't pass --cwd to the child process, because we've already
741 # changed directory.
741 # changed directory.
742 for i in xrange(1, len(runargs)):
742 for i in xrange(1, len(runargs)):
743 if runargs[i].startswith('--cwd='):
743 if runargs[i].startswith('--cwd='):
744 del runargs[i]
744 del runargs[i]
745 break
745 break
746 elif runargs[i].startswith('--cwd'):
746 elif runargs[i].startswith('--cwd'):
747 del runargs[i:i + 2]
747 del runargs[i:i + 2]
748 break
748 break
749 def condfn():
749 def condfn():
750 return not os.path.exists(lockpath)
750 return not os.path.exists(lockpath)
751 pid = util.rundetached(runargs, condfn)
751 pid = util.rundetached(runargs, condfn)
752 if pid < 0:
752 if pid < 0:
753 raise util.Abort(_('child process failed to start'))
753 raise util.Abort(_('child process failed to start'))
754 writepid(pid)
754 writepid(pid)
755 finally:
755 finally:
756 try:
756 try:
757 os.unlink(lockpath)
757 os.unlink(lockpath)
758 except OSError, e:
758 except OSError, e:
759 if e.errno != errno.ENOENT:
759 if e.errno != errno.ENOENT:
760 raise
760 raise
761 if parentfn:
761 if parentfn:
762 return parentfn(pid)
762 return parentfn(pid)
763 else:
763 else:
764 return
764 return
765
765
766 if initfn:
766 if initfn:
767 initfn()
767 initfn()
768
768
769 if not opts['daemon']:
769 if not opts['daemon']:
770 writepid(os.getpid())
770 writepid(os.getpid())
771
771
772 if opts['daemon_pipefds']:
772 if opts['daemon_pipefds']:
773 lockpath = opts['daemon_pipefds']
773 lockpath = opts['daemon_pipefds']
774 try:
774 try:
775 os.setsid()
775 os.setsid()
776 except AttributeError:
776 except AttributeError:
777 pass
777 pass
778 os.unlink(lockpath)
778 os.unlink(lockpath)
779 util.hidewindow()
779 util.hidewindow()
780 sys.stdout.flush()
780 sys.stdout.flush()
781 sys.stderr.flush()
781 sys.stderr.flush()
782
782
783 nullfd = os.open(os.devnull, os.O_RDWR)
783 nullfd = os.open(os.devnull, os.O_RDWR)
784 logfilefd = nullfd
784 logfilefd = nullfd
785 if logfile:
785 if logfile:
786 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
786 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
787 os.dup2(nullfd, 0)
787 os.dup2(nullfd, 0)
788 os.dup2(logfilefd, 1)
788 os.dup2(logfilefd, 1)
789 os.dup2(logfilefd, 2)
789 os.dup2(logfilefd, 2)
790 if nullfd not in (0, 1, 2):
790 if nullfd not in (0, 1, 2):
791 os.close(nullfd)
791 os.close(nullfd)
792 if logfile and logfilefd not in (0, 1, 2):
792 if logfile and logfilefd not in (0, 1, 2):
793 os.close(logfilefd)
793 os.close(logfilefd)
794
794
795 if runfn:
795 if runfn:
796 return runfn()
796 return runfn()
797
797
798 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
798 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
799 """Utility function used by commands.import to import a single patch
799 """Utility function used by commands.import to import a single patch
800
800
801 This function is explicitly defined here to help the evolve extension to
801 This function is explicitly defined here to help the evolve extension to
802 wrap this part of the import logic.
802 wrap this part of the import logic.
803
803
804 The API is currently a bit ugly because it a simple code translation from
804 The API is currently a bit ugly because it a simple code translation from
805 the import command. Feel free to make it better.
805 the import command. Feel free to make it better.
806
806
807 :hunk: a patch (as a binary string)
807 :hunk: a patch (as a binary string)
808 :parents: nodes that will be parent of the created commit
808 :parents: nodes that will be parent of the created commit
809 :opts: the full dict of option passed to the import command
809 :opts: the full dict of option passed to the import command
810 :msgs: list to save commit message to.
810 :msgs: list to save commit message to.
811 (used in case we need to save it when failing)
811 (used in case we need to save it when failing)
812 :updatefunc: a function that update a repo to a given node
812 :updatefunc: a function that update a repo to a given node
813 updatefunc(<repo>, <node>)
813 updatefunc(<repo>, <node>)
814 """
814 """
815 tmpname, message, user, date, branch, nodeid, p1, p2 = \
815 tmpname, message, user, date, branch, nodeid, p1, p2 = \
816 patch.extract(ui, hunk)
816 patch.extract(ui, hunk)
817
817
818 update = not opts.get('bypass')
818 update = not opts.get('bypass')
819 strip = opts["strip"]
819 strip = opts["strip"]
820 prefix = opts["prefix"]
820 prefix = opts["prefix"]
821 sim = float(opts.get('similarity') or 0)
821 sim = float(opts.get('similarity') or 0)
822 if not tmpname:
822 if not tmpname:
823 return (None, None, False)
823 return (None, None, False)
824 msg = _('applied to working directory')
824 msg = _('applied to working directory')
825
825
826 rejects = False
826 rejects = False
827
827
828 try:
828 try:
829 cmdline_message = logmessage(ui, opts)
829 cmdline_message = logmessage(ui, opts)
830 if cmdline_message:
830 if cmdline_message:
831 # pickup the cmdline msg
831 # pickup the cmdline msg
832 message = cmdline_message
832 message = cmdline_message
833 elif message:
833 elif message:
834 # pickup the patch msg
834 # pickup the patch msg
835 message = message.strip()
835 message = message.strip()
836 else:
836 else:
837 # launch the editor
837 # launch the editor
838 message = None
838 message = None
839 ui.debug('message:\n%s\n' % message)
839 ui.debug('message:\n%s\n' % message)
840
840
841 if len(parents) == 1:
841 if len(parents) == 1:
842 parents.append(repo[nullid])
842 parents.append(repo[nullid])
843 if opts.get('exact'):
843 if opts.get('exact'):
844 if not nodeid or not p1:
844 if not nodeid or not p1:
845 raise util.Abort(_('not a Mercurial patch'))
845 raise util.Abort(_('not a Mercurial patch'))
846 p1 = repo[p1]
846 p1 = repo[p1]
847 p2 = repo[p2 or nullid]
847 p2 = repo[p2 or nullid]
848 elif p2:
848 elif p2:
849 try:
849 try:
850 p1 = repo[p1]
850 p1 = repo[p1]
851 p2 = repo[p2]
851 p2 = repo[p2]
852 # Without any options, consider p2 only if the
852 # Without any options, consider p2 only if the
853 # patch is being applied on top of the recorded
853 # patch is being applied on top of the recorded
854 # first parent.
854 # first parent.
855 if p1 != parents[0]:
855 if p1 != parents[0]:
856 p1 = parents[0]
856 p1 = parents[0]
857 p2 = repo[nullid]
857 p2 = repo[nullid]
858 except error.RepoError:
858 except error.RepoError:
859 p1, p2 = parents
859 p1, p2 = parents
860 if p2.node() == nullid:
860 if p2.node() == nullid:
861 ui.warn(_("warning: import the patch as a normal revision\n"
861 ui.warn(_("warning: import the patch as a normal revision\n"
862 "(use --exact to import the patch as a merge)\n"))
862 "(use --exact to import the patch as a merge)\n"))
863 else:
863 else:
864 p1, p2 = parents
864 p1, p2 = parents
865
865
866 n = None
866 n = None
867 if update:
867 if update:
868 repo.dirstate.beginparentchange()
868 repo.dirstate.beginparentchange()
869 if p1 != parents[0]:
869 if p1 != parents[0]:
870 updatefunc(repo, p1.node())
870 updatefunc(repo, p1.node())
871 if p2 != parents[1]:
871 if p2 != parents[1]:
872 repo.setparents(p1.node(), p2.node())
872 repo.setparents(p1.node(), p2.node())
873
873
874 if opts.get('exact') or opts.get('import_branch'):
874 if opts.get('exact') or opts.get('import_branch'):
875 repo.dirstate.setbranch(branch or 'default')
875 repo.dirstate.setbranch(branch or 'default')
876
876
877 partial = opts.get('partial', False)
877 partial = opts.get('partial', False)
878 files = set()
878 files = set()
879 try:
879 try:
880 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
880 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
881 files=files, eolmode=None, similarity=sim / 100.0)
881 files=files, eolmode=None, similarity=sim / 100.0)
882 except patch.PatchError, e:
882 except patch.PatchError, e:
883 if not partial:
883 if not partial:
884 raise util.Abort(str(e))
884 raise util.Abort(str(e))
885 if partial:
885 if partial:
886 rejects = True
886 rejects = True
887
887
888 files = list(files)
888 files = list(files)
889 if opts.get('no_commit'):
889 if opts.get('no_commit'):
890 if message:
890 if message:
891 msgs.append(message)
891 msgs.append(message)
892 else:
892 else:
893 if opts.get('exact') or p2:
893 if opts.get('exact') or p2:
894 # If you got here, you either use --force and know what
894 # If you got here, you either use --force and know what
895 # you are doing or used --exact or a merge patch while
895 # you are doing or used --exact or a merge patch while
896 # being updated to its first parent.
896 # being updated to its first parent.
897 m = None
897 m = None
898 else:
898 else:
899 m = scmutil.matchfiles(repo, files or [])
899 m = scmutil.matchfiles(repo, files or [])
900 editform = mergeeditform(repo[None], 'import.normal')
900 editform = mergeeditform(repo[None], 'import.normal')
901 if opts.get('exact'):
901 if opts.get('exact'):
902 editor = None
902 editor = None
903 else:
903 else:
904 editor = getcommiteditor(editform=editform, **opts)
904 editor = getcommiteditor(editform=editform, **opts)
905 n = repo.commit(message, opts.get('user') or user,
905 n = repo.commit(message, opts.get('user') or user,
906 opts.get('date') or date, match=m,
906 opts.get('date') or date, match=m,
907 editor=editor, force=partial)
907 editor=editor, force=partial)
908 repo.dirstate.endparentchange()
908 repo.dirstate.endparentchange()
909 else:
909 else:
910 if opts.get('exact') or opts.get('import_branch'):
910 if opts.get('exact') or opts.get('import_branch'):
911 branch = branch or 'default'
911 branch = branch or 'default'
912 else:
912 else:
913 branch = p1.branch()
913 branch = p1.branch()
914 store = patch.filestore()
914 store = patch.filestore()
915 try:
915 try:
916 files = set()
916 files = set()
917 try:
917 try:
918 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
918 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
919 files, eolmode=None)
919 files, eolmode=None)
920 except patch.PatchError, e:
920 except patch.PatchError, e:
921 raise util.Abort(str(e))
921 raise util.Abort(str(e))
922 if opts.get('exact'):
922 if opts.get('exact'):
923 editor = None
923 editor = None
924 else:
924 else:
925 editor = getcommiteditor(editform='import.bypass')
925 editor = getcommiteditor(editform='import.bypass')
926 memctx = context.makememctx(repo, (p1.node(), p2.node()),
926 memctx = context.makememctx(repo, (p1.node(), p2.node()),
927 message,
927 message,
928 opts.get('user') or user,
928 opts.get('user') or user,
929 opts.get('date') or date,
929 opts.get('date') or date,
930 branch, files, store,
930 branch, files, store,
931 editor=editor)
931 editor=editor)
932 n = memctx.commit()
932 n = memctx.commit()
933 finally:
933 finally:
934 store.close()
934 store.close()
935 if opts.get('exact') and opts.get('no_commit'):
935 if opts.get('exact') and opts.get('no_commit'):
936 # --exact with --no-commit is still useful in that it does merge
936 # --exact with --no-commit is still useful in that it does merge
937 # and branch bits
937 # and branch bits
938 ui.warn(_("warning: can't check exact import with --no-commit\n"))
938 ui.warn(_("warning: can't check exact import with --no-commit\n"))
939 elif opts.get('exact') and hex(n) != nodeid:
939 elif opts.get('exact') and hex(n) != nodeid:
940 raise util.Abort(_('patch is damaged or loses information'))
940 raise util.Abort(_('patch is damaged or loses information'))
941 if n:
941 if n:
942 # i18n: refers to a short changeset id
942 # i18n: refers to a short changeset id
943 msg = _('created %s') % short(n)
943 msg = _('created %s') % short(n)
944 return (msg, n, rejects)
944 return (msg, n, rejects)
945 finally:
945 finally:
946 os.unlink(tmpname)
946 os.unlink(tmpname)
947
947
948 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
948 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
949 opts=None):
949 opts=None):
950 '''export changesets as hg patches.'''
950 '''export changesets as hg patches.'''
951
951
952 total = len(revs)
952 total = len(revs)
953 revwidth = max([len(str(rev)) for rev in revs])
953 revwidth = max([len(str(rev)) for rev in revs])
954 filemode = {}
954 filemode = {}
955
955
956 def single(rev, seqno, fp):
956 def single(rev, seqno, fp):
957 ctx = repo[rev]
957 ctx = repo[rev]
958 node = ctx.node()
958 node = ctx.node()
959 parents = [p.node() for p in ctx.parents() if p]
959 parents = [p.node() for p in ctx.parents() if p]
960 branch = ctx.branch()
960 branch = ctx.branch()
961 if switch_parent:
961 if switch_parent:
962 parents.reverse()
962 parents.reverse()
963
963
964 if parents:
964 if parents:
965 prev = parents[0]
965 prev = parents[0]
966 else:
966 else:
967 prev = nullid
967 prev = nullid
968
968
969 shouldclose = False
969 shouldclose = False
970 if not fp and len(template) > 0:
970 if not fp and len(template) > 0:
971 desc_lines = ctx.description().rstrip().split('\n')
971 desc_lines = ctx.description().rstrip().split('\n')
972 desc = desc_lines[0] #Commit always has a first line.
972 desc = desc_lines[0] #Commit always has a first line.
973 fp = makefileobj(repo, template, node, desc=desc, total=total,
973 fp = makefileobj(repo, template, node, desc=desc, total=total,
974 seqno=seqno, revwidth=revwidth, mode='wb',
974 seqno=seqno, revwidth=revwidth, mode='wb',
975 modemap=filemode)
975 modemap=filemode)
976 if fp != template:
976 if fp != template:
977 shouldclose = True
977 shouldclose = True
978 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
978 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
979 repo.ui.note("%s\n" % fp.name)
979 repo.ui.note("%s\n" % fp.name)
980
980
981 if not fp:
981 if not fp:
982 write = repo.ui.write
982 write = repo.ui.write
983 else:
983 else:
984 def write(s, **kw):
984 def write(s, **kw):
985 fp.write(s)
985 fp.write(s)
986
986
987
987
988 write("# HG changeset patch\n")
988 write("# HG changeset patch\n")
989 write("# User %s\n" % ctx.user())
989 write("# User %s\n" % ctx.user())
990 write("# Date %d %d\n" % ctx.date())
990 write("# Date %d %d\n" % ctx.date())
991 write("# %s\n" % util.datestr(ctx.date()))
991 write("# %s\n" % util.datestr(ctx.date()))
992 if branch and branch != 'default':
992 if branch and branch != 'default':
993 write("# Branch %s\n" % branch)
993 write("# Branch %s\n" % branch)
994 write("# Node ID %s\n" % hex(node))
994 write("# Node ID %s\n" % hex(node))
995 write("# Parent %s\n" % hex(prev))
995 write("# Parent %s\n" % hex(prev))
996 if len(parents) > 1:
996 if len(parents) > 1:
997 write("# Parent %s\n" % hex(parents[1]))
997 write("# Parent %s\n" % hex(parents[1]))
998 write(ctx.description().rstrip())
998 write(ctx.description().rstrip())
999 write("\n\n")
999 write("\n\n")
1000
1000
1001 for chunk, label in patch.diffui(repo, prev, node, opts=opts):
1001 for chunk, label in patch.diffui(repo, prev, node, opts=opts):
1002 write(chunk, label=label)
1002 write(chunk, label=label)
1003
1003
1004 if shouldclose:
1004 if shouldclose:
1005 fp.close()
1005 fp.close()
1006
1006
1007 for seqno, rev in enumerate(revs):
1007 for seqno, rev in enumerate(revs):
1008 single(rev, seqno + 1, fp)
1008 single(rev, seqno + 1, fp)
1009
1009
1010 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1010 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1011 changes=None, stat=False, fp=None, prefix='',
1011 changes=None, stat=False, fp=None, prefix='',
1012 listsubrepos=False):
1012 listsubrepos=False):
1013 '''show diff or diffstat.'''
1013 '''show diff or diffstat.'''
1014 if fp is None:
1014 if fp is None:
1015 write = ui.write
1015 write = ui.write
1016 else:
1016 else:
1017 def write(s, **kw):
1017 def write(s, **kw):
1018 fp.write(s)
1018 fp.write(s)
1019
1019
1020 if stat:
1020 if stat:
1021 diffopts = diffopts.copy(context=0)
1021 diffopts = diffopts.copy(context=0)
1022 width = 80
1022 width = 80
1023 if not ui.plain():
1023 if not ui.plain():
1024 width = ui.termwidth()
1024 width = ui.termwidth()
1025 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1025 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1026 prefix=prefix)
1026 prefix=prefix)
1027 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1027 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1028 width=width,
1028 width=width,
1029 git=diffopts.git):
1029 git=diffopts.git):
1030 write(chunk, label=label)
1030 write(chunk, label=label)
1031 else:
1031 else:
1032 for chunk, label in patch.diffui(repo, node1, node2, match,
1032 for chunk, label in patch.diffui(repo, node1, node2, match,
1033 changes, diffopts, prefix=prefix):
1033 changes, diffopts, prefix=prefix):
1034 write(chunk, label=label)
1034 write(chunk, label=label)
1035
1035
1036 if listsubrepos:
1036 if listsubrepos:
1037 ctx1 = repo[node1]
1037 ctx1 = repo[node1]
1038 ctx2 = repo[node2]
1038 ctx2 = repo[node2]
1039 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1039 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1040 tempnode2 = node2
1040 tempnode2 = node2
1041 try:
1041 try:
1042 if node2 is not None:
1042 if node2 is not None:
1043 tempnode2 = ctx2.substate[subpath][1]
1043 tempnode2 = ctx2.substate[subpath][1]
1044 except KeyError:
1044 except KeyError:
1045 # A subrepo that existed in node1 was deleted between node1 and
1045 # A subrepo that existed in node1 was deleted between node1 and
1046 # node2 (inclusive). Thus, ctx2's substate won't contain that
1046 # node2 (inclusive). Thus, ctx2's substate won't contain that
1047 # subpath. The best we can do is to ignore it.
1047 # subpath. The best we can do is to ignore it.
1048 tempnode2 = None
1048 tempnode2 = None
1049 submatch = matchmod.narrowmatcher(subpath, match)
1049 submatch = matchmod.narrowmatcher(subpath, match)
1050 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1050 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1051 stat=stat, fp=fp, prefix=prefix)
1051 stat=stat, fp=fp, prefix=prefix)
1052
1052
1053 class changeset_printer(object):
1053 class changeset_printer(object):
1054 '''show changeset information when templating not requested.'''
1054 '''show changeset information when templating not requested.'''
1055
1055
1056 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1056 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1057 self.ui = ui
1057 self.ui = ui
1058 self.repo = repo
1058 self.repo = repo
1059 self.buffered = buffered
1059 self.buffered = buffered
1060 self.matchfn = matchfn
1060 self.matchfn = matchfn
1061 self.diffopts = diffopts
1061 self.diffopts = diffopts
1062 self.header = {}
1062 self.header = {}
1063 self.hunk = {}
1063 self.hunk = {}
1064 self.lastheader = None
1064 self.lastheader = None
1065 self.footer = None
1065 self.footer = None
1066
1066
1067 def flush(self, rev):
1067 def flush(self, rev):
1068 if rev in self.header:
1068 if rev in self.header:
1069 h = self.header[rev]
1069 h = self.header[rev]
1070 if h != self.lastheader:
1070 if h != self.lastheader:
1071 self.lastheader = h
1071 self.lastheader = h
1072 self.ui.write(h)
1072 self.ui.write(h)
1073 del self.header[rev]
1073 del self.header[rev]
1074 if rev in self.hunk:
1074 if rev in self.hunk:
1075 self.ui.write(self.hunk[rev])
1075 self.ui.write(self.hunk[rev])
1076 del self.hunk[rev]
1076 del self.hunk[rev]
1077 return 1
1077 return 1
1078 return 0
1078 return 0
1079
1079
1080 def close(self):
1080 def close(self):
1081 if self.footer:
1081 if self.footer:
1082 self.ui.write(self.footer)
1082 self.ui.write(self.footer)
1083
1083
1084 def show(self, ctx, copies=None, matchfn=None, **props):
1084 def show(self, ctx, copies=None, matchfn=None, **props):
1085 if self.buffered:
1085 if self.buffered:
1086 self.ui.pushbuffer()
1086 self.ui.pushbuffer()
1087 self._show(ctx, copies, matchfn, props)
1087 self._show(ctx, copies, matchfn, props)
1088 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
1088 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
1089 else:
1089 else:
1090 self._show(ctx, copies, matchfn, props)
1090 self._show(ctx, copies, matchfn, props)
1091
1091
1092 def _show(self, ctx, copies, matchfn, props):
1092 def _show(self, ctx, copies, matchfn, props):
1093 '''show a single changeset or file revision'''
1093 '''show a single changeset or file revision'''
1094 changenode = ctx.node()
1094 changenode = ctx.node()
1095 rev = ctx.rev()
1095 rev = ctx.rev()
1096
1096
1097 if self.ui.quiet:
1097 if self.ui.quiet:
1098 self.ui.write("%d:%s\n" % (rev, short(changenode)),
1098 self.ui.write("%d:%s\n" % (rev, short(changenode)),
1099 label='log.node')
1099 label='log.node')
1100 return
1100 return
1101
1101
1102 log = self.repo.changelog
1102 log = self.repo.changelog
1103 date = util.datestr(ctx.date())
1103 date = util.datestr(ctx.date())
1104
1104
1105 if self.ui.debugflag:
1105 if self.ui.debugflag:
1106 hexfunc = hex
1106 hexfunc = hex
1107 else:
1107 else:
1108 hexfunc = short
1108 hexfunc = short
1109
1109
1110 parents = [(p, hexfunc(log.node(p)))
1110 parents = [(p, hexfunc(log.node(p)))
1111 for p in self._meaningful_parentrevs(log, rev)]
1111 for p in self._meaningful_parentrevs(log, rev)]
1112
1112
1113 # i18n: column positioning for "hg log"
1113 # i18n: column positioning for "hg log"
1114 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
1114 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
1115 label='log.changeset changeset.%s' % ctx.phasestr())
1115 label='log.changeset changeset.%s' % ctx.phasestr())
1116
1116
1117 # branches are shown first before any other names due to backwards
1117 # branches are shown first before any other names due to backwards
1118 # compatibility
1118 # compatibility
1119 branch = ctx.branch()
1119 branch = ctx.branch()
1120 # don't show the default branch name
1120 # don't show the default branch name
1121 if branch != 'default':
1121 if branch != 'default':
1122 # i18n: column positioning for "hg log"
1122 # i18n: column positioning for "hg log"
1123 self.ui.write(_("branch: %s\n") % branch,
1123 self.ui.write(_("branch: %s\n") % branch,
1124 label='log.branch')
1124 label='log.branch')
1125
1125
1126 for name, ns in self.repo.names.iteritems():
1126 for name, ns in self.repo.names.iteritems():
1127 # branches has special logic already handled above, so here we just
1127 # branches has special logic already handled above, so here we just
1128 # skip it
1128 # skip it
1129 if name == 'branches':
1129 if name == 'branches':
1130 continue
1130 continue
1131 # we will use the templatename as the color name since those two
1131 # we will use the templatename as the color name since those two
1132 # should be the same
1132 # should be the same
1133 for name in ns.names(self.repo, changenode):
1133 for name in ns.names(self.repo, changenode):
1134 self.ui.write(ns.logfmt % name,
1134 self.ui.write(ns.logfmt % name,
1135 label='log.%s' % ns.colorname)
1135 label='log.%s' % ns.colorname)
1136 if self.ui.debugflag:
1136 if self.ui.debugflag:
1137 # i18n: column positioning for "hg log"
1137 # i18n: column positioning for "hg log"
1138 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
1138 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
1139 label='log.phase')
1139 label='log.phase')
1140 for parent in parents:
1140 for parent in parents:
1141 label = 'log.parent changeset.%s' % self.repo[parent[0]].phasestr()
1141 label = 'log.parent changeset.%s' % self.repo[parent[0]].phasestr()
1142 # i18n: column positioning for "hg log"
1142 # i18n: column positioning for "hg log"
1143 self.ui.write(_("parent: %d:%s\n") % parent,
1143 self.ui.write(_("parent: %d:%s\n") % parent,
1144 label=label)
1144 label=label)
1145
1145
1146 if self.ui.debugflag:
1146 if self.ui.debugflag:
1147 mnode = ctx.manifestnode()
1147 mnode = ctx.manifestnode()
1148 # i18n: column positioning for "hg log"
1148 # i18n: column positioning for "hg log"
1149 self.ui.write(_("manifest: %d:%s\n") %
1149 self.ui.write(_("manifest: %d:%s\n") %
1150 (self.repo.manifest.rev(mnode), hex(mnode)),
1150 (self.repo.manifest.rev(mnode), hex(mnode)),
1151 label='ui.debug log.manifest')
1151 label='ui.debug log.manifest')
1152 # i18n: column positioning for "hg log"
1152 # i18n: column positioning for "hg log"
1153 self.ui.write(_("user: %s\n") % ctx.user(),
1153 self.ui.write(_("user: %s\n") % ctx.user(),
1154 label='log.user')
1154 label='log.user')
1155 # i18n: column positioning for "hg log"
1155 # i18n: column positioning for "hg log"
1156 self.ui.write(_("date: %s\n") % date,
1156 self.ui.write(_("date: %s\n") % date,
1157 label='log.date')
1157 label='log.date')
1158
1158
1159 if self.ui.debugflag:
1159 if self.ui.debugflag:
1160 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
1160 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
1161 for key, value in zip([# i18n: column positioning for "hg log"
1161 for key, value in zip([# i18n: column positioning for "hg log"
1162 _("files:"),
1162 _("files:"),
1163 # i18n: column positioning for "hg log"
1163 # i18n: column positioning for "hg log"
1164 _("files+:"),
1164 _("files+:"),
1165 # i18n: column positioning for "hg log"
1165 # i18n: column positioning for "hg log"
1166 _("files-:")], files):
1166 _("files-:")], files):
1167 if value:
1167 if value:
1168 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1168 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1169 label='ui.debug log.files')
1169 label='ui.debug log.files')
1170 elif ctx.files() and self.ui.verbose:
1170 elif ctx.files() and self.ui.verbose:
1171 # i18n: column positioning for "hg log"
1171 # i18n: column positioning for "hg log"
1172 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1172 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1173 label='ui.note log.files')
1173 label='ui.note log.files')
1174 if copies and self.ui.verbose:
1174 if copies and self.ui.verbose:
1175 copies = ['%s (%s)' % c for c in copies]
1175 copies = ['%s (%s)' % c for c in copies]
1176 # i18n: column positioning for "hg log"
1176 # i18n: column positioning for "hg log"
1177 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1177 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1178 label='ui.note log.copies')
1178 label='ui.note log.copies')
1179
1179
1180 extra = ctx.extra()
1180 extra = ctx.extra()
1181 if extra and self.ui.debugflag:
1181 if extra and self.ui.debugflag:
1182 for key, value in sorted(extra.items()):
1182 for key, value in sorted(extra.items()):
1183 # i18n: column positioning for "hg log"
1183 # i18n: column positioning for "hg log"
1184 self.ui.write(_("extra: %s=%s\n")
1184 self.ui.write(_("extra: %s=%s\n")
1185 % (key, value.encode('string_escape')),
1185 % (key, value.encode('string_escape')),
1186 label='ui.debug log.extra')
1186 label='ui.debug log.extra')
1187
1187
1188 description = ctx.description().strip()
1188 description = ctx.description().strip()
1189 if description:
1189 if description:
1190 if self.ui.verbose:
1190 if self.ui.verbose:
1191 self.ui.write(_("description:\n"),
1191 self.ui.write(_("description:\n"),
1192 label='ui.note log.description')
1192 label='ui.note log.description')
1193 self.ui.write(description,
1193 self.ui.write(description,
1194 label='ui.note log.description')
1194 label='ui.note log.description')
1195 self.ui.write("\n\n")
1195 self.ui.write("\n\n")
1196 else:
1196 else:
1197 # i18n: column positioning for "hg log"
1197 # i18n: column positioning for "hg log"
1198 self.ui.write(_("summary: %s\n") %
1198 self.ui.write(_("summary: %s\n") %
1199 description.splitlines()[0],
1199 description.splitlines()[0],
1200 label='log.summary')
1200 label='log.summary')
1201 self.ui.write("\n")
1201 self.ui.write("\n")
1202
1202
1203 self.showpatch(changenode, matchfn)
1203 self.showpatch(changenode, matchfn)
1204
1204
1205 def showpatch(self, node, matchfn):
1205 def showpatch(self, node, matchfn):
1206 if not matchfn:
1206 if not matchfn:
1207 matchfn = self.matchfn
1207 matchfn = self.matchfn
1208 if matchfn:
1208 if matchfn:
1209 stat = self.diffopts.get('stat')
1209 stat = self.diffopts.get('stat')
1210 diff = self.diffopts.get('patch')
1210 diff = self.diffopts.get('patch')
1211 diffopts = patch.diffallopts(self.ui, self.diffopts)
1211 diffopts = patch.diffallopts(self.ui, self.diffopts)
1212 prev = self.repo.changelog.parents(node)[0]
1212 prev = self.repo.changelog.parents(node)[0]
1213 if stat:
1213 if stat:
1214 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1214 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1215 match=matchfn, stat=True)
1215 match=matchfn, stat=True)
1216 if diff:
1216 if diff:
1217 if stat:
1217 if stat:
1218 self.ui.write("\n")
1218 self.ui.write("\n")
1219 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1219 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1220 match=matchfn, stat=False)
1220 match=matchfn, stat=False)
1221 self.ui.write("\n")
1221 self.ui.write("\n")
1222
1222
1223 def _meaningful_parentrevs(self, log, rev):
1223 def _meaningful_parentrevs(self, log, rev):
1224 """Return list of meaningful (or all if debug) parentrevs for rev.
1224 """Return list of meaningful (or all if debug) parentrevs for rev.
1225
1225
1226 For merges (two non-nullrev revisions) both parents are meaningful.
1226 For merges (two non-nullrev revisions) both parents are meaningful.
1227 Otherwise the first parent revision is considered meaningful if it
1227 Otherwise the first parent revision is considered meaningful if it
1228 is not the preceding revision.
1228 is not the preceding revision.
1229 """
1229 """
1230 parents = log.parentrevs(rev)
1230 parents = log.parentrevs(rev)
1231 if not self.ui.debugflag and parents[1] == nullrev:
1231 if not self.ui.debugflag and parents[1] == nullrev:
1232 if parents[0] >= rev - 1:
1232 if parents[0] >= rev - 1:
1233 parents = []
1233 parents = []
1234 else:
1234 else:
1235 parents = [parents[0]]
1235 parents = [parents[0]]
1236 return parents
1236 return parents
1237
1237
1238 class jsonchangeset(changeset_printer):
1238 class jsonchangeset(changeset_printer):
1239 '''format changeset information.'''
1239 '''format changeset information.'''
1240
1240
1241 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1241 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1242 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1242 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1243 self.cache = {}
1243 self.cache = {}
1244 self._first = True
1244 self._first = True
1245
1245
1246 def close(self):
1246 def close(self):
1247 if not self._first:
1247 if not self._first:
1248 self.ui.write("\n]\n")
1248 self.ui.write("\n]\n")
1249 else:
1249 else:
1250 self.ui.write("[]\n")
1250 self.ui.write("[]\n")
1251
1251
1252 def _show(self, ctx, copies, matchfn, props):
1252 def _show(self, ctx, copies, matchfn, props):
1253 '''show a single changeset or file revision'''
1253 '''show a single changeset or file revision'''
1254 hexnode = hex(ctx.node())
1254 hexnode = hex(ctx.node())
1255 rev = ctx.rev()
1255 rev = ctx.rev()
1256 j = encoding.jsonescape
1256 j = encoding.jsonescape
1257
1257
1258 if self._first:
1258 if self._first:
1259 self.ui.write("[\n {")
1259 self.ui.write("[\n {")
1260 self._first = False
1260 self._first = False
1261 else:
1261 else:
1262 self.ui.write(",\n {")
1262 self.ui.write(",\n {")
1263
1263
1264 if self.ui.quiet:
1264 if self.ui.quiet:
1265 self.ui.write('\n "rev": %d' % rev)
1265 self.ui.write('\n "rev": %d' % rev)
1266 self.ui.write(',\n "node": "%s"' % hexnode)
1266 self.ui.write(',\n "node": "%s"' % hexnode)
1267 self.ui.write('\n }')
1267 self.ui.write('\n }')
1268 return
1268 return
1269
1269
1270 self.ui.write('\n "rev": %d' % rev)
1270 self.ui.write('\n "rev": %d' % rev)
1271 self.ui.write(',\n "node": "%s"' % hexnode)
1271 self.ui.write(',\n "node": "%s"' % hexnode)
1272 self.ui.write(',\n "branch": "%s"' % j(ctx.branch()))
1272 self.ui.write(',\n "branch": "%s"' % j(ctx.branch()))
1273 self.ui.write(',\n "phase": "%s"' % ctx.phasestr())
1273 self.ui.write(',\n "phase": "%s"' % ctx.phasestr())
1274 self.ui.write(',\n "user": "%s"' % j(ctx.user()))
1274 self.ui.write(',\n "user": "%s"' % j(ctx.user()))
1275 self.ui.write(',\n "date": [%d, %d]' % ctx.date())
1275 self.ui.write(',\n "date": [%d, %d]' % ctx.date())
1276 self.ui.write(',\n "desc": "%s"' % j(ctx.description()))
1276 self.ui.write(',\n "desc": "%s"' % j(ctx.description()))
1277
1277
1278 self.ui.write(',\n "bookmarks": [%s]' %
1278 self.ui.write(',\n "bookmarks": [%s]' %
1279 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1279 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1280 self.ui.write(',\n "tags": [%s]' %
1280 self.ui.write(',\n "tags": [%s]' %
1281 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1281 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1282 self.ui.write(',\n "parents": [%s]' %
1282 self.ui.write(',\n "parents": [%s]' %
1283 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1283 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1284
1284
1285 if self.ui.debugflag:
1285 if self.ui.debugflag:
1286 self.ui.write(',\n "manifest": "%s"' % hex(ctx.manifestnode()))
1286 self.ui.write(',\n "manifest": "%s"' % hex(ctx.manifestnode()))
1287
1287
1288 self.ui.write(',\n "extra": {%s}' %
1288 self.ui.write(',\n "extra": {%s}' %
1289 ", ".join('"%s": "%s"' % (j(k), j(v))
1289 ", ".join('"%s": "%s"' % (j(k), j(v))
1290 for k, v in ctx.extra().items()))
1290 for k, v in ctx.extra().items()))
1291
1291
1292 files = ctx.p1().status(ctx)
1292 files = ctx.p1().status(ctx)
1293 self.ui.write(',\n "modified": [%s]' %
1293 self.ui.write(',\n "modified": [%s]' %
1294 ", ".join('"%s"' % j(f) for f in files[0]))
1294 ", ".join('"%s"' % j(f) for f in files[0]))
1295 self.ui.write(',\n "added": [%s]' %
1295 self.ui.write(',\n "added": [%s]' %
1296 ", ".join('"%s"' % j(f) for f in files[1]))
1296 ", ".join('"%s"' % j(f) for f in files[1]))
1297 self.ui.write(',\n "removed": [%s]' %
1297 self.ui.write(',\n "removed": [%s]' %
1298 ", ".join('"%s"' % j(f) for f in files[2]))
1298 ", ".join('"%s"' % j(f) for f in files[2]))
1299
1299
1300 elif self.ui.verbose:
1300 elif self.ui.verbose:
1301 self.ui.write(',\n "files": [%s]' %
1301 self.ui.write(',\n "files": [%s]' %
1302 ", ".join('"%s"' % j(f) for f in ctx.files()))
1302 ", ".join('"%s"' % j(f) for f in ctx.files()))
1303
1303
1304 if copies:
1304 if copies:
1305 self.ui.write(',\n "copies": {%s}' %
1305 self.ui.write(',\n "copies": {%s}' %
1306 ", ".join('"%s": "%s"' % (j(k), j(v))
1306 ", ".join('"%s": "%s"' % (j(k), j(v))
1307 for k, v in copies))
1307 for k, v in copies))
1308
1308
1309 matchfn = self.matchfn
1309 matchfn = self.matchfn
1310 if matchfn:
1310 if matchfn:
1311 stat = self.diffopts.get('stat')
1311 stat = self.diffopts.get('stat')
1312 diff = self.diffopts.get('patch')
1312 diff = self.diffopts.get('patch')
1313 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1313 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1314 node, prev = ctx.node(), ctx.p1().node()
1314 node, prev = ctx.node(), ctx.p1().node()
1315 if stat:
1315 if stat:
1316 self.ui.pushbuffer()
1316 self.ui.pushbuffer()
1317 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1317 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1318 match=matchfn, stat=True)
1318 match=matchfn, stat=True)
1319 self.ui.write(',\n "diffstat": "%s"' % j(self.ui.popbuffer()))
1319 self.ui.write(',\n "diffstat": "%s"' % j(self.ui.popbuffer()))
1320 if diff:
1320 if diff:
1321 self.ui.pushbuffer()
1321 self.ui.pushbuffer()
1322 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1322 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1323 match=matchfn, stat=False)
1323 match=matchfn, stat=False)
1324 self.ui.write(',\n "diff": "%s"' % j(self.ui.popbuffer()))
1324 self.ui.write(',\n "diff": "%s"' % j(self.ui.popbuffer()))
1325
1325
1326 self.ui.write("\n }")
1326 self.ui.write("\n }")
1327
1327
1328 class changeset_templater(changeset_printer):
1328 class changeset_templater(changeset_printer):
1329 '''format changeset information.'''
1329 '''format changeset information.'''
1330
1330
1331 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1331 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1332 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1332 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1333 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1333 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1334 defaulttempl = {
1334 defaulttempl = {
1335 'parent': '{rev}:{node|formatnode} ',
1335 'parent': '{rev}:{node|formatnode} ',
1336 'manifest': '{rev}:{node|formatnode}',
1336 'manifest': '{rev}:{node|formatnode}',
1337 'file_copy': '{name} ({source})',
1337 'file_copy': '{name} ({source})',
1338 'extra': '{key}={value|stringescape}'
1338 'extra': '{key}={value|stringescape}'
1339 }
1339 }
1340 # filecopy is preserved for compatibility reasons
1340 # filecopy is preserved for compatibility reasons
1341 defaulttempl['filecopy'] = defaulttempl['file_copy']
1341 defaulttempl['filecopy'] = defaulttempl['file_copy']
1342 self.t = templater.templater(mapfile, {'formatnode': formatnode},
1342 self.t = templater.templater(mapfile, {'formatnode': formatnode},
1343 cache=defaulttempl)
1343 cache=defaulttempl)
1344 if tmpl:
1344 if tmpl:
1345 self.t.cache['changeset'] = tmpl
1345 self.t.cache['changeset'] = tmpl
1346
1346
1347 self.cache = {}
1347 self.cache = {}
1348
1348
1349 def _meaningful_parentrevs(self, ctx):
1349 def _meaningful_parentrevs(self, ctx):
1350 """Return list of meaningful (or all if debug) parentrevs for rev.
1350 """Return list of meaningful (or all if debug) parentrevs for rev.
1351 """
1351 """
1352 parents = ctx.parents()
1352 parents = ctx.parents()
1353 if len(parents) > 1:
1353 if len(parents) > 1:
1354 return parents
1354 return parents
1355 if self.ui.debugflag:
1355 if self.ui.debugflag:
1356 return [parents[0], self.repo['null']]
1356 return [parents[0], self.repo['null']]
1357 if parents[0].rev() >= ctx.rev() - 1:
1357 if parents[0].rev() >= ctx.rev() - 1:
1358 return []
1358 return []
1359 return parents
1359 return parents
1360
1360
1361 def _show(self, ctx, copies, matchfn, props):
1361 def _show(self, ctx, copies, matchfn, props):
1362 '''show a single changeset or file revision'''
1362 '''show a single changeset or file revision'''
1363
1363
1364 showlist = templatekw.showlist
1364 showlist = templatekw.showlist
1365
1365
1366 # showparents() behaviour depends on ui trace level which
1366 # showparents() behaviour depends on ui trace level which
1367 # causes unexpected behaviours at templating level and makes
1367 # causes unexpected behaviours at templating level and makes
1368 # it harder to extract it in a standalone function. Its
1368 # it harder to extract it in a standalone function. Its
1369 # behaviour cannot be changed so leave it here for now.
1369 # behaviour cannot be changed so leave it here for now.
1370 def showparents(**args):
1370 def showparents(**args):
1371 ctx = args['ctx']
1371 ctx = args['ctx']
1372 parents = [[('rev', p.rev()),
1372 parents = [[('rev', p.rev()),
1373 ('node', p.hex()),
1373 ('node', p.hex()),
1374 ('phase', p.phasestr())]
1374 ('phase', p.phasestr())]
1375 for p in self._meaningful_parentrevs(ctx)]
1375 for p in self._meaningful_parentrevs(ctx)]
1376 return showlist('parent', parents, **args)
1376 return showlist('parent', parents, **args)
1377
1377
1378 props = props.copy()
1378 props = props.copy()
1379 props.update(templatekw.keywords)
1379 props.update(templatekw.keywords)
1380 props['parents'] = showparents
1380 props['parents'] = showparents
1381 props['templ'] = self.t
1381 props['templ'] = self.t
1382 props['ctx'] = ctx
1382 props['ctx'] = ctx
1383 props['repo'] = self.repo
1383 props['repo'] = self.repo
1384 props['revcache'] = {'copies': copies}
1384 props['revcache'] = {'copies': copies}
1385 props['cache'] = self.cache
1385 props['cache'] = self.cache
1386
1386
1387 # find correct templates for current mode
1387 # find correct templates for current mode
1388
1388
1389 tmplmodes = [
1389 tmplmodes = [
1390 (True, None),
1390 (True, None),
1391 (self.ui.verbose, 'verbose'),
1391 (self.ui.verbose, 'verbose'),
1392 (self.ui.quiet, 'quiet'),
1392 (self.ui.quiet, 'quiet'),
1393 (self.ui.debugflag, 'debug'),
1393 (self.ui.debugflag, 'debug'),
1394 ]
1394 ]
1395
1395
1396 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
1396 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
1397 for mode, postfix in tmplmodes:
1397 for mode, postfix in tmplmodes:
1398 for type in types:
1398 for type in types:
1399 cur = postfix and ('%s_%s' % (type, postfix)) or type
1399 cur = postfix and ('%s_%s' % (type, postfix)) or type
1400 if mode and cur in self.t:
1400 if mode and cur in self.t:
1401 types[type] = cur
1401 types[type] = cur
1402
1402
1403 try:
1403 try:
1404
1404
1405 # write header
1405 # write header
1406 if types['header']:
1406 if types['header']:
1407 h = templater.stringify(self.t(types['header'], **props))
1407 h = templater.stringify(self.t(types['header'], **props))
1408 if self.buffered:
1408 if self.buffered:
1409 self.header[ctx.rev()] = h
1409 self.header[ctx.rev()] = h
1410 else:
1410 else:
1411 if self.lastheader != h:
1411 if self.lastheader != h:
1412 self.lastheader = h
1412 self.lastheader = h
1413 self.ui.write(h)
1413 self.ui.write(h)
1414
1414
1415 # write changeset metadata, then patch if requested
1415 # write changeset metadata, then patch if requested
1416 key = types['changeset']
1416 key = types['changeset']
1417 self.ui.write(templater.stringify(self.t(key, **props)))
1417 self.ui.write(templater.stringify(self.t(key, **props)))
1418 self.showpatch(ctx.node(), matchfn)
1418 self.showpatch(ctx.node(), matchfn)
1419
1419
1420 if types['footer']:
1420 if types['footer']:
1421 if not self.footer:
1421 if not self.footer:
1422 self.footer = templater.stringify(self.t(types['footer'],
1422 self.footer = templater.stringify(self.t(types['footer'],
1423 **props))
1423 **props))
1424
1424
1425 except KeyError, inst:
1425 except KeyError, inst:
1426 msg = _("%s: no key named '%s'")
1426 msg = _("%s: no key named '%s'")
1427 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
1427 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
1428 except SyntaxError, inst:
1428 except SyntaxError, inst:
1429 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
1429 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
1430
1430
1431 def gettemplate(ui, tmpl, style):
1431 def gettemplate(ui, tmpl, style):
1432 """
1432 """
1433 Find the template matching the given template spec or style.
1433 Find the template matching the given template spec or style.
1434 """
1434 """
1435
1435
1436 # ui settings
1436 # ui settings
1437 if not tmpl and not style: # template are stronger than style
1437 if not tmpl and not style: # template are stronger than style
1438 tmpl = ui.config('ui', 'logtemplate')
1438 tmpl = ui.config('ui', 'logtemplate')
1439 if tmpl:
1439 if tmpl:
1440 try:
1440 try:
1441 tmpl = templater.parsestring(tmpl)
1441 tmpl = templater.parsestring(tmpl)
1442 except SyntaxError:
1442 except SyntaxError:
1443 tmpl = templater.parsestring(tmpl, quoted=False)
1443 tmpl = templater.parsestring(tmpl, quoted=False)
1444 return tmpl, None
1444 return tmpl, None
1445 else:
1445 else:
1446 style = util.expandpath(ui.config('ui', 'style', ''))
1446 style = util.expandpath(ui.config('ui', 'style', ''))
1447
1447
1448 if not tmpl and style:
1448 if not tmpl and style:
1449 mapfile = style
1449 mapfile = style
1450 if not os.path.split(mapfile)[0]:
1450 if not os.path.split(mapfile)[0]:
1451 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1451 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1452 or templater.templatepath(mapfile))
1452 or templater.templatepath(mapfile))
1453 if mapname:
1453 if mapname:
1454 mapfile = mapname
1454 mapfile = mapname
1455 return None, mapfile
1455 return None, mapfile
1456
1456
1457 if not tmpl:
1457 if not tmpl:
1458 return None, None
1458 return None, None
1459
1459
1460 # looks like a literal template?
1460 # looks like a literal template?
1461 if '{' in tmpl:
1461 if '{' in tmpl:
1462 return tmpl, None
1462 return tmpl, None
1463
1463
1464 # perhaps a stock style?
1464 # perhaps a stock style?
1465 if not os.path.split(tmpl)[0]:
1465 if not os.path.split(tmpl)[0]:
1466 mapname = (templater.templatepath('map-cmdline.' + tmpl)
1466 mapname = (templater.templatepath('map-cmdline.' + tmpl)
1467 or templater.templatepath(tmpl))
1467 or templater.templatepath(tmpl))
1468 if mapname and os.path.isfile(mapname):
1468 if mapname and os.path.isfile(mapname):
1469 return None, mapname
1469 return None, mapname
1470
1470
1471 # perhaps it's a reference to [templates]
1471 # perhaps it's a reference to [templates]
1472 t = ui.config('templates', tmpl)
1472 t = ui.config('templates', tmpl)
1473 if t:
1473 if t:
1474 try:
1474 try:
1475 tmpl = templater.parsestring(t)
1475 tmpl = templater.parsestring(t)
1476 except SyntaxError:
1476 except SyntaxError:
1477 tmpl = templater.parsestring(t, quoted=False)
1477 tmpl = templater.parsestring(t, quoted=False)
1478 return tmpl, None
1478 return tmpl, None
1479
1479
1480 if tmpl == 'list':
1480 if tmpl == 'list':
1481 ui.write(_("available styles: %s\n") % templater.stylelist())
1481 ui.write(_("available styles: %s\n") % templater.stylelist())
1482 raise util.Abort(_("specify a template"))
1482 raise util.Abort(_("specify a template"))
1483
1483
1484 # perhaps it's a path to a map or a template
1484 # perhaps it's a path to a map or a template
1485 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
1485 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
1486 # is it a mapfile for a style?
1486 # is it a mapfile for a style?
1487 if os.path.basename(tmpl).startswith("map-"):
1487 if os.path.basename(tmpl).startswith("map-"):
1488 return None, os.path.realpath(tmpl)
1488 return None, os.path.realpath(tmpl)
1489 tmpl = open(tmpl).read()
1489 tmpl = open(tmpl).read()
1490 return tmpl, None
1490 return tmpl, None
1491
1491
1492 # constant string?
1492 # constant string?
1493 return tmpl, None
1493 return tmpl, None
1494
1494
1495 def show_changeset(ui, repo, opts, buffered=False):
1495 def show_changeset(ui, repo, opts, buffered=False):
1496 """show one changeset using template or regular display.
1496 """show one changeset using template or regular display.
1497
1497
1498 Display format will be the first non-empty hit of:
1498 Display format will be the first non-empty hit of:
1499 1. option 'template'
1499 1. option 'template'
1500 2. option 'style'
1500 2. option 'style'
1501 3. [ui] setting 'logtemplate'
1501 3. [ui] setting 'logtemplate'
1502 4. [ui] setting 'style'
1502 4. [ui] setting 'style'
1503 If all of these values are either the unset or the empty string,
1503 If all of these values are either the unset or the empty string,
1504 regular display via changeset_printer() is done.
1504 regular display via changeset_printer() is done.
1505 """
1505 """
1506 # options
1506 # options
1507 matchfn = None
1507 matchfn = None
1508 if opts.get('patch') or opts.get('stat'):
1508 if opts.get('patch') or opts.get('stat'):
1509 matchfn = scmutil.matchall(repo)
1509 matchfn = scmutil.matchall(repo)
1510
1510
1511 if opts.get('template') == 'json':
1511 if opts.get('template') == 'json':
1512 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1512 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1513
1513
1514 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1514 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1515
1515
1516 if not tmpl and not mapfile:
1516 if not tmpl and not mapfile:
1517 return changeset_printer(ui, repo, matchfn, opts, buffered)
1517 return changeset_printer(ui, repo, matchfn, opts, buffered)
1518
1518
1519 try:
1519 try:
1520 t = changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile,
1520 t = changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile,
1521 buffered)
1521 buffered)
1522 except SyntaxError, inst:
1522 except SyntaxError, inst:
1523 raise util.Abort(inst.args[0])
1523 raise util.Abort(inst.args[0])
1524 return t
1524 return t
1525
1525
1526 def showmarker(ui, marker):
1526 def showmarker(ui, marker):
1527 """utility function to display obsolescence marker in a readable way
1527 """utility function to display obsolescence marker in a readable way
1528
1528
1529 To be used by debug function."""
1529 To be used by debug function."""
1530 ui.write(hex(marker.precnode()))
1530 ui.write(hex(marker.precnode()))
1531 for repl in marker.succnodes():
1531 for repl in marker.succnodes():
1532 ui.write(' ')
1532 ui.write(' ')
1533 ui.write(hex(repl))
1533 ui.write(hex(repl))
1534 ui.write(' %X ' % marker.flags())
1534 ui.write(' %X ' % marker.flags())
1535 parents = marker.parentnodes()
1535 parents = marker.parentnodes()
1536 if parents is not None:
1536 if parents is not None:
1537 ui.write('{%s} ' % ', '.join(hex(p) for p in parents))
1537 ui.write('{%s} ' % ', '.join(hex(p) for p in parents))
1538 ui.write('(%s) ' % util.datestr(marker.date()))
1538 ui.write('(%s) ' % util.datestr(marker.date()))
1539 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1539 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1540 sorted(marker.metadata().items())
1540 sorted(marker.metadata().items())
1541 if t[0] != 'date')))
1541 if t[0] != 'date')))
1542 ui.write('\n')
1542 ui.write('\n')
1543
1543
1544 def finddate(ui, repo, date):
1544 def finddate(ui, repo, date):
1545 """Find the tipmost changeset that matches the given date spec"""
1545 """Find the tipmost changeset that matches the given date spec"""
1546
1546
1547 df = util.matchdate(date)
1547 df = util.matchdate(date)
1548 m = scmutil.matchall(repo)
1548 m = scmutil.matchall(repo)
1549 results = {}
1549 results = {}
1550
1550
1551 def prep(ctx, fns):
1551 def prep(ctx, fns):
1552 d = ctx.date()
1552 d = ctx.date()
1553 if df(d[0]):
1553 if df(d[0]):
1554 results[ctx.rev()] = d
1554 results[ctx.rev()] = d
1555
1555
1556 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1556 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1557 rev = ctx.rev()
1557 rev = ctx.rev()
1558 if rev in results:
1558 if rev in results:
1559 ui.status(_("found revision %s from %s\n") %
1559 ui.status(_("found revision %s from %s\n") %
1560 (rev, util.datestr(results[rev])))
1560 (rev, util.datestr(results[rev])))
1561 return str(rev)
1561 return str(rev)
1562
1562
1563 raise util.Abort(_("revision matching date not found"))
1563 raise util.Abort(_("revision matching date not found"))
1564
1564
1565 def increasingwindows(windowsize=8, sizelimit=512):
1565 def increasingwindows(windowsize=8, sizelimit=512):
1566 while True:
1566 while True:
1567 yield windowsize
1567 yield windowsize
1568 if windowsize < sizelimit:
1568 if windowsize < sizelimit:
1569 windowsize *= 2
1569 windowsize *= 2
1570
1570
1571 class FileWalkError(Exception):
1571 class FileWalkError(Exception):
1572 pass
1572 pass
1573
1573
1574 def walkfilerevs(repo, match, follow, revs, fncache):
1574 def walkfilerevs(repo, match, follow, revs, fncache):
1575 '''Walks the file history for the matched files.
1575 '''Walks the file history for the matched files.
1576
1576
1577 Returns the changeset revs that are involved in the file history.
1577 Returns the changeset revs that are involved in the file history.
1578
1578
1579 Throws FileWalkError if the file history can't be walked using
1579 Throws FileWalkError if the file history can't be walked using
1580 filelogs alone.
1580 filelogs alone.
1581 '''
1581 '''
1582 wanted = set()
1582 wanted = set()
1583 copies = []
1583 copies = []
1584 minrev, maxrev = min(revs), max(revs)
1584 minrev, maxrev = min(revs), max(revs)
1585 def filerevgen(filelog, last):
1585 def filerevgen(filelog, last):
1586 """
1586 """
1587 Only files, no patterns. Check the history of each file.
1587 Only files, no patterns. Check the history of each file.
1588
1588
1589 Examines filelog entries within minrev, maxrev linkrev range
1589 Examines filelog entries within minrev, maxrev linkrev range
1590 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1590 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1591 tuples in backwards order
1591 tuples in backwards order
1592 """
1592 """
1593 cl_count = len(repo)
1593 cl_count = len(repo)
1594 revs = []
1594 revs = []
1595 for j in xrange(0, last + 1):
1595 for j in xrange(0, last + 1):
1596 linkrev = filelog.linkrev(j)
1596 linkrev = filelog.linkrev(j)
1597 if linkrev < minrev:
1597 if linkrev < minrev:
1598 continue
1598 continue
1599 # only yield rev for which we have the changelog, it can
1599 # only yield rev for which we have the changelog, it can
1600 # happen while doing "hg log" during a pull or commit
1600 # happen while doing "hg log" during a pull or commit
1601 if linkrev >= cl_count:
1601 if linkrev >= cl_count:
1602 break
1602 break
1603
1603
1604 parentlinkrevs = []
1604 parentlinkrevs = []
1605 for p in filelog.parentrevs(j):
1605 for p in filelog.parentrevs(j):
1606 if p != nullrev:
1606 if p != nullrev:
1607 parentlinkrevs.append(filelog.linkrev(p))
1607 parentlinkrevs.append(filelog.linkrev(p))
1608 n = filelog.node(j)
1608 n = filelog.node(j)
1609 revs.append((linkrev, parentlinkrevs,
1609 revs.append((linkrev, parentlinkrevs,
1610 follow and filelog.renamed(n)))
1610 follow and filelog.renamed(n)))
1611
1611
1612 return reversed(revs)
1612 return reversed(revs)
1613 def iterfiles():
1613 def iterfiles():
1614 pctx = repo['.']
1614 pctx = repo['.']
1615 for filename in match.files():
1615 for filename in match.files():
1616 if follow:
1616 if follow:
1617 if filename not in pctx:
1617 if filename not in pctx:
1618 raise util.Abort(_('cannot follow file not in parent '
1618 raise util.Abort(_('cannot follow file not in parent '
1619 'revision: "%s"') % filename)
1619 'revision: "%s"') % filename)
1620 yield filename, pctx[filename].filenode()
1620 yield filename, pctx[filename].filenode()
1621 else:
1621 else:
1622 yield filename, None
1622 yield filename, None
1623 for filename_node in copies:
1623 for filename_node in copies:
1624 yield filename_node
1624 yield filename_node
1625
1625
1626 for file_, node in iterfiles():
1626 for file_, node in iterfiles():
1627 filelog = repo.file(file_)
1627 filelog = repo.file(file_)
1628 if not len(filelog):
1628 if not len(filelog):
1629 if node is None:
1629 if node is None:
1630 # A zero count may be a directory or deleted file, so
1630 # A zero count may be a directory or deleted file, so
1631 # try to find matching entries on the slow path.
1631 # try to find matching entries on the slow path.
1632 if follow:
1632 if follow:
1633 raise util.Abort(
1633 raise util.Abort(
1634 _('cannot follow nonexistent file: "%s"') % file_)
1634 _('cannot follow nonexistent file: "%s"') % file_)
1635 raise FileWalkError("Cannot walk via filelog")
1635 raise FileWalkError("Cannot walk via filelog")
1636 else:
1636 else:
1637 continue
1637 continue
1638
1638
1639 if node is None:
1639 if node is None:
1640 last = len(filelog) - 1
1640 last = len(filelog) - 1
1641 else:
1641 else:
1642 last = filelog.rev(node)
1642 last = filelog.rev(node)
1643
1643
1644
1644
1645 # keep track of all ancestors of the file
1645 # keep track of all ancestors of the file
1646 ancestors = set([filelog.linkrev(last)])
1646 ancestors = set([filelog.linkrev(last)])
1647
1647
1648 # iterate from latest to oldest revision
1648 # iterate from latest to oldest revision
1649 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1649 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1650 if not follow:
1650 if not follow:
1651 if rev > maxrev:
1651 if rev > maxrev:
1652 continue
1652 continue
1653 else:
1653 else:
1654 # Note that last might not be the first interesting
1654 # Note that last might not be the first interesting
1655 # rev to us:
1655 # rev to us:
1656 # if the file has been changed after maxrev, we'll
1656 # if the file has been changed after maxrev, we'll
1657 # have linkrev(last) > maxrev, and we still need
1657 # have linkrev(last) > maxrev, and we still need
1658 # to explore the file graph
1658 # to explore the file graph
1659 if rev not in ancestors:
1659 if rev not in ancestors:
1660 continue
1660 continue
1661 # XXX insert 1327 fix here
1661 # XXX insert 1327 fix here
1662 if flparentlinkrevs:
1662 if flparentlinkrevs:
1663 ancestors.update(flparentlinkrevs)
1663 ancestors.update(flparentlinkrevs)
1664
1664
1665 fncache.setdefault(rev, []).append(file_)
1665 fncache.setdefault(rev, []).append(file_)
1666 wanted.add(rev)
1666 wanted.add(rev)
1667 if copied:
1667 if copied:
1668 copies.append(copied)
1668 copies.append(copied)
1669
1669
1670 return wanted
1670 return wanted
1671
1671
1672 class _followfilter(object):
1672 class _followfilter(object):
1673 def __init__(self, repo, onlyfirst=False):
1673 def __init__(self, repo, onlyfirst=False):
1674 self.repo = repo
1674 self.repo = repo
1675 self.startrev = nullrev
1675 self.startrev = nullrev
1676 self.roots = set()
1676 self.roots = set()
1677 self.onlyfirst = onlyfirst
1677 self.onlyfirst = onlyfirst
1678
1678
1679 def match(self, rev):
1679 def match(self, rev):
1680 def realparents(rev):
1680 def realparents(rev):
1681 if self.onlyfirst:
1681 if self.onlyfirst:
1682 return self.repo.changelog.parentrevs(rev)[0:1]
1682 return self.repo.changelog.parentrevs(rev)[0:1]
1683 else:
1683 else:
1684 return filter(lambda x: x != nullrev,
1684 return filter(lambda x: x != nullrev,
1685 self.repo.changelog.parentrevs(rev))
1685 self.repo.changelog.parentrevs(rev))
1686
1686
1687 if self.startrev == nullrev:
1687 if self.startrev == nullrev:
1688 self.startrev = rev
1688 self.startrev = rev
1689 return True
1689 return True
1690
1690
1691 if rev > self.startrev:
1691 if rev > self.startrev:
1692 # forward: all descendants
1692 # forward: all descendants
1693 if not self.roots:
1693 if not self.roots:
1694 self.roots.add(self.startrev)
1694 self.roots.add(self.startrev)
1695 for parent in realparents(rev):
1695 for parent in realparents(rev):
1696 if parent in self.roots:
1696 if parent in self.roots:
1697 self.roots.add(rev)
1697 self.roots.add(rev)
1698 return True
1698 return True
1699 else:
1699 else:
1700 # backwards: all parents
1700 # backwards: all parents
1701 if not self.roots:
1701 if not self.roots:
1702 self.roots.update(realparents(self.startrev))
1702 self.roots.update(realparents(self.startrev))
1703 if rev in self.roots:
1703 if rev in self.roots:
1704 self.roots.remove(rev)
1704 self.roots.remove(rev)
1705 self.roots.update(realparents(rev))
1705 self.roots.update(realparents(rev))
1706 return True
1706 return True
1707
1707
1708 return False
1708 return False
1709
1709
1710 def walkchangerevs(repo, match, opts, prepare):
1710 def walkchangerevs(repo, match, opts, prepare):
1711 '''Iterate over files and the revs in which they changed.
1711 '''Iterate over files and the revs in which they changed.
1712
1712
1713 Callers most commonly need to iterate backwards over the history
1713 Callers most commonly need to iterate backwards over the history
1714 in which they are interested. Doing so has awful (quadratic-looking)
1714 in which they are interested. Doing so has awful (quadratic-looking)
1715 performance, so we use iterators in a "windowed" way.
1715 performance, so we use iterators in a "windowed" way.
1716
1716
1717 We walk a window of revisions in the desired order. Within the
1717 We walk a window of revisions in the desired order. Within the
1718 window, we first walk forwards to gather data, then in the desired
1718 window, we first walk forwards to gather data, then in the desired
1719 order (usually backwards) to display it.
1719 order (usually backwards) to display it.
1720
1720
1721 This function returns an iterator yielding contexts. Before
1721 This function returns an iterator yielding contexts. Before
1722 yielding each context, the iterator will first call the prepare
1722 yielding each context, the iterator will first call the prepare
1723 function on each context in the window in forward order.'''
1723 function on each context in the window in forward order.'''
1724
1724
1725 follow = opts.get('follow') or opts.get('follow_first')
1725 follow = opts.get('follow') or opts.get('follow_first')
1726 revs = _logrevs(repo, opts)
1726 revs = _logrevs(repo, opts)
1727 if not revs:
1727 if not revs:
1728 return []
1728 return []
1729 wanted = set()
1729 wanted = set()
1730 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1730 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1731 fncache = {}
1731 fncache = {}
1732 change = repo.changectx
1732 change = repo.changectx
1733
1733
1734 # First step is to fill wanted, the set of revisions that we want to yield.
1734 # First step is to fill wanted, the set of revisions that we want to yield.
1735 # When it does not induce extra cost, we also fill fncache for revisions in
1735 # When it does not induce extra cost, we also fill fncache for revisions in
1736 # wanted: a cache of filenames that were changed (ctx.files()) and that
1736 # wanted: a cache of filenames that were changed (ctx.files()) and that
1737 # match the file filtering conditions.
1737 # match the file filtering conditions.
1738
1738
1739 if match.always():
1739 if match.always():
1740 # No files, no patterns. Display all revs.
1740 # No files, no patterns. Display all revs.
1741 wanted = revs
1741 wanted = revs
1742
1742
1743 if not slowpath and match.files():
1743 if not slowpath and match.files():
1744 # We only have to read through the filelog to find wanted revisions
1744 # We only have to read through the filelog to find wanted revisions
1745
1745
1746 try:
1746 try:
1747 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1747 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1748 except FileWalkError:
1748 except FileWalkError:
1749 slowpath = True
1749 slowpath = True
1750
1750
1751 # We decided to fall back to the slowpath because at least one
1751 # We decided to fall back to the slowpath because at least one
1752 # of the paths was not a file. Check to see if at least one of them
1752 # of the paths was not a file. Check to see if at least one of them
1753 # existed in history, otherwise simply return
1753 # existed in history, otherwise simply return
1754 for path in match.files():
1754 for path in match.files():
1755 if path == '.' or path in repo.store:
1755 if path == '.' or path in repo.store:
1756 break
1756 break
1757 else:
1757 else:
1758 return []
1758 return []
1759
1759
1760 if slowpath:
1760 if slowpath:
1761 # We have to read the changelog to match filenames against
1761 # We have to read the changelog to match filenames against
1762 # changed files
1762 # changed files
1763
1763
1764 if follow:
1764 if follow:
1765 raise util.Abort(_('can only follow copies/renames for explicit '
1765 raise util.Abort(_('can only follow copies/renames for explicit '
1766 'filenames'))
1766 'filenames'))
1767
1767
1768 # The slow path checks files modified in every changeset.
1768 # The slow path checks files modified in every changeset.
1769 # This is really slow on large repos, so compute the set lazily.
1769 # This is really slow on large repos, so compute the set lazily.
1770 class lazywantedset(object):
1770 class lazywantedset(object):
1771 def __init__(self):
1771 def __init__(self):
1772 self.set = set()
1772 self.set = set()
1773 self.revs = set(revs)
1773 self.revs = set(revs)
1774
1774
1775 # No need to worry about locality here because it will be accessed
1775 # No need to worry about locality here because it will be accessed
1776 # in the same order as the increasing window below.
1776 # in the same order as the increasing window below.
1777 def __contains__(self, value):
1777 def __contains__(self, value):
1778 if value in self.set:
1778 if value in self.set:
1779 return True
1779 return True
1780 elif not value in self.revs:
1780 elif not value in self.revs:
1781 return False
1781 return False
1782 else:
1782 else:
1783 self.revs.discard(value)
1783 self.revs.discard(value)
1784 ctx = change(value)
1784 ctx = change(value)
1785 matches = filter(match, ctx.files())
1785 matches = filter(match, ctx.files())
1786 if matches:
1786 if matches:
1787 fncache[value] = matches
1787 fncache[value] = matches
1788 self.set.add(value)
1788 self.set.add(value)
1789 return True
1789 return True
1790 return False
1790 return False
1791
1791
1792 def discard(self, value):
1792 def discard(self, value):
1793 self.revs.discard(value)
1793 self.revs.discard(value)
1794 self.set.discard(value)
1794 self.set.discard(value)
1795
1795
1796 wanted = lazywantedset()
1796 wanted = lazywantedset()
1797
1797
1798 # it might be worthwhile to do this in the iterator if the rev range
1798 # it might be worthwhile to do this in the iterator if the rev range
1799 # is descending and the prune args are all within that range
1799 # is descending and the prune args are all within that range
1800 for rev in opts.get('prune', ()):
1800 for rev in opts.get('prune', ()):
1801 rev = repo[rev].rev()
1801 rev = repo[rev].rev()
1802 ff = _followfilter(repo)
1802 ff = _followfilter(repo)
1803 stop = min(revs[0], revs[-1])
1803 stop = min(revs[0], revs[-1])
1804 for x in xrange(rev, stop - 1, -1):
1804 for x in xrange(rev, stop - 1, -1):
1805 if ff.match(x):
1805 if ff.match(x):
1806 wanted = wanted - [x]
1806 wanted = wanted - [x]
1807
1807
1808 # Now that wanted is correctly initialized, we can iterate over the
1808 # Now that wanted is correctly initialized, we can iterate over the
1809 # revision range, yielding only revisions in wanted.
1809 # revision range, yielding only revisions in wanted.
1810 def iterate():
1810 def iterate():
1811 if follow and not match.files():
1811 if follow and not match.files():
1812 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1812 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1813 def want(rev):
1813 def want(rev):
1814 return ff.match(rev) and rev in wanted
1814 return ff.match(rev) and rev in wanted
1815 else:
1815 else:
1816 def want(rev):
1816 def want(rev):
1817 return rev in wanted
1817 return rev in wanted
1818
1818
1819 it = iter(revs)
1819 it = iter(revs)
1820 stopiteration = False
1820 stopiteration = False
1821 for windowsize in increasingwindows():
1821 for windowsize in increasingwindows():
1822 nrevs = []
1822 nrevs = []
1823 for i in xrange(windowsize):
1823 for i in xrange(windowsize):
1824 try:
1824 try:
1825 rev = it.next()
1825 rev = it.next()
1826 if want(rev):
1826 if want(rev):
1827 nrevs.append(rev)
1827 nrevs.append(rev)
1828 except (StopIteration):
1828 except (StopIteration):
1829 stopiteration = True
1829 stopiteration = True
1830 break
1830 break
1831 for rev in sorted(nrevs):
1831 for rev in sorted(nrevs):
1832 fns = fncache.get(rev)
1832 fns = fncache.get(rev)
1833 ctx = change(rev)
1833 ctx = change(rev)
1834 if not fns:
1834 if not fns:
1835 def fns_generator():
1835 def fns_generator():
1836 for f in ctx.files():
1836 for f in ctx.files():
1837 if match(f):
1837 if match(f):
1838 yield f
1838 yield f
1839 fns = fns_generator()
1839 fns = fns_generator()
1840 prepare(ctx, fns)
1840 prepare(ctx, fns)
1841 for rev in nrevs:
1841 for rev in nrevs:
1842 yield change(rev)
1842 yield change(rev)
1843
1843
1844 if stopiteration:
1844 if stopiteration:
1845 break
1845 break
1846
1846
1847 return iterate()
1847 return iterate()
1848
1848
1849 def _makefollowlogfilematcher(repo, files, followfirst):
1849 def _makefollowlogfilematcher(repo, files, followfirst):
1850 # When displaying a revision with --patch --follow FILE, we have
1850 # When displaying a revision with --patch --follow FILE, we have
1851 # to know which file of the revision must be diffed. With
1851 # to know which file of the revision must be diffed. With
1852 # --follow, we want the names of the ancestors of FILE in the
1852 # --follow, we want the names of the ancestors of FILE in the
1853 # revision, stored in "fcache". "fcache" is populated by
1853 # revision, stored in "fcache". "fcache" is populated by
1854 # reproducing the graph traversal already done by --follow revset
1854 # reproducing the graph traversal already done by --follow revset
1855 # and relating linkrevs to file names (which is not "correct" but
1855 # and relating linkrevs to file names (which is not "correct" but
1856 # good enough).
1856 # good enough).
1857 fcache = {}
1857 fcache = {}
1858 fcacheready = [False]
1858 fcacheready = [False]
1859 pctx = repo['.']
1859 pctx = repo['.']
1860
1860
1861 def populate():
1861 def populate():
1862 for fn in files:
1862 for fn in files:
1863 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1863 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1864 for c in i:
1864 for c in i:
1865 fcache.setdefault(c.linkrev(), set()).add(c.path())
1865 fcache.setdefault(c.linkrev(), set()).add(c.path())
1866
1866
1867 def filematcher(rev):
1867 def filematcher(rev):
1868 if not fcacheready[0]:
1868 if not fcacheready[0]:
1869 # Lazy initialization
1869 # Lazy initialization
1870 fcacheready[0] = True
1870 fcacheready[0] = True
1871 populate()
1871 populate()
1872 return scmutil.matchfiles(repo, fcache.get(rev, []))
1872 return scmutil.matchfiles(repo, fcache.get(rev, []))
1873
1873
1874 return filematcher
1874 return filematcher
1875
1875
1876 def _makenofollowlogfilematcher(repo, pats, opts):
1876 def _makenofollowlogfilematcher(repo, pats, opts):
1877 '''hook for extensions to override the filematcher for non-follow cases'''
1877 '''hook for extensions to override the filematcher for non-follow cases'''
1878 return None
1878 return None
1879
1879
1880 def _makelogrevset(repo, pats, opts, revs):
1880 def _makelogrevset(repo, pats, opts, revs):
1881 """Return (expr, filematcher) where expr is a revset string built
1881 """Return (expr, filematcher) where expr is a revset string built
1882 from log options and file patterns or None. If --stat or --patch
1882 from log options and file patterns or None. If --stat or --patch
1883 are not passed filematcher is None. Otherwise it is a callable
1883 are not passed filematcher is None. Otherwise it is a callable
1884 taking a revision number and returning a match objects filtering
1884 taking a revision number and returning a match objects filtering
1885 the files to be detailed when displaying the revision.
1885 the files to be detailed when displaying the revision.
1886 """
1886 """
1887 opt2revset = {
1887 opt2revset = {
1888 'no_merges': ('not merge()', None),
1888 'no_merges': ('not merge()', None),
1889 'only_merges': ('merge()', None),
1889 'only_merges': ('merge()', None),
1890 '_ancestors': ('ancestors(%(val)s)', None),
1890 '_ancestors': ('ancestors(%(val)s)', None),
1891 '_fancestors': ('_firstancestors(%(val)s)', None),
1891 '_fancestors': ('_firstancestors(%(val)s)', None),
1892 '_descendants': ('descendants(%(val)s)', None),
1892 '_descendants': ('descendants(%(val)s)', None),
1893 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1893 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1894 '_matchfiles': ('_matchfiles(%(val)s)', None),
1894 '_matchfiles': ('_matchfiles(%(val)s)', None),
1895 'date': ('date(%(val)r)', None),
1895 'date': ('date(%(val)r)', None),
1896 'branch': ('branch(%(val)r)', ' or '),
1896 'branch': ('branch(%(val)r)', ' or '),
1897 '_patslog': ('filelog(%(val)r)', ' or '),
1897 '_patslog': ('filelog(%(val)r)', ' or '),
1898 '_patsfollow': ('follow(%(val)r)', ' or '),
1898 '_patsfollow': ('follow(%(val)r)', ' or '),
1899 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1899 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1900 'keyword': ('keyword(%(val)r)', ' or '),
1900 'keyword': ('keyword(%(val)r)', ' or '),
1901 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1901 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1902 'user': ('user(%(val)r)', ' or '),
1902 'user': ('user(%(val)r)', ' or '),
1903 }
1903 }
1904
1904
1905 opts = dict(opts)
1905 opts = dict(opts)
1906 # follow or not follow?
1906 # follow or not follow?
1907 follow = opts.get('follow') or opts.get('follow_first')
1907 follow = opts.get('follow') or opts.get('follow_first')
1908 if opts.get('follow_first'):
1908 if opts.get('follow_first'):
1909 followfirst = 1
1909 followfirst = 1
1910 else:
1910 else:
1911 followfirst = 0
1911 followfirst = 0
1912 # --follow with FILE behaviour depends on revs...
1912 # --follow with FILE behaviour depends on revs...
1913 it = iter(revs)
1913 it = iter(revs)
1914 startrev = it.next()
1914 startrev = it.next()
1915 try:
1915 try:
1916 followdescendants = startrev < it.next()
1916 followdescendants = startrev < it.next()
1917 except (StopIteration):
1917 except (StopIteration):
1918 followdescendants = False
1918 followdescendants = False
1919
1919
1920 # branch and only_branch are really aliases and must be handled at
1920 # branch and only_branch are really aliases and must be handled at
1921 # the same time
1921 # the same time
1922 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1922 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1923 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1923 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1924 # pats/include/exclude are passed to match.match() directly in
1924 # pats/include/exclude are passed to match.match() directly in
1925 # _matchfiles() revset but walkchangerevs() builds its matcher with
1925 # _matchfiles() revset but walkchangerevs() builds its matcher with
1926 # scmutil.match(). The difference is input pats are globbed on
1926 # scmutil.match(). The difference is input pats are globbed on
1927 # platforms without shell expansion (windows).
1927 # platforms without shell expansion (windows).
1928 pctx = repo[None]
1928 pctx = repo[None]
1929 match, pats = scmutil.matchandpats(pctx, pats, opts)
1929 match, pats = scmutil.matchandpats(pctx, pats, opts)
1930 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1930 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1931 if not slowpath:
1931 if not slowpath:
1932 for f in match.files():
1932 for f in match.files():
1933 if follow and f not in pctx:
1933 if follow and f not in pctx:
1934 # If the file exists, it may be a directory, so let it
1934 # If the file exists, it may be a directory, so let it
1935 # take the slow path.
1935 # take the slow path.
1936 if os.path.exists(repo.wjoin(f)):
1936 if os.path.exists(repo.wjoin(f)):
1937 slowpath = True
1937 slowpath = True
1938 continue
1938 continue
1939 else:
1939 else:
1940 raise util.Abort(_('cannot follow file not in parent '
1940 raise util.Abort(_('cannot follow file not in parent '
1941 'revision: "%s"') % f)
1941 'revision: "%s"') % f)
1942 filelog = repo.file(f)
1942 filelog = repo.file(f)
1943 if not filelog:
1943 if not filelog:
1944 # A zero count may be a directory or deleted file, so
1944 # A zero count may be a directory or deleted file, so
1945 # try to find matching entries on the slow path.
1945 # try to find matching entries on the slow path.
1946 if follow:
1946 if follow:
1947 raise util.Abort(
1947 raise util.Abort(
1948 _('cannot follow nonexistent file: "%s"') % f)
1948 _('cannot follow nonexistent file: "%s"') % f)
1949 slowpath = True
1949 slowpath = True
1950
1950
1951 # We decided to fall back to the slowpath because at least one
1951 # We decided to fall back to the slowpath because at least one
1952 # of the paths was not a file. Check to see if at least one of them
1952 # of the paths was not a file. Check to see if at least one of them
1953 # existed in history - in that case, we'll continue down the
1953 # existed in history - in that case, we'll continue down the
1954 # slowpath; otherwise, we can turn off the slowpath
1954 # slowpath; otherwise, we can turn off the slowpath
1955 if slowpath:
1955 if slowpath:
1956 for path in match.files():
1956 for path in match.files():
1957 if path == '.' or path in repo.store:
1957 if path == '.' or path in repo.store:
1958 break
1958 break
1959 else:
1959 else:
1960 slowpath = False
1960 slowpath = False
1961
1961
1962 fpats = ('_patsfollow', '_patsfollowfirst')
1962 fpats = ('_patsfollow', '_patsfollowfirst')
1963 fnopats = (('_ancestors', '_fancestors'),
1963 fnopats = (('_ancestors', '_fancestors'),
1964 ('_descendants', '_fdescendants'))
1964 ('_descendants', '_fdescendants'))
1965 if slowpath:
1965 if slowpath:
1966 # See walkchangerevs() slow path.
1966 # See walkchangerevs() slow path.
1967 #
1967 #
1968 # pats/include/exclude cannot be represented as separate
1968 # pats/include/exclude cannot be represented as separate
1969 # revset expressions as their filtering logic applies at file
1969 # revset expressions as their filtering logic applies at file
1970 # level. For instance "-I a -X a" matches a revision touching
1970 # level. For instance "-I a -X a" matches a revision touching
1971 # "a" and "b" while "file(a) and not file(b)" does
1971 # "a" and "b" while "file(a) and not file(b)" does
1972 # not. Besides, filesets are evaluated against the working
1972 # not. Besides, filesets are evaluated against the working
1973 # directory.
1973 # directory.
1974 matchargs = ['r:', 'd:relpath']
1974 matchargs = ['r:', 'd:relpath']
1975 for p in pats:
1975 for p in pats:
1976 matchargs.append('p:' + p)
1976 matchargs.append('p:' + p)
1977 for p in opts.get('include', []):
1977 for p in opts.get('include', []):
1978 matchargs.append('i:' + p)
1978 matchargs.append('i:' + p)
1979 for p in opts.get('exclude', []):
1979 for p in opts.get('exclude', []):
1980 matchargs.append('x:' + p)
1980 matchargs.append('x:' + p)
1981 matchargs = ','.join(('%r' % p) for p in matchargs)
1981 matchargs = ','.join(('%r' % p) for p in matchargs)
1982 opts['_matchfiles'] = matchargs
1982 opts['_matchfiles'] = matchargs
1983 if follow:
1983 if follow:
1984 opts[fnopats[0][followfirst]] = '.'
1984 opts[fnopats[0][followfirst]] = '.'
1985 else:
1985 else:
1986 if follow:
1986 if follow:
1987 if pats:
1987 if pats:
1988 # follow() revset interprets its file argument as a
1988 # follow() revset interprets its file argument as a
1989 # manifest entry, so use match.files(), not pats.
1989 # manifest entry, so use match.files(), not pats.
1990 opts[fpats[followfirst]] = list(match.files())
1990 opts[fpats[followfirst]] = list(match.files())
1991 else:
1991 else:
1992 op = fnopats[followdescendants][followfirst]
1992 op = fnopats[followdescendants][followfirst]
1993 opts[op] = 'rev(%d)' % startrev
1993 opts[op] = 'rev(%d)' % startrev
1994 else:
1994 else:
1995 opts['_patslog'] = list(pats)
1995 opts['_patslog'] = list(pats)
1996
1996
1997 filematcher = None
1997 filematcher = None
1998 if opts.get('patch') or opts.get('stat'):
1998 if opts.get('patch') or opts.get('stat'):
1999 # When following files, track renames via a special matcher.
1999 # When following files, track renames via a special matcher.
2000 # If we're forced to take the slowpath it means we're following
2000 # If we're forced to take the slowpath it means we're following
2001 # at least one pattern/directory, so don't bother with rename tracking.
2001 # at least one pattern/directory, so don't bother with rename tracking.
2002 if follow and not match.always() and not slowpath:
2002 if follow and not match.always() and not slowpath:
2003 # _makefollowlogfilematcher expects its files argument to be
2003 # _makefollowlogfilematcher expects its files argument to be
2004 # relative to the repo root, so use match.files(), not pats.
2004 # relative to the repo root, so use match.files(), not pats.
2005 filematcher = _makefollowlogfilematcher(repo, match.files(),
2005 filematcher = _makefollowlogfilematcher(repo, match.files(),
2006 followfirst)
2006 followfirst)
2007 else:
2007 else:
2008 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2008 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2009 if filematcher is None:
2009 if filematcher is None:
2010 filematcher = lambda rev: match
2010 filematcher = lambda rev: match
2011
2011
2012 expr = []
2012 expr = []
2013 for op, val in sorted(opts.iteritems()):
2013 for op, val in sorted(opts.iteritems()):
2014 if not val:
2014 if not val:
2015 continue
2015 continue
2016 if op not in opt2revset:
2016 if op not in opt2revset:
2017 continue
2017 continue
2018 revop, andor = opt2revset[op]
2018 revop, andor = opt2revset[op]
2019 if '%(val)' not in revop:
2019 if '%(val)' not in revop:
2020 expr.append(revop)
2020 expr.append(revop)
2021 else:
2021 else:
2022 if not isinstance(val, list):
2022 if not isinstance(val, list):
2023 e = revop % {'val': val}
2023 e = revop % {'val': val}
2024 else:
2024 else:
2025 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2025 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2026 expr.append(e)
2026 expr.append(e)
2027
2027
2028 if expr:
2028 if expr:
2029 expr = '(' + ' and '.join(expr) + ')'
2029 expr = '(' + ' and '.join(expr) + ')'
2030 else:
2030 else:
2031 expr = None
2031 expr = None
2032 return expr, filematcher
2032 return expr, filematcher
2033
2033
2034 def _logrevs(repo, opts):
2034 def _logrevs(repo, opts):
2035 # Default --rev value depends on --follow but --follow behaviour
2035 # Default --rev value depends on --follow but --follow behaviour
2036 # depends on revisions resolved from --rev...
2036 # depends on revisions resolved from --rev...
2037 follow = opts.get('follow') or opts.get('follow_first')
2037 follow = opts.get('follow') or opts.get('follow_first')
2038 if opts.get('rev'):
2038 if opts.get('rev'):
2039 revs = scmutil.revrange(repo, opts['rev'])
2039 revs = scmutil.revrange(repo, opts['rev'])
2040 elif follow and repo.dirstate.p1() == nullid:
2040 elif follow and repo.dirstate.p1() == nullid:
2041 revs = revset.baseset()
2041 revs = revset.baseset()
2042 elif follow:
2042 elif follow:
2043 revs = repo.revs('reverse(:.)')
2043 revs = repo.revs('reverse(:.)')
2044 else:
2044 else:
2045 revs = revset.spanset(repo)
2045 revs = revset.spanset(repo)
2046 revs.reverse()
2046 revs.reverse()
2047 return revs
2047 return revs
2048
2048
2049 def getgraphlogrevs(repo, pats, opts):
2049 def getgraphlogrevs(repo, pats, opts):
2050 """Return (revs, expr, filematcher) where revs is an iterable of
2050 """Return (revs, expr, filematcher) where revs is an iterable of
2051 revision numbers, expr is a revset string built from log options
2051 revision numbers, expr is a revset string built from log options
2052 and file patterns or None, and used to filter 'revs'. If --stat or
2052 and file patterns or None, and used to filter 'revs'. If --stat or
2053 --patch are not passed filematcher is None. Otherwise it is a
2053 --patch are not passed filematcher is None. Otherwise it is a
2054 callable taking a revision number and returning a match objects
2054 callable taking a revision number and returning a match objects
2055 filtering the files to be detailed when displaying the revision.
2055 filtering the files to be detailed when displaying the revision.
2056 """
2056 """
2057 limit = loglimit(opts)
2057 limit = loglimit(opts)
2058 revs = _logrevs(repo, opts)
2058 revs = _logrevs(repo, opts)
2059 if not revs:
2059 if not revs:
2060 return revset.baseset(), None, None
2060 return revset.baseset(), None, None
2061 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2061 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2062 if opts.get('rev'):
2062 if opts.get('rev'):
2063 # User-specified revs might be unsorted, but don't sort before
2063 # User-specified revs might be unsorted, but don't sort before
2064 # _makelogrevset because it might depend on the order of revs
2064 # _makelogrevset because it might depend on the order of revs
2065 revs.sort(reverse=True)
2065 revs.sort(reverse=True)
2066 if expr:
2066 if expr:
2067 # Revset matchers often operate faster on revisions in changelog
2067 # Revset matchers often operate faster on revisions in changelog
2068 # order, because most filters deal with the changelog.
2068 # order, because most filters deal with the changelog.
2069 revs.reverse()
2069 revs.reverse()
2070 matcher = revset.match(repo.ui, expr)
2070 matcher = revset.match(repo.ui, expr)
2071 # Revset matches can reorder revisions. "A or B" typically returns
2071 # Revset matches can reorder revisions. "A or B" typically returns
2072 # returns the revision matching A then the revision matching B. Sort
2072 # returns the revision matching A then the revision matching B. Sort
2073 # again to fix that.
2073 # again to fix that.
2074 revs = matcher(repo, revs)
2074 revs = matcher(repo, revs)
2075 revs.sort(reverse=True)
2075 revs.sort(reverse=True)
2076 if limit is not None:
2076 if limit is not None:
2077 limitedrevs = []
2077 limitedrevs = []
2078 for idx, rev in enumerate(revs):
2078 for idx, rev in enumerate(revs):
2079 if idx >= limit:
2079 if idx >= limit:
2080 break
2080 break
2081 limitedrevs.append(rev)
2081 limitedrevs.append(rev)
2082 revs = revset.baseset(limitedrevs)
2082 revs = revset.baseset(limitedrevs)
2083
2083
2084 return revs, expr, filematcher
2084 return revs, expr, filematcher
2085
2085
2086 def getlogrevs(repo, pats, opts):
2086 def getlogrevs(repo, pats, opts):
2087 """Return (revs, expr, filematcher) where revs is an iterable of
2087 """Return (revs, expr, filematcher) where revs is an iterable of
2088 revision numbers, expr is a revset string built from log options
2088 revision numbers, expr is a revset string built from log options
2089 and file patterns or None, and used to filter 'revs'. If --stat or
2089 and file patterns or None, and used to filter 'revs'. If --stat or
2090 --patch are not passed filematcher is None. Otherwise it is a
2090 --patch are not passed filematcher is None. Otherwise it is a
2091 callable taking a revision number and returning a match objects
2091 callable taking a revision number and returning a match objects
2092 filtering the files to be detailed when displaying the revision.
2092 filtering the files to be detailed when displaying the revision.
2093 """
2093 """
2094 limit = loglimit(opts)
2094 limit = loglimit(opts)
2095 revs = _logrevs(repo, opts)
2095 revs = _logrevs(repo, opts)
2096 if not revs:
2096 if not revs:
2097 return revset.baseset([]), None, None
2097 return revset.baseset([]), None, None
2098 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2098 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2099 if expr:
2099 if expr:
2100 # Revset matchers often operate faster on revisions in changelog
2100 # Revset matchers often operate faster on revisions in changelog
2101 # order, because most filters deal with the changelog.
2101 # order, because most filters deal with the changelog.
2102 if not opts.get('rev'):
2102 if not opts.get('rev'):
2103 revs.reverse()
2103 revs.reverse()
2104 matcher = revset.match(repo.ui, expr)
2104 matcher = revset.match(repo.ui, expr)
2105 # Revset matches can reorder revisions. "A or B" typically returns
2105 # Revset matches can reorder revisions. "A or B" typically returns
2106 # returns the revision matching A then the revision matching B. Sort
2106 # returns the revision matching A then the revision matching B. Sort
2107 # again to fix that.
2107 # again to fix that.
2108 revs = matcher(repo, revs)
2108 revs = matcher(repo, revs)
2109 if not opts.get('rev'):
2109 if not opts.get('rev'):
2110 revs.sort(reverse=True)
2110 revs.sort(reverse=True)
2111 if limit is not None:
2111 if limit is not None:
2112 count = 0
2112 count = 0
2113 limitedrevs = []
2113 limitedrevs = []
2114 it = iter(revs)
2114 it = iter(revs)
2115 while count < limit:
2115 while count < limit:
2116 try:
2116 try:
2117 limitedrevs.append(it.next())
2117 limitedrevs.append(it.next())
2118 except (StopIteration):
2118 except (StopIteration):
2119 break
2119 break
2120 count += 1
2120 count += 1
2121 revs = revset.baseset(limitedrevs)
2121 revs = revset.baseset(limitedrevs)
2122
2122
2123 return revs, expr, filematcher
2123 return revs, expr, filematcher
2124
2124
2125 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
2125 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
2126 filematcher=None):
2126 filematcher=None):
2127 seen, state = [], graphmod.asciistate()
2127 seen, state = [], graphmod.asciistate()
2128 for rev, type, ctx, parents in dag:
2128 for rev, type, ctx, parents in dag:
2129 char = 'o'
2129 char = 'o'
2130 if ctx.node() in showparents:
2130 if ctx.node() in showparents:
2131 char = '@'
2131 char = '@'
2132 elif ctx.obsolete():
2132 elif ctx.obsolete():
2133 char = 'x'
2133 char = 'x'
2134 elif ctx.closesbranch():
2134 elif ctx.closesbranch():
2135 char = '_'
2135 char = '_'
2136 copies = None
2136 copies = None
2137 if getrenamed and ctx.rev():
2137 if getrenamed and ctx.rev():
2138 copies = []
2138 copies = []
2139 for fn in ctx.files():
2139 for fn in ctx.files():
2140 rename = getrenamed(fn, ctx.rev())
2140 rename = getrenamed(fn, ctx.rev())
2141 if rename:
2141 if rename:
2142 copies.append((fn, rename[0]))
2142 copies.append((fn, rename[0]))
2143 revmatchfn = None
2143 revmatchfn = None
2144 if filematcher is not None:
2144 if filematcher is not None:
2145 revmatchfn = filematcher(ctx.rev())
2145 revmatchfn = filematcher(ctx.rev())
2146 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2146 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2147 lines = displayer.hunk.pop(rev).split('\n')
2147 lines = displayer.hunk.pop(rev).split('\n')
2148 if not lines[-1]:
2148 if not lines[-1]:
2149 del lines[-1]
2149 del lines[-1]
2150 displayer.flush(rev)
2150 displayer.flush(rev)
2151 edges = edgefn(type, char, lines, seen, rev, parents)
2151 edges = edgefn(type, char, lines, seen, rev, parents)
2152 for type, char, lines, coldata in edges:
2152 for type, char, lines, coldata in edges:
2153 graphmod.ascii(ui, state, type, char, lines, coldata)
2153 graphmod.ascii(ui, state, type, char, lines, coldata)
2154 displayer.close()
2154 displayer.close()
2155
2155
2156 def graphlog(ui, repo, *pats, **opts):
2156 def graphlog(ui, repo, *pats, **opts):
2157 # Parameters are identical to log command ones
2157 # Parameters are identical to log command ones
2158 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2158 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2159 revdag = graphmod.dagwalker(repo, revs)
2159 revdag = graphmod.dagwalker(repo, revs)
2160
2160
2161 getrenamed = None
2161 getrenamed = None
2162 if opts.get('copies'):
2162 if opts.get('copies'):
2163 endrev = None
2163 endrev = None
2164 if opts.get('rev'):
2164 if opts.get('rev'):
2165 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2165 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2166 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2166 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2167 displayer = show_changeset(ui, repo, opts, buffered=True)
2167 displayer = show_changeset(ui, repo, opts, buffered=True)
2168 showparents = [ctx.node() for ctx in repo[None].parents()]
2168 showparents = [ctx.node() for ctx in repo[None].parents()]
2169 displaygraph(ui, revdag, displayer, showparents,
2169 displaygraph(ui, revdag, displayer, showparents,
2170 graphmod.asciiedges, getrenamed, filematcher)
2170 graphmod.asciiedges, getrenamed, filematcher)
2171
2171
2172 def checkunsupportedgraphflags(pats, opts):
2172 def checkunsupportedgraphflags(pats, opts):
2173 for op in ["newest_first"]:
2173 for op in ["newest_first"]:
2174 if op in opts and opts[op]:
2174 if op in opts and opts[op]:
2175 raise util.Abort(_("-G/--graph option is incompatible with --%s")
2175 raise util.Abort(_("-G/--graph option is incompatible with --%s")
2176 % op.replace("_", "-"))
2176 % op.replace("_", "-"))
2177
2177
2178 def graphrevs(repo, nodes, opts):
2178 def graphrevs(repo, nodes, opts):
2179 limit = loglimit(opts)
2179 limit = loglimit(opts)
2180 nodes.reverse()
2180 nodes.reverse()
2181 if limit is not None:
2181 if limit is not None:
2182 nodes = nodes[:limit]
2182 nodes = nodes[:limit]
2183 return graphmod.nodes(repo, nodes)
2183 return graphmod.nodes(repo, nodes)
2184
2184
2185 def add(ui, repo, match, prefix, explicitonly, **opts):
2185 def add(ui, repo, match, prefix, explicitonly, **opts):
2186 join = lambda f: os.path.join(prefix, f)
2186 join = lambda f: os.path.join(prefix, f)
2187 bad = []
2187 bad = []
2188 oldbad = match.bad
2188 oldbad = match.bad
2189 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
2189 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
2190 names = []
2190 names = []
2191 wctx = repo[None]
2191 wctx = repo[None]
2192 cca = None
2192 cca = None
2193 abort, warn = scmutil.checkportabilityalert(ui)
2193 abort, warn = scmutil.checkportabilityalert(ui)
2194 if abort or warn:
2194 if abort or warn:
2195 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2195 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2196 for f in wctx.walk(match):
2196 for f in wctx.walk(match):
2197 exact = match.exact(f)
2197 exact = match.exact(f)
2198 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2198 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2199 if cca:
2199 if cca:
2200 cca(f)
2200 cca(f)
2201 names.append(f)
2201 names.append(f)
2202 if ui.verbose or not exact:
2202 if ui.verbose or not exact:
2203 ui.status(_('adding %s\n') % match.rel(f))
2203 ui.status(_('adding %s\n') % match.rel(f))
2204
2204
2205 for subpath in sorted(wctx.substate):
2205 for subpath in sorted(wctx.substate):
2206 sub = wctx.sub(subpath)
2206 sub = wctx.sub(subpath)
2207 try:
2207 try:
2208 submatch = matchmod.narrowmatcher(subpath, match)
2208 submatch = matchmod.narrowmatcher(subpath, match)
2209 if opts.get('subrepos'):
2209 if opts.get('subrepos'):
2210 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2210 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2211 else:
2211 else:
2212 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2212 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2213 except error.LookupError:
2213 except error.LookupError:
2214 ui.status(_("skipping missing subrepository: %s\n")
2214 ui.status(_("skipping missing subrepository: %s\n")
2215 % join(subpath))
2215 % join(subpath))
2216
2216
2217 if not opts.get('dry_run'):
2217 if not opts.get('dry_run'):
2218 rejected = wctx.add(names, prefix)
2218 rejected = wctx.add(names, prefix)
2219 bad.extend(f for f in rejected if f in match.files())
2219 bad.extend(f for f in rejected if f in match.files())
2220 return bad
2220 return bad
2221
2221
2222 def forget(ui, repo, match, prefix, explicitonly):
2222 def forget(ui, repo, match, prefix, explicitonly):
2223 join = lambda f: os.path.join(prefix, f)
2223 join = lambda f: os.path.join(prefix, f)
2224 bad = []
2224 bad = []
2225 oldbad = match.bad
2225 oldbad = match.bad
2226 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
2226 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
2227 wctx = repo[None]
2227 wctx = repo[None]
2228 forgot = []
2228 forgot = []
2229 s = repo.status(match=match, clean=True)
2229 s = repo.status(match=match, clean=True)
2230 forget = sorted(s[0] + s[1] + s[3] + s[6])
2230 forget = sorted(s[0] + s[1] + s[3] + s[6])
2231 if explicitonly:
2231 if explicitonly:
2232 forget = [f for f in forget if match.exact(f)]
2232 forget = [f for f in forget if match.exact(f)]
2233
2233
2234 for subpath in sorted(wctx.substate):
2234 for subpath in sorted(wctx.substate):
2235 sub = wctx.sub(subpath)
2235 sub = wctx.sub(subpath)
2236 try:
2236 try:
2237 submatch = matchmod.narrowmatcher(subpath, match)
2237 submatch = matchmod.narrowmatcher(subpath, match)
2238 subbad, subforgot = sub.forget(submatch, prefix)
2238 subbad, subforgot = sub.forget(submatch, prefix)
2239 bad.extend([subpath + '/' + f for f in subbad])
2239 bad.extend([subpath + '/' + f for f in subbad])
2240 forgot.extend([subpath + '/' + f for f in subforgot])
2240 forgot.extend([subpath + '/' + f for f in subforgot])
2241 except error.LookupError:
2241 except error.LookupError:
2242 ui.status(_("skipping missing subrepository: %s\n")
2242 ui.status(_("skipping missing subrepository: %s\n")
2243 % join(subpath))
2243 % join(subpath))
2244
2244
2245 if not explicitonly:
2245 if not explicitonly:
2246 for f in match.files():
2246 for f in match.files():
2247 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2247 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2248 if f not in forgot:
2248 if f not in forgot:
2249 if repo.wvfs.exists(f):
2249 if repo.wvfs.exists(f):
2250 ui.warn(_('not removing %s: '
2250 ui.warn(_('not removing %s: '
2251 'file is already untracked\n')
2251 'file is already untracked\n')
2252 % match.rel(f))
2252 % match.rel(f))
2253 bad.append(f)
2253 bad.append(f)
2254
2254
2255 for f in forget:
2255 for f in forget:
2256 if ui.verbose or not match.exact(f):
2256 if ui.verbose or not match.exact(f):
2257 ui.status(_('removing %s\n') % match.rel(f))
2257 ui.status(_('removing %s\n') % match.rel(f))
2258
2258
2259 rejected = wctx.forget(forget, prefix)
2259 rejected = wctx.forget(forget, prefix)
2260 bad.extend(f for f in rejected if f in match.files())
2260 bad.extend(f for f in rejected if f in match.files())
2261 forgot.extend(f for f in forget if f not in rejected)
2261 forgot.extend(f for f in forget if f not in rejected)
2262 return bad, forgot
2262 return bad, forgot
2263
2263
2264 def files(ui, ctx, m, fm, fmt):
2264 def files(ui, ctx, m, fm, fmt, subrepos):
2265 rev = ctx.rev()
2265 rev = ctx.rev()
2266 ret = 1
2266 ret = 1
2267 ds = ctx.repo().dirstate
2267 ds = ctx.repo().dirstate
2268
2268
2269 for f in ctx.matches(m):
2269 for f in ctx.matches(m):
2270 if rev is None and ds[f] == 'r':
2270 if rev is None and ds[f] == 'r':
2271 continue
2271 continue
2272 fm.startitem()
2272 fm.startitem()
2273 if ui.verbose:
2273 if ui.verbose:
2274 fc = ctx[f]
2274 fc = ctx[f]
2275 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2275 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2276 fm.data(abspath=f)
2276 fm.data(abspath=f)
2277 fm.write('path', fmt, m.rel(f))
2277 fm.write('path', fmt, m.rel(f))
2278 ret = 0
2278 ret = 0
2279
2279
2280 if subrepos:
2281 for subpath in sorted(ctx.substate):
2282 sub = ctx.sub(subpath)
2283 try:
2284 submatch = matchmod.narrowmatcher(subpath, m)
2285 if sub.printfiles(ui, submatch, fm, fmt) == 0:
2286 ret = 0
2287 except error.LookupError:
2288 ui.status(_("skipping missing subrepository: %s\n")
2289 % m.abs(subpath))
2290
2280 return ret
2291 return ret
2281
2292
2282 def remove(ui, repo, m, prefix, after, force, subrepos):
2293 def remove(ui, repo, m, prefix, after, force, subrepos):
2283 join = lambda f: os.path.join(prefix, f)
2294 join = lambda f: os.path.join(prefix, f)
2284 ret = 0
2295 ret = 0
2285 s = repo.status(match=m, clean=True)
2296 s = repo.status(match=m, clean=True)
2286 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2297 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2287
2298
2288 wctx = repo[None]
2299 wctx = repo[None]
2289
2300
2290 for subpath in sorted(wctx.substate):
2301 for subpath in sorted(wctx.substate):
2291 def matchessubrepo(matcher, subpath):
2302 def matchessubrepo(matcher, subpath):
2292 if matcher.exact(subpath):
2303 if matcher.exact(subpath):
2293 return True
2304 return True
2294 for f in matcher.files():
2305 for f in matcher.files():
2295 if f.startswith(subpath):
2306 if f.startswith(subpath):
2296 return True
2307 return True
2297 return False
2308 return False
2298
2309
2299 if subrepos or matchessubrepo(m, subpath):
2310 if subrepos or matchessubrepo(m, subpath):
2300 sub = wctx.sub(subpath)
2311 sub = wctx.sub(subpath)
2301 try:
2312 try:
2302 submatch = matchmod.narrowmatcher(subpath, m)
2313 submatch = matchmod.narrowmatcher(subpath, m)
2303 if sub.removefiles(submatch, prefix, after, force, subrepos):
2314 if sub.removefiles(submatch, prefix, after, force, subrepos):
2304 ret = 1
2315 ret = 1
2305 except error.LookupError:
2316 except error.LookupError:
2306 ui.status(_("skipping missing subrepository: %s\n")
2317 ui.status(_("skipping missing subrepository: %s\n")
2307 % join(subpath))
2318 % join(subpath))
2308
2319
2309 # warn about failure to delete explicit files/dirs
2320 # warn about failure to delete explicit files/dirs
2310 deleteddirs = scmutil.dirs(deleted)
2321 deleteddirs = scmutil.dirs(deleted)
2311 for f in m.files():
2322 for f in m.files():
2312 def insubrepo():
2323 def insubrepo():
2313 for subpath in wctx.substate:
2324 for subpath in wctx.substate:
2314 if f.startswith(subpath):
2325 if f.startswith(subpath):
2315 return True
2326 return True
2316 return False
2327 return False
2317
2328
2318 isdir = f in deleteddirs or f in wctx.dirs()
2329 isdir = f in deleteddirs or f in wctx.dirs()
2319 if f in repo.dirstate or isdir or f == '.' or insubrepo():
2330 if f in repo.dirstate or isdir or f == '.' or insubrepo():
2320 continue
2331 continue
2321
2332
2322 if repo.wvfs.exists(f):
2333 if repo.wvfs.exists(f):
2323 if repo.wvfs.isdir(f):
2334 if repo.wvfs.isdir(f):
2324 ui.warn(_('not removing %s: no tracked files\n')
2335 ui.warn(_('not removing %s: no tracked files\n')
2325 % m.rel(f))
2336 % m.rel(f))
2326 else:
2337 else:
2327 ui.warn(_('not removing %s: file is untracked\n')
2338 ui.warn(_('not removing %s: file is untracked\n')
2328 % m.rel(f))
2339 % m.rel(f))
2329 # missing files will generate a warning elsewhere
2340 # missing files will generate a warning elsewhere
2330 ret = 1
2341 ret = 1
2331
2342
2332 if force:
2343 if force:
2333 list = modified + deleted + clean + added
2344 list = modified + deleted + clean + added
2334 elif after:
2345 elif after:
2335 list = deleted
2346 list = deleted
2336 for f in modified + added + clean:
2347 for f in modified + added + clean:
2337 ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
2348 ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
2338 ret = 1
2349 ret = 1
2339 else:
2350 else:
2340 list = deleted + clean
2351 list = deleted + clean
2341 for f in modified:
2352 for f in modified:
2342 ui.warn(_('not removing %s: file is modified (use -f'
2353 ui.warn(_('not removing %s: file is modified (use -f'
2343 ' to force removal)\n') % m.rel(f))
2354 ' to force removal)\n') % m.rel(f))
2344 ret = 1
2355 ret = 1
2345 for f in added:
2356 for f in added:
2346 ui.warn(_('not removing %s: file has been marked for add'
2357 ui.warn(_('not removing %s: file has been marked for add'
2347 ' (use forget to undo)\n') % m.rel(f))
2358 ' (use forget to undo)\n') % m.rel(f))
2348 ret = 1
2359 ret = 1
2349
2360
2350 for f in sorted(list):
2361 for f in sorted(list):
2351 if ui.verbose or not m.exact(f):
2362 if ui.verbose or not m.exact(f):
2352 ui.status(_('removing %s\n') % m.rel(f))
2363 ui.status(_('removing %s\n') % m.rel(f))
2353
2364
2354 wlock = repo.wlock()
2365 wlock = repo.wlock()
2355 try:
2366 try:
2356 if not after:
2367 if not after:
2357 for f in list:
2368 for f in list:
2358 if f in added:
2369 if f in added:
2359 continue # we never unlink added files on remove
2370 continue # we never unlink added files on remove
2360 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
2371 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
2361 repo[None].forget(list)
2372 repo[None].forget(list)
2362 finally:
2373 finally:
2363 wlock.release()
2374 wlock.release()
2364
2375
2365 return ret
2376 return ret
2366
2377
2367 def cat(ui, repo, ctx, matcher, prefix, **opts):
2378 def cat(ui, repo, ctx, matcher, prefix, **opts):
2368 err = 1
2379 err = 1
2369
2380
2370 def write(path):
2381 def write(path):
2371 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2382 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2372 pathname=os.path.join(prefix, path))
2383 pathname=os.path.join(prefix, path))
2373 data = ctx[path].data()
2384 data = ctx[path].data()
2374 if opts.get('decode'):
2385 if opts.get('decode'):
2375 data = repo.wwritedata(path, data)
2386 data = repo.wwritedata(path, data)
2376 fp.write(data)
2387 fp.write(data)
2377 fp.close()
2388 fp.close()
2378
2389
2379 # Automation often uses hg cat on single files, so special case it
2390 # Automation often uses hg cat on single files, so special case it
2380 # for performance to avoid the cost of parsing the manifest.
2391 # for performance to avoid the cost of parsing the manifest.
2381 if len(matcher.files()) == 1 and not matcher.anypats():
2392 if len(matcher.files()) == 1 and not matcher.anypats():
2382 file = matcher.files()[0]
2393 file = matcher.files()[0]
2383 mf = repo.manifest
2394 mf = repo.manifest
2384 mfnode = ctx._changeset[0]
2395 mfnode = ctx._changeset[0]
2385 if mf.find(mfnode, file)[0]:
2396 if mf.find(mfnode, file)[0]:
2386 write(file)
2397 write(file)
2387 return 0
2398 return 0
2388
2399
2389 # Don't warn about "missing" files that are really in subrepos
2400 # Don't warn about "missing" files that are really in subrepos
2390 bad = matcher.bad
2401 bad = matcher.bad
2391
2402
2392 def badfn(path, msg):
2403 def badfn(path, msg):
2393 for subpath in ctx.substate:
2404 for subpath in ctx.substate:
2394 if path.startswith(subpath):
2405 if path.startswith(subpath):
2395 return
2406 return
2396 bad(path, msg)
2407 bad(path, msg)
2397
2408
2398 matcher.bad = badfn
2409 matcher.bad = badfn
2399
2410
2400 for abs in ctx.walk(matcher):
2411 for abs in ctx.walk(matcher):
2401 write(abs)
2412 write(abs)
2402 err = 0
2413 err = 0
2403
2414
2404 matcher.bad = bad
2415 matcher.bad = bad
2405
2416
2406 for subpath in sorted(ctx.substate):
2417 for subpath in sorted(ctx.substate):
2407 sub = ctx.sub(subpath)
2418 sub = ctx.sub(subpath)
2408 try:
2419 try:
2409 submatch = matchmod.narrowmatcher(subpath, matcher)
2420 submatch = matchmod.narrowmatcher(subpath, matcher)
2410
2421
2411 if not sub.cat(submatch, os.path.join(prefix, sub._path),
2422 if not sub.cat(submatch, os.path.join(prefix, sub._path),
2412 **opts):
2423 **opts):
2413 err = 0
2424 err = 0
2414 except error.RepoLookupError:
2425 except error.RepoLookupError:
2415 ui.status(_("skipping missing subrepository: %s\n")
2426 ui.status(_("skipping missing subrepository: %s\n")
2416 % os.path.join(prefix, subpath))
2427 % os.path.join(prefix, subpath))
2417
2428
2418 return err
2429 return err
2419
2430
2420 def commit(ui, repo, commitfunc, pats, opts):
2431 def commit(ui, repo, commitfunc, pats, opts):
2421 '''commit the specified files or all outstanding changes'''
2432 '''commit the specified files or all outstanding changes'''
2422 date = opts.get('date')
2433 date = opts.get('date')
2423 if date:
2434 if date:
2424 opts['date'] = util.parsedate(date)
2435 opts['date'] = util.parsedate(date)
2425 message = logmessage(ui, opts)
2436 message = logmessage(ui, opts)
2426 matcher = scmutil.match(repo[None], pats, opts)
2437 matcher = scmutil.match(repo[None], pats, opts)
2427
2438
2428 # extract addremove carefully -- this function can be called from a command
2439 # extract addremove carefully -- this function can be called from a command
2429 # that doesn't support addremove
2440 # that doesn't support addremove
2430 if opts.get('addremove'):
2441 if opts.get('addremove'):
2431 if scmutil.addremove(repo, matcher, "", opts) != 0:
2442 if scmutil.addremove(repo, matcher, "", opts) != 0:
2432 raise util.Abort(
2443 raise util.Abort(
2433 _("failed to mark all new/missing files as added/removed"))
2444 _("failed to mark all new/missing files as added/removed"))
2434
2445
2435 return commitfunc(ui, repo, message, matcher, opts)
2446 return commitfunc(ui, repo, message, matcher, opts)
2436
2447
2437 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2448 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2438 # amend will reuse the existing user if not specified, but the obsolete
2449 # amend will reuse the existing user if not specified, but the obsolete
2439 # marker creation requires that the current user's name is specified.
2450 # marker creation requires that the current user's name is specified.
2440 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2451 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2441 ui.username() # raise exception if username not set
2452 ui.username() # raise exception if username not set
2442
2453
2443 ui.note(_('amending changeset %s\n') % old)
2454 ui.note(_('amending changeset %s\n') % old)
2444 base = old.p1()
2455 base = old.p1()
2445
2456
2446 wlock = lock = newid = None
2457 wlock = lock = newid = None
2447 try:
2458 try:
2448 wlock = repo.wlock()
2459 wlock = repo.wlock()
2449 lock = repo.lock()
2460 lock = repo.lock()
2450 tr = repo.transaction('amend')
2461 tr = repo.transaction('amend')
2451 try:
2462 try:
2452 # See if we got a message from -m or -l, if not, open the editor
2463 # See if we got a message from -m or -l, if not, open the editor
2453 # with the message of the changeset to amend
2464 # with the message of the changeset to amend
2454 message = logmessage(ui, opts)
2465 message = logmessage(ui, opts)
2455 # ensure logfile does not conflict with later enforcement of the
2466 # ensure logfile does not conflict with later enforcement of the
2456 # message. potential logfile content has been processed by
2467 # message. potential logfile content has been processed by
2457 # `logmessage` anyway.
2468 # `logmessage` anyway.
2458 opts.pop('logfile')
2469 opts.pop('logfile')
2459 # First, do a regular commit to record all changes in the working
2470 # First, do a regular commit to record all changes in the working
2460 # directory (if there are any)
2471 # directory (if there are any)
2461 ui.callhooks = False
2472 ui.callhooks = False
2462 currentbookmark = repo._bookmarkcurrent
2473 currentbookmark = repo._bookmarkcurrent
2463 try:
2474 try:
2464 repo._bookmarkcurrent = None
2475 repo._bookmarkcurrent = None
2465 opts['message'] = 'temporary amend commit for %s' % old
2476 opts['message'] = 'temporary amend commit for %s' % old
2466 node = commit(ui, repo, commitfunc, pats, opts)
2477 node = commit(ui, repo, commitfunc, pats, opts)
2467 finally:
2478 finally:
2468 repo._bookmarkcurrent = currentbookmark
2479 repo._bookmarkcurrent = currentbookmark
2469 ui.callhooks = True
2480 ui.callhooks = True
2470 ctx = repo[node]
2481 ctx = repo[node]
2471
2482
2472 # Participating changesets:
2483 # Participating changesets:
2473 #
2484 #
2474 # node/ctx o - new (intermediate) commit that contains changes
2485 # node/ctx o - new (intermediate) commit that contains changes
2475 # | from working dir to go into amending commit
2486 # | from working dir to go into amending commit
2476 # | (or a workingctx if there were no changes)
2487 # | (or a workingctx if there were no changes)
2477 # |
2488 # |
2478 # old o - changeset to amend
2489 # old o - changeset to amend
2479 # |
2490 # |
2480 # base o - parent of amending changeset
2491 # base o - parent of amending changeset
2481
2492
2482 # Update extra dict from amended commit (e.g. to preserve graft
2493 # Update extra dict from amended commit (e.g. to preserve graft
2483 # source)
2494 # source)
2484 extra.update(old.extra())
2495 extra.update(old.extra())
2485
2496
2486 # Also update it from the intermediate commit or from the wctx
2497 # Also update it from the intermediate commit or from the wctx
2487 extra.update(ctx.extra())
2498 extra.update(ctx.extra())
2488
2499
2489 if len(old.parents()) > 1:
2500 if len(old.parents()) > 1:
2490 # ctx.files() isn't reliable for merges, so fall back to the
2501 # ctx.files() isn't reliable for merges, so fall back to the
2491 # slower repo.status() method
2502 # slower repo.status() method
2492 files = set([fn for st in repo.status(base, old)[:3]
2503 files = set([fn for st in repo.status(base, old)[:3]
2493 for fn in st])
2504 for fn in st])
2494 else:
2505 else:
2495 files = set(old.files())
2506 files = set(old.files())
2496
2507
2497 # Second, we use either the commit we just did, or if there were no
2508 # Second, we use either the commit we just did, or if there were no
2498 # changes the parent of the working directory as the version of the
2509 # changes the parent of the working directory as the version of the
2499 # files in the final amend commit
2510 # files in the final amend commit
2500 if node:
2511 if node:
2501 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2512 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2502
2513
2503 user = ctx.user()
2514 user = ctx.user()
2504 date = ctx.date()
2515 date = ctx.date()
2505 # Recompute copies (avoid recording a -> b -> a)
2516 # Recompute copies (avoid recording a -> b -> a)
2506 copied = copies.pathcopies(base, ctx)
2517 copied = copies.pathcopies(base, ctx)
2507 if old.p2:
2518 if old.p2:
2508 copied.update(copies.pathcopies(old.p2(), ctx))
2519 copied.update(copies.pathcopies(old.p2(), ctx))
2509
2520
2510 # Prune files which were reverted by the updates: if old
2521 # Prune files which were reverted by the updates: if old
2511 # introduced file X and our intermediate commit, node,
2522 # introduced file X and our intermediate commit, node,
2512 # renamed that file, then those two files are the same and
2523 # renamed that file, then those two files are the same and
2513 # we can discard X from our list of files. Likewise if X
2524 # we can discard X from our list of files. Likewise if X
2514 # was deleted, it's no longer relevant
2525 # was deleted, it's no longer relevant
2515 files.update(ctx.files())
2526 files.update(ctx.files())
2516
2527
2517 def samefile(f):
2528 def samefile(f):
2518 if f in ctx.manifest():
2529 if f in ctx.manifest():
2519 a = ctx.filectx(f)
2530 a = ctx.filectx(f)
2520 if f in base.manifest():
2531 if f in base.manifest():
2521 b = base.filectx(f)
2532 b = base.filectx(f)
2522 return (not a.cmp(b)
2533 return (not a.cmp(b)
2523 and a.flags() == b.flags())
2534 and a.flags() == b.flags())
2524 else:
2535 else:
2525 return False
2536 return False
2526 else:
2537 else:
2527 return f not in base.manifest()
2538 return f not in base.manifest()
2528 files = [f for f in files if not samefile(f)]
2539 files = [f for f in files if not samefile(f)]
2529
2540
2530 def filectxfn(repo, ctx_, path):
2541 def filectxfn(repo, ctx_, path):
2531 try:
2542 try:
2532 fctx = ctx[path]
2543 fctx = ctx[path]
2533 flags = fctx.flags()
2544 flags = fctx.flags()
2534 mctx = context.memfilectx(repo,
2545 mctx = context.memfilectx(repo,
2535 fctx.path(), fctx.data(),
2546 fctx.path(), fctx.data(),
2536 islink='l' in flags,
2547 islink='l' in flags,
2537 isexec='x' in flags,
2548 isexec='x' in flags,
2538 copied=copied.get(path))
2549 copied=copied.get(path))
2539 return mctx
2550 return mctx
2540 except KeyError:
2551 except KeyError:
2541 return None
2552 return None
2542 else:
2553 else:
2543 ui.note(_('copying changeset %s to %s\n') % (old, base))
2554 ui.note(_('copying changeset %s to %s\n') % (old, base))
2544
2555
2545 # Use version of files as in the old cset
2556 # Use version of files as in the old cset
2546 def filectxfn(repo, ctx_, path):
2557 def filectxfn(repo, ctx_, path):
2547 try:
2558 try:
2548 return old.filectx(path)
2559 return old.filectx(path)
2549 except KeyError:
2560 except KeyError:
2550 return None
2561 return None
2551
2562
2552 user = opts.get('user') or old.user()
2563 user = opts.get('user') or old.user()
2553 date = opts.get('date') or old.date()
2564 date = opts.get('date') or old.date()
2554 editform = mergeeditform(old, 'commit.amend')
2565 editform = mergeeditform(old, 'commit.amend')
2555 editor = getcommiteditor(editform=editform, **opts)
2566 editor = getcommiteditor(editform=editform, **opts)
2556 if not message:
2567 if not message:
2557 editor = getcommiteditor(edit=True, editform=editform)
2568 editor = getcommiteditor(edit=True, editform=editform)
2558 message = old.description()
2569 message = old.description()
2559
2570
2560 pureextra = extra.copy()
2571 pureextra = extra.copy()
2561 extra['amend_source'] = old.hex()
2572 extra['amend_source'] = old.hex()
2562
2573
2563 new = context.memctx(repo,
2574 new = context.memctx(repo,
2564 parents=[base.node(), old.p2().node()],
2575 parents=[base.node(), old.p2().node()],
2565 text=message,
2576 text=message,
2566 files=files,
2577 files=files,
2567 filectxfn=filectxfn,
2578 filectxfn=filectxfn,
2568 user=user,
2579 user=user,
2569 date=date,
2580 date=date,
2570 extra=extra,
2581 extra=extra,
2571 editor=editor)
2582 editor=editor)
2572
2583
2573 newdesc = changelog.stripdesc(new.description())
2584 newdesc = changelog.stripdesc(new.description())
2574 if ((not node)
2585 if ((not node)
2575 and newdesc == old.description()
2586 and newdesc == old.description()
2576 and user == old.user()
2587 and user == old.user()
2577 and date == old.date()
2588 and date == old.date()
2578 and pureextra == old.extra()):
2589 and pureextra == old.extra()):
2579 # nothing changed. continuing here would create a new node
2590 # nothing changed. continuing here would create a new node
2580 # anyway because of the amend_source noise.
2591 # anyway because of the amend_source noise.
2581 #
2592 #
2582 # This not what we expect from amend.
2593 # This not what we expect from amend.
2583 return old.node()
2594 return old.node()
2584
2595
2585 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2596 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2586 try:
2597 try:
2587 if opts.get('secret'):
2598 if opts.get('secret'):
2588 commitphase = 'secret'
2599 commitphase = 'secret'
2589 else:
2600 else:
2590 commitphase = old.phase()
2601 commitphase = old.phase()
2591 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2602 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2592 newid = repo.commitctx(new)
2603 newid = repo.commitctx(new)
2593 finally:
2604 finally:
2594 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2605 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2595 if newid != old.node():
2606 if newid != old.node():
2596 # Reroute the working copy parent to the new changeset
2607 # Reroute the working copy parent to the new changeset
2597 repo.setparents(newid, nullid)
2608 repo.setparents(newid, nullid)
2598
2609
2599 # Move bookmarks from old parent to amend commit
2610 # Move bookmarks from old parent to amend commit
2600 bms = repo.nodebookmarks(old.node())
2611 bms = repo.nodebookmarks(old.node())
2601 if bms:
2612 if bms:
2602 marks = repo._bookmarks
2613 marks = repo._bookmarks
2603 for bm in bms:
2614 for bm in bms:
2604 marks[bm] = newid
2615 marks[bm] = newid
2605 marks.write()
2616 marks.write()
2606 #commit the whole amend process
2617 #commit the whole amend process
2607 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2618 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2608 if createmarkers and newid != old.node():
2619 if createmarkers and newid != old.node():
2609 # mark the new changeset as successor of the rewritten one
2620 # mark the new changeset as successor of the rewritten one
2610 new = repo[newid]
2621 new = repo[newid]
2611 obs = [(old, (new,))]
2622 obs = [(old, (new,))]
2612 if node:
2623 if node:
2613 obs.append((ctx, ()))
2624 obs.append((ctx, ()))
2614
2625
2615 obsolete.createmarkers(repo, obs)
2626 obsolete.createmarkers(repo, obs)
2616 tr.close()
2627 tr.close()
2617 finally:
2628 finally:
2618 tr.release()
2629 tr.release()
2619 if not createmarkers and newid != old.node():
2630 if not createmarkers and newid != old.node():
2620 # Strip the intermediate commit (if there was one) and the amended
2631 # Strip the intermediate commit (if there was one) and the amended
2621 # commit
2632 # commit
2622 if node:
2633 if node:
2623 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2634 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2624 ui.note(_('stripping amended changeset %s\n') % old)
2635 ui.note(_('stripping amended changeset %s\n') % old)
2625 repair.strip(ui, repo, old.node(), topic='amend-backup')
2636 repair.strip(ui, repo, old.node(), topic='amend-backup')
2626 finally:
2637 finally:
2627 if newid is None:
2638 if newid is None:
2628 repo.dirstate.invalidate()
2639 repo.dirstate.invalidate()
2629 lockmod.release(lock, wlock)
2640 lockmod.release(lock, wlock)
2630 return newid
2641 return newid
2631
2642
2632 def commiteditor(repo, ctx, subs, editform=''):
2643 def commiteditor(repo, ctx, subs, editform=''):
2633 if ctx.description():
2644 if ctx.description():
2634 return ctx.description()
2645 return ctx.description()
2635 return commitforceeditor(repo, ctx, subs, editform=editform)
2646 return commitforceeditor(repo, ctx, subs, editform=editform)
2636
2647
2637 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2648 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2638 editform=''):
2649 editform=''):
2639 if not extramsg:
2650 if not extramsg:
2640 extramsg = _("Leave message empty to abort commit.")
2651 extramsg = _("Leave message empty to abort commit.")
2641
2652
2642 forms = [e for e in editform.split('.') if e]
2653 forms = [e for e in editform.split('.') if e]
2643 forms.insert(0, 'changeset')
2654 forms.insert(0, 'changeset')
2644 while forms:
2655 while forms:
2645 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2656 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2646 if tmpl:
2657 if tmpl:
2647 committext = buildcommittemplate(repo, ctx, subs, extramsg, tmpl)
2658 committext = buildcommittemplate(repo, ctx, subs, extramsg, tmpl)
2648 break
2659 break
2649 forms.pop()
2660 forms.pop()
2650 else:
2661 else:
2651 committext = buildcommittext(repo, ctx, subs, extramsg)
2662 committext = buildcommittext(repo, ctx, subs, extramsg)
2652
2663
2653 # run editor in the repository root
2664 # run editor in the repository root
2654 olddir = os.getcwd()
2665 olddir = os.getcwd()
2655 os.chdir(repo.root)
2666 os.chdir(repo.root)
2656 text = repo.ui.edit(committext, ctx.user(), ctx.extra(), editform=editform)
2667 text = repo.ui.edit(committext, ctx.user(), ctx.extra(), editform=editform)
2657 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2668 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2658 os.chdir(olddir)
2669 os.chdir(olddir)
2659
2670
2660 if finishdesc:
2671 if finishdesc:
2661 text = finishdesc(text)
2672 text = finishdesc(text)
2662 if not text.strip():
2673 if not text.strip():
2663 raise util.Abort(_("empty commit message"))
2674 raise util.Abort(_("empty commit message"))
2664
2675
2665 return text
2676 return text
2666
2677
2667 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2678 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2668 ui = repo.ui
2679 ui = repo.ui
2669 tmpl, mapfile = gettemplate(ui, tmpl, None)
2680 tmpl, mapfile = gettemplate(ui, tmpl, None)
2670
2681
2671 try:
2682 try:
2672 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2683 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2673 except SyntaxError, inst:
2684 except SyntaxError, inst:
2674 raise util.Abort(inst.args[0])
2685 raise util.Abort(inst.args[0])
2675
2686
2676 for k, v in repo.ui.configitems('committemplate'):
2687 for k, v in repo.ui.configitems('committemplate'):
2677 if k != 'changeset':
2688 if k != 'changeset':
2678 t.t.cache[k] = v
2689 t.t.cache[k] = v
2679
2690
2680 if not extramsg:
2691 if not extramsg:
2681 extramsg = '' # ensure that extramsg is string
2692 extramsg = '' # ensure that extramsg is string
2682
2693
2683 ui.pushbuffer()
2694 ui.pushbuffer()
2684 t.show(ctx, extramsg=extramsg)
2695 t.show(ctx, extramsg=extramsg)
2685 return ui.popbuffer()
2696 return ui.popbuffer()
2686
2697
2687 def buildcommittext(repo, ctx, subs, extramsg):
2698 def buildcommittext(repo, ctx, subs, extramsg):
2688 edittext = []
2699 edittext = []
2689 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2700 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2690 if ctx.description():
2701 if ctx.description():
2691 edittext.append(ctx.description())
2702 edittext.append(ctx.description())
2692 edittext.append("")
2703 edittext.append("")
2693 edittext.append("") # Empty line between message and comments.
2704 edittext.append("") # Empty line between message and comments.
2694 edittext.append(_("HG: Enter commit message."
2705 edittext.append(_("HG: Enter commit message."
2695 " Lines beginning with 'HG:' are removed."))
2706 " Lines beginning with 'HG:' are removed."))
2696 edittext.append("HG: %s" % extramsg)
2707 edittext.append("HG: %s" % extramsg)
2697 edittext.append("HG: --")
2708 edittext.append("HG: --")
2698 edittext.append(_("HG: user: %s") % ctx.user())
2709 edittext.append(_("HG: user: %s") % ctx.user())
2699 if ctx.p2():
2710 if ctx.p2():
2700 edittext.append(_("HG: branch merge"))
2711 edittext.append(_("HG: branch merge"))
2701 if ctx.branch():
2712 if ctx.branch():
2702 edittext.append(_("HG: branch '%s'") % ctx.branch())
2713 edittext.append(_("HG: branch '%s'") % ctx.branch())
2703 if bookmarks.iscurrent(repo):
2714 if bookmarks.iscurrent(repo):
2704 edittext.append(_("HG: bookmark '%s'") % repo._bookmarkcurrent)
2715 edittext.append(_("HG: bookmark '%s'") % repo._bookmarkcurrent)
2705 edittext.extend([_("HG: subrepo %s") % s for s in subs])
2716 edittext.extend([_("HG: subrepo %s") % s for s in subs])
2706 edittext.extend([_("HG: added %s") % f for f in added])
2717 edittext.extend([_("HG: added %s") % f for f in added])
2707 edittext.extend([_("HG: changed %s") % f for f in modified])
2718 edittext.extend([_("HG: changed %s") % f for f in modified])
2708 edittext.extend([_("HG: removed %s") % f for f in removed])
2719 edittext.extend([_("HG: removed %s") % f for f in removed])
2709 if not added and not modified and not removed:
2720 if not added and not modified and not removed:
2710 edittext.append(_("HG: no files changed"))
2721 edittext.append(_("HG: no files changed"))
2711 edittext.append("")
2722 edittext.append("")
2712
2723
2713 return "\n".join(edittext)
2724 return "\n".join(edittext)
2714
2725
2715 def commitstatus(repo, node, branch, bheads=None, opts={}):
2726 def commitstatus(repo, node, branch, bheads=None, opts={}):
2716 ctx = repo[node]
2727 ctx = repo[node]
2717 parents = ctx.parents()
2728 parents = ctx.parents()
2718
2729
2719 if (not opts.get('amend') and bheads and node not in bheads and not
2730 if (not opts.get('amend') and bheads and node not in bheads and not
2720 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2731 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2721 repo.ui.status(_('created new head\n'))
2732 repo.ui.status(_('created new head\n'))
2722 # The message is not printed for initial roots. For the other
2733 # The message is not printed for initial roots. For the other
2723 # changesets, it is printed in the following situations:
2734 # changesets, it is printed in the following situations:
2724 #
2735 #
2725 # Par column: for the 2 parents with ...
2736 # Par column: for the 2 parents with ...
2726 # N: null or no parent
2737 # N: null or no parent
2727 # B: parent is on another named branch
2738 # B: parent is on another named branch
2728 # C: parent is a regular non head changeset
2739 # C: parent is a regular non head changeset
2729 # H: parent was a branch head of the current branch
2740 # H: parent was a branch head of the current branch
2730 # Msg column: whether we print "created new head" message
2741 # Msg column: whether we print "created new head" message
2731 # In the following, it is assumed that there already exists some
2742 # In the following, it is assumed that there already exists some
2732 # initial branch heads of the current branch, otherwise nothing is
2743 # initial branch heads of the current branch, otherwise nothing is
2733 # printed anyway.
2744 # printed anyway.
2734 #
2745 #
2735 # Par Msg Comment
2746 # Par Msg Comment
2736 # N N y additional topo root
2747 # N N y additional topo root
2737 #
2748 #
2738 # B N y additional branch root
2749 # B N y additional branch root
2739 # C N y additional topo head
2750 # C N y additional topo head
2740 # H N n usual case
2751 # H N n usual case
2741 #
2752 #
2742 # B B y weird additional branch root
2753 # B B y weird additional branch root
2743 # C B y branch merge
2754 # C B y branch merge
2744 # H B n merge with named branch
2755 # H B n merge with named branch
2745 #
2756 #
2746 # C C y additional head from merge
2757 # C C y additional head from merge
2747 # C H n merge with a head
2758 # C H n merge with a head
2748 #
2759 #
2749 # H H n head merge: head count decreases
2760 # H H n head merge: head count decreases
2750
2761
2751 if not opts.get('close_branch'):
2762 if not opts.get('close_branch'):
2752 for r in parents:
2763 for r in parents:
2753 if r.closesbranch() and r.branch() == branch:
2764 if r.closesbranch() and r.branch() == branch:
2754 repo.ui.status(_('reopening closed branch head %d\n') % r)
2765 repo.ui.status(_('reopening closed branch head %d\n') % r)
2755
2766
2756 if repo.ui.debugflag:
2767 if repo.ui.debugflag:
2757 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2768 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2758 elif repo.ui.verbose:
2769 elif repo.ui.verbose:
2759 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2770 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2760
2771
2761 def revert(ui, repo, ctx, parents, *pats, **opts):
2772 def revert(ui, repo, ctx, parents, *pats, **opts):
2762 parent, p2 = parents
2773 parent, p2 = parents
2763 node = ctx.node()
2774 node = ctx.node()
2764
2775
2765 mf = ctx.manifest()
2776 mf = ctx.manifest()
2766 if node == p2:
2777 if node == p2:
2767 parent = p2
2778 parent = p2
2768 if node == parent:
2779 if node == parent:
2769 pmf = mf
2780 pmf = mf
2770 else:
2781 else:
2771 pmf = None
2782 pmf = None
2772
2783
2773 # need all matching names in dirstate and manifest of target rev,
2784 # need all matching names in dirstate and manifest of target rev,
2774 # so have to walk both. do not print errors if files exist in one
2785 # so have to walk both. do not print errors if files exist in one
2775 # but not other.
2786 # but not other.
2776
2787
2777 # `names` is a mapping for all elements in working copy and target revision
2788 # `names` is a mapping for all elements in working copy and target revision
2778 # The mapping is in the form:
2789 # The mapping is in the form:
2779 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2790 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2780 names = {}
2791 names = {}
2781
2792
2782 wlock = repo.wlock()
2793 wlock = repo.wlock()
2783 try:
2794 try:
2784 ## filling of the `names` mapping
2795 ## filling of the `names` mapping
2785 # walk dirstate to fill `names`
2796 # walk dirstate to fill `names`
2786
2797
2787 m = scmutil.match(repo[None], pats, opts)
2798 m = scmutil.match(repo[None], pats, opts)
2788 if not m.always() or node != parent:
2799 if not m.always() or node != parent:
2789 m.bad = lambda x, y: False
2800 m.bad = lambda x, y: False
2790 for abs in repo.walk(m):
2801 for abs in repo.walk(m):
2791 names[abs] = m.rel(abs), m.exact(abs)
2802 names[abs] = m.rel(abs), m.exact(abs)
2792
2803
2793 # walk target manifest to fill `names`
2804 # walk target manifest to fill `names`
2794
2805
2795 def badfn(path, msg):
2806 def badfn(path, msg):
2796 if path in names:
2807 if path in names:
2797 return
2808 return
2798 if path in ctx.substate:
2809 if path in ctx.substate:
2799 return
2810 return
2800 path_ = path + '/'
2811 path_ = path + '/'
2801 for f in names:
2812 for f in names:
2802 if f.startswith(path_):
2813 if f.startswith(path_):
2803 return
2814 return
2804 ui.warn("%s: %s\n" % (m.rel(path), msg))
2815 ui.warn("%s: %s\n" % (m.rel(path), msg))
2805
2816
2806 m = scmutil.match(ctx, pats, opts)
2817 m = scmutil.match(ctx, pats, opts)
2807 m.bad = badfn
2818 m.bad = badfn
2808 for abs in ctx.walk(m):
2819 for abs in ctx.walk(m):
2809 if abs not in names:
2820 if abs not in names:
2810 names[abs] = m.rel(abs), m.exact(abs)
2821 names[abs] = m.rel(abs), m.exact(abs)
2811
2822
2812 # Find status of all file in `names`.
2823 # Find status of all file in `names`.
2813 m = scmutil.matchfiles(repo, names)
2824 m = scmutil.matchfiles(repo, names)
2814
2825
2815 changes = repo.status(node1=node, match=m,
2826 changes = repo.status(node1=node, match=m,
2816 unknown=True, ignored=True, clean=True)
2827 unknown=True, ignored=True, clean=True)
2817 else:
2828 else:
2818 changes = repo.status(match=m)
2829 changes = repo.status(match=m)
2819 for kind in changes:
2830 for kind in changes:
2820 for abs in kind:
2831 for abs in kind:
2821 names[abs] = m.rel(abs), m.exact(abs)
2832 names[abs] = m.rel(abs), m.exact(abs)
2822
2833
2823 m = scmutil.matchfiles(repo, names)
2834 m = scmutil.matchfiles(repo, names)
2824
2835
2825 modified = set(changes.modified)
2836 modified = set(changes.modified)
2826 added = set(changes.added)
2837 added = set(changes.added)
2827 removed = set(changes.removed)
2838 removed = set(changes.removed)
2828 _deleted = set(changes.deleted)
2839 _deleted = set(changes.deleted)
2829 unknown = set(changes.unknown)
2840 unknown = set(changes.unknown)
2830 unknown.update(changes.ignored)
2841 unknown.update(changes.ignored)
2831 clean = set(changes.clean)
2842 clean = set(changes.clean)
2832 modadded = set()
2843 modadded = set()
2833
2844
2834 # split between files known in target manifest and the others
2845 # split between files known in target manifest and the others
2835 smf = set(mf)
2846 smf = set(mf)
2836
2847
2837 # determine the exact nature of the deleted changesets
2848 # determine the exact nature of the deleted changesets
2838 deladded = _deleted - smf
2849 deladded = _deleted - smf
2839 deleted = _deleted - deladded
2850 deleted = _deleted - deladded
2840
2851
2841 # We need to account for the state of the file in the dirstate,
2852 # We need to account for the state of the file in the dirstate,
2842 # even when we revert against something else than parent. This will
2853 # even when we revert against something else than parent. This will
2843 # slightly alter the behavior of revert (doing back up or not, delete
2854 # slightly alter the behavior of revert (doing back up or not, delete
2844 # or just forget etc).
2855 # or just forget etc).
2845 if parent == node:
2856 if parent == node:
2846 dsmodified = modified
2857 dsmodified = modified
2847 dsadded = added
2858 dsadded = added
2848 dsremoved = removed
2859 dsremoved = removed
2849 # store all local modifications, useful later for rename detection
2860 # store all local modifications, useful later for rename detection
2850 localchanges = dsmodified | dsadded
2861 localchanges = dsmodified | dsadded
2851 modified, added, removed = set(), set(), set()
2862 modified, added, removed = set(), set(), set()
2852 else:
2863 else:
2853 changes = repo.status(node1=parent, match=m)
2864 changes = repo.status(node1=parent, match=m)
2854 dsmodified = set(changes.modified)
2865 dsmodified = set(changes.modified)
2855 dsadded = set(changes.added)
2866 dsadded = set(changes.added)
2856 dsremoved = set(changes.removed)
2867 dsremoved = set(changes.removed)
2857 # store all local modifications, useful later for rename detection
2868 # store all local modifications, useful later for rename detection
2858 localchanges = dsmodified | dsadded
2869 localchanges = dsmodified | dsadded
2859
2870
2860 # only take into account for removes between wc and target
2871 # only take into account for removes between wc and target
2861 clean |= dsremoved - removed
2872 clean |= dsremoved - removed
2862 dsremoved &= removed
2873 dsremoved &= removed
2863 # distinct between dirstate remove and other
2874 # distinct between dirstate remove and other
2864 removed -= dsremoved
2875 removed -= dsremoved
2865
2876
2866 modadded = added & dsmodified
2877 modadded = added & dsmodified
2867 added -= modadded
2878 added -= modadded
2868
2879
2869 # tell newly modified apart.
2880 # tell newly modified apart.
2870 dsmodified &= modified
2881 dsmodified &= modified
2871 dsmodified |= modified & dsadded # dirstate added may needs backup
2882 dsmodified |= modified & dsadded # dirstate added may needs backup
2872 modified -= dsmodified
2883 modified -= dsmodified
2873
2884
2874 # We need to wait for some post-processing to update this set
2885 # We need to wait for some post-processing to update this set
2875 # before making the distinction. The dirstate will be used for
2886 # before making the distinction. The dirstate will be used for
2876 # that purpose.
2887 # that purpose.
2877 dsadded = added
2888 dsadded = added
2878
2889
2879 # in case of merge, files that are actually added can be reported as
2890 # in case of merge, files that are actually added can be reported as
2880 # modified, we need to post process the result
2891 # modified, we need to post process the result
2881 if p2 != nullid:
2892 if p2 != nullid:
2882 if pmf is None:
2893 if pmf is None:
2883 # only need parent manifest in the merge case,
2894 # only need parent manifest in the merge case,
2884 # so do not read by default
2895 # so do not read by default
2885 pmf = repo[parent].manifest()
2896 pmf = repo[parent].manifest()
2886 mergeadd = dsmodified - set(pmf)
2897 mergeadd = dsmodified - set(pmf)
2887 dsadded |= mergeadd
2898 dsadded |= mergeadd
2888 dsmodified -= mergeadd
2899 dsmodified -= mergeadd
2889
2900
2890 # if f is a rename, update `names` to also revert the source
2901 # if f is a rename, update `names` to also revert the source
2891 cwd = repo.getcwd()
2902 cwd = repo.getcwd()
2892 for f in localchanges:
2903 for f in localchanges:
2893 src = repo.dirstate.copied(f)
2904 src = repo.dirstate.copied(f)
2894 # XXX should we check for rename down to target node?
2905 # XXX should we check for rename down to target node?
2895 if src and src not in names and repo.dirstate[src] == 'r':
2906 if src and src not in names and repo.dirstate[src] == 'r':
2896 dsremoved.add(src)
2907 dsremoved.add(src)
2897 names[src] = (repo.pathto(src, cwd), True)
2908 names[src] = (repo.pathto(src, cwd), True)
2898
2909
2899 # distinguish between file to forget and the other
2910 # distinguish between file to forget and the other
2900 added = set()
2911 added = set()
2901 for abs in dsadded:
2912 for abs in dsadded:
2902 if repo.dirstate[abs] != 'a':
2913 if repo.dirstate[abs] != 'a':
2903 added.add(abs)
2914 added.add(abs)
2904 dsadded -= added
2915 dsadded -= added
2905
2916
2906 for abs in deladded:
2917 for abs in deladded:
2907 if repo.dirstate[abs] == 'a':
2918 if repo.dirstate[abs] == 'a':
2908 dsadded.add(abs)
2919 dsadded.add(abs)
2909 deladded -= dsadded
2920 deladded -= dsadded
2910
2921
2911 # For files marked as removed, we check if an unknown file is present at
2922 # For files marked as removed, we check if an unknown file is present at
2912 # the same path. If a such file exists it may need to be backed up.
2923 # the same path. If a such file exists it may need to be backed up.
2913 # Making the distinction at this stage helps have simpler backup
2924 # Making the distinction at this stage helps have simpler backup
2914 # logic.
2925 # logic.
2915 removunk = set()
2926 removunk = set()
2916 for abs in removed:
2927 for abs in removed:
2917 target = repo.wjoin(abs)
2928 target = repo.wjoin(abs)
2918 if os.path.lexists(target):
2929 if os.path.lexists(target):
2919 removunk.add(abs)
2930 removunk.add(abs)
2920 removed -= removunk
2931 removed -= removunk
2921
2932
2922 dsremovunk = set()
2933 dsremovunk = set()
2923 for abs in dsremoved:
2934 for abs in dsremoved:
2924 target = repo.wjoin(abs)
2935 target = repo.wjoin(abs)
2925 if os.path.lexists(target):
2936 if os.path.lexists(target):
2926 dsremovunk.add(abs)
2937 dsremovunk.add(abs)
2927 dsremoved -= dsremovunk
2938 dsremoved -= dsremovunk
2928
2939
2929 # action to be actually performed by revert
2940 # action to be actually performed by revert
2930 # (<list of file>, message>) tuple
2941 # (<list of file>, message>) tuple
2931 actions = {'revert': ([], _('reverting %s\n')),
2942 actions = {'revert': ([], _('reverting %s\n')),
2932 'add': ([], _('adding %s\n')),
2943 'add': ([], _('adding %s\n')),
2933 'remove': ([], _('removing %s\n')),
2944 'remove': ([], _('removing %s\n')),
2934 'drop': ([], _('removing %s\n')),
2945 'drop': ([], _('removing %s\n')),
2935 'forget': ([], _('forgetting %s\n')),
2946 'forget': ([], _('forgetting %s\n')),
2936 'undelete': ([], _('undeleting %s\n')),
2947 'undelete': ([], _('undeleting %s\n')),
2937 'noop': (None, _('no changes needed to %s\n')),
2948 'noop': (None, _('no changes needed to %s\n')),
2938 'unknown': (None, _('file not managed: %s\n')),
2949 'unknown': (None, _('file not managed: %s\n')),
2939 }
2950 }
2940
2951
2941 # "constant" that convey the backup strategy.
2952 # "constant" that convey the backup strategy.
2942 # All set to `discard` if `no-backup` is set do avoid checking
2953 # All set to `discard` if `no-backup` is set do avoid checking
2943 # no_backup lower in the code.
2954 # no_backup lower in the code.
2944 # These values are ordered for comparison purposes
2955 # These values are ordered for comparison purposes
2945 backup = 2 # unconditionally do backup
2956 backup = 2 # unconditionally do backup
2946 check = 1 # check if the existing file differs from target
2957 check = 1 # check if the existing file differs from target
2947 discard = 0 # never do backup
2958 discard = 0 # never do backup
2948 if opts.get('no_backup'):
2959 if opts.get('no_backup'):
2949 backup = check = discard
2960 backup = check = discard
2950
2961
2951 backupanddel = actions['remove']
2962 backupanddel = actions['remove']
2952 if not opts.get('no_backup'):
2963 if not opts.get('no_backup'):
2953 backupanddel = actions['drop']
2964 backupanddel = actions['drop']
2954
2965
2955 disptable = (
2966 disptable = (
2956 # dispatch table:
2967 # dispatch table:
2957 # file state
2968 # file state
2958 # action
2969 # action
2959 # make backup
2970 # make backup
2960
2971
2961 ## Sets that results that will change file on disk
2972 ## Sets that results that will change file on disk
2962 # Modified compared to target, no local change
2973 # Modified compared to target, no local change
2963 (modified, actions['revert'], discard),
2974 (modified, actions['revert'], discard),
2964 # Modified compared to target, but local file is deleted
2975 # Modified compared to target, but local file is deleted
2965 (deleted, actions['revert'], discard),
2976 (deleted, actions['revert'], discard),
2966 # Modified compared to target, local change
2977 # Modified compared to target, local change
2967 (dsmodified, actions['revert'], backup),
2978 (dsmodified, actions['revert'], backup),
2968 # Added since target
2979 # Added since target
2969 (added, actions['remove'], discard),
2980 (added, actions['remove'], discard),
2970 # Added in working directory
2981 # Added in working directory
2971 (dsadded, actions['forget'], discard),
2982 (dsadded, actions['forget'], discard),
2972 # Added since target, have local modification
2983 # Added since target, have local modification
2973 (modadded, backupanddel, backup),
2984 (modadded, backupanddel, backup),
2974 # Added since target but file is missing in working directory
2985 # Added since target but file is missing in working directory
2975 (deladded, actions['drop'], discard),
2986 (deladded, actions['drop'], discard),
2976 # Removed since target, before working copy parent
2987 # Removed since target, before working copy parent
2977 (removed, actions['add'], discard),
2988 (removed, actions['add'], discard),
2978 # Same as `removed` but an unknown file exists at the same path
2989 # Same as `removed` but an unknown file exists at the same path
2979 (removunk, actions['add'], check),
2990 (removunk, actions['add'], check),
2980 # Removed since targe, marked as such in working copy parent
2991 # Removed since targe, marked as such in working copy parent
2981 (dsremoved, actions['undelete'], discard),
2992 (dsremoved, actions['undelete'], discard),
2982 # Same as `dsremoved` but an unknown file exists at the same path
2993 # Same as `dsremoved` but an unknown file exists at the same path
2983 (dsremovunk, actions['undelete'], check),
2994 (dsremovunk, actions['undelete'], check),
2984 ## the following sets does not result in any file changes
2995 ## the following sets does not result in any file changes
2985 # File with no modification
2996 # File with no modification
2986 (clean, actions['noop'], discard),
2997 (clean, actions['noop'], discard),
2987 # Existing file, not tracked anywhere
2998 # Existing file, not tracked anywhere
2988 (unknown, actions['unknown'], discard),
2999 (unknown, actions['unknown'], discard),
2989 )
3000 )
2990
3001
2991 wctx = repo[None]
3002 wctx = repo[None]
2992 for abs, (rel, exact) in sorted(names.items()):
3003 for abs, (rel, exact) in sorted(names.items()):
2993 # target file to be touch on disk (relative to cwd)
3004 # target file to be touch on disk (relative to cwd)
2994 target = repo.wjoin(abs)
3005 target = repo.wjoin(abs)
2995 # search the entry in the dispatch table.
3006 # search the entry in the dispatch table.
2996 # if the file is in any of these sets, it was touched in the working
3007 # if the file is in any of these sets, it was touched in the working
2997 # directory parent and we are sure it needs to be reverted.
3008 # directory parent and we are sure it needs to be reverted.
2998 for table, (xlist, msg), dobackup in disptable:
3009 for table, (xlist, msg), dobackup in disptable:
2999 if abs not in table:
3010 if abs not in table:
3000 continue
3011 continue
3001 if xlist is not None:
3012 if xlist is not None:
3002 xlist.append(abs)
3013 xlist.append(abs)
3003 if dobackup and (backup <= dobackup
3014 if dobackup and (backup <= dobackup
3004 or wctx[abs].cmp(ctx[abs])):
3015 or wctx[abs].cmp(ctx[abs])):
3005 bakname = "%s.orig" % rel
3016 bakname = "%s.orig" % rel
3006 ui.note(_('saving current version of %s as %s\n') %
3017 ui.note(_('saving current version of %s as %s\n') %
3007 (rel, bakname))
3018 (rel, bakname))
3008 if not opts.get('dry_run'):
3019 if not opts.get('dry_run'):
3009 util.rename(target, bakname)
3020 util.rename(target, bakname)
3010 if ui.verbose or not exact:
3021 if ui.verbose or not exact:
3011 if not isinstance(msg, basestring):
3022 if not isinstance(msg, basestring):
3012 msg = msg(abs)
3023 msg = msg(abs)
3013 ui.status(msg % rel)
3024 ui.status(msg % rel)
3014 elif exact:
3025 elif exact:
3015 ui.warn(msg % rel)
3026 ui.warn(msg % rel)
3016 break
3027 break
3017
3028
3018
3029
3019 if not opts.get('dry_run'):
3030 if not opts.get('dry_run'):
3020 needdata = ('revert', 'add', 'undelete')
3031 needdata = ('revert', 'add', 'undelete')
3021 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3032 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3022 interactive = opts.get('interactive', False)
3033 interactive = opts.get('interactive', False)
3023 _performrevert(repo, parents, ctx, actions, interactive)
3034 _performrevert(repo, parents, ctx, actions, interactive)
3024
3035
3025 # get the list of subrepos that must be reverted
3036 # get the list of subrepos that must be reverted
3026 subrepomatch = scmutil.match(ctx, pats, opts)
3037 subrepomatch = scmutil.match(ctx, pats, opts)
3027 targetsubs = sorted(s for s in ctx.substate if subrepomatch(s))
3038 targetsubs = sorted(s for s in ctx.substate if subrepomatch(s))
3028
3039
3029 if targetsubs:
3040 if targetsubs:
3030 # Revert the subrepos on the revert list
3041 # Revert the subrepos on the revert list
3031 for sub in targetsubs:
3042 for sub in targetsubs:
3032 ctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3043 ctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3033 finally:
3044 finally:
3034 wlock.release()
3045 wlock.release()
3035
3046
3036 def _revertprefetch(repo, ctx, *files):
3047 def _revertprefetch(repo, ctx, *files):
3037 """Let extension changing the storage layer prefetch content"""
3048 """Let extension changing the storage layer prefetch content"""
3038 pass
3049 pass
3039
3050
3040 def _performrevert(repo, parents, ctx, actions, interactive=False):
3051 def _performrevert(repo, parents, ctx, actions, interactive=False):
3041 """function that actually perform all the actions computed for revert
3052 """function that actually perform all the actions computed for revert
3042
3053
3043 This is an independent function to let extension to plug in and react to
3054 This is an independent function to let extension to plug in and react to
3044 the imminent revert.
3055 the imminent revert.
3045
3056
3046 Make sure you have the working directory locked when calling this function.
3057 Make sure you have the working directory locked when calling this function.
3047 """
3058 """
3048 parent, p2 = parents
3059 parent, p2 = parents
3049 node = ctx.node()
3060 node = ctx.node()
3050 def checkout(f):
3061 def checkout(f):
3051 fc = ctx[f]
3062 fc = ctx[f]
3052 repo.wwrite(f, fc.data(), fc.flags())
3063 repo.wwrite(f, fc.data(), fc.flags())
3053
3064
3054 audit_path = pathutil.pathauditor(repo.root)
3065 audit_path = pathutil.pathauditor(repo.root)
3055 for f in actions['forget'][0]:
3066 for f in actions['forget'][0]:
3056 repo.dirstate.drop(f)
3067 repo.dirstate.drop(f)
3057 for f in actions['remove'][0]:
3068 for f in actions['remove'][0]:
3058 audit_path(f)
3069 audit_path(f)
3059 util.unlinkpath(repo.wjoin(f))
3070 util.unlinkpath(repo.wjoin(f))
3060 repo.dirstate.remove(f)
3071 repo.dirstate.remove(f)
3061 for f in actions['drop'][0]:
3072 for f in actions['drop'][0]:
3062 audit_path(f)
3073 audit_path(f)
3063 repo.dirstate.remove(f)
3074 repo.dirstate.remove(f)
3064
3075
3065 normal = None
3076 normal = None
3066 if node == parent:
3077 if node == parent:
3067 # We're reverting to our parent. If possible, we'd like status
3078 # We're reverting to our parent. If possible, we'd like status
3068 # to report the file as clean. We have to use normallookup for
3079 # to report the file as clean. We have to use normallookup for
3069 # merges to avoid losing information about merged/dirty files.
3080 # merges to avoid losing information about merged/dirty files.
3070 if p2 != nullid:
3081 if p2 != nullid:
3071 normal = repo.dirstate.normallookup
3082 normal = repo.dirstate.normallookup
3072 else:
3083 else:
3073 normal = repo.dirstate.normal
3084 normal = repo.dirstate.normal
3074
3085
3075 if interactive:
3086 if interactive:
3076 # Prompt the user for changes to revert
3087 # Prompt the user for changes to revert
3077 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3088 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3078 m = scmutil.match(ctx, torevert, {})
3089 m = scmutil.match(ctx, torevert, {})
3079 diff = patch.diff(repo, None, ctx.node(), m)
3090 diff = patch.diff(repo, None, ctx.node(), m)
3080 originalchunks = patch.parsepatch(diff)
3091 originalchunks = patch.parsepatch(diff)
3081 try:
3092 try:
3082 chunks = recordfilter(repo.ui, originalchunks)
3093 chunks = recordfilter(repo.ui, originalchunks)
3083 except patch.PatchError, err:
3094 except patch.PatchError, err:
3084 raise util.Abort(_('error parsing patch: %s') % err)
3095 raise util.Abort(_('error parsing patch: %s') % err)
3085
3096
3086 # Apply changes
3097 # Apply changes
3087 fp = cStringIO.StringIO()
3098 fp = cStringIO.StringIO()
3088 for c in chunks:
3099 for c in chunks:
3089 c.write(fp)
3100 c.write(fp)
3090 dopatch = fp.tell()
3101 dopatch = fp.tell()
3091 fp.seek(0)
3102 fp.seek(0)
3092 if dopatch:
3103 if dopatch:
3093 try:
3104 try:
3094 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3105 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3095 except patch.PatchError, err:
3106 except patch.PatchError, err:
3096 raise util.Abort(str(err))
3107 raise util.Abort(str(err))
3097 del fp
3108 del fp
3098
3109
3099 for f in actions['revert'][0]:
3110 for f in actions['revert'][0]:
3100 if normal:
3111 if normal:
3101 normal(f)
3112 normal(f)
3102
3113
3103 else:
3114 else:
3104 for f in actions['revert'][0]:
3115 for f in actions['revert'][0]:
3105 checkout(f)
3116 checkout(f)
3106 if normal:
3117 if normal:
3107 normal(f)
3118 normal(f)
3108
3119
3109 for f in actions['add'][0]:
3120 for f in actions['add'][0]:
3110 checkout(f)
3121 checkout(f)
3111 repo.dirstate.add(f)
3122 repo.dirstate.add(f)
3112
3123
3113 normal = repo.dirstate.normallookup
3124 normal = repo.dirstate.normallookup
3114 if node == parent and p2 == nullid:
3125 if node == parent and p2 == nullid:
3115 normal = repo.dirstate.normal
3126 normal = repo.dirstate.normal
3116 for f in actions['undelete'][0]:
3127 for f in actions['undelete'][0]:
3117 checkout(f)
3128 checkout(f)
3118 normal(f)
3129 normal(f)
3119
3130
3120 copied = copies.pathcopies(repo[parent], ctx)
3131 copied = copies.pathcopies(repo[parent], ctx)
3121
3132
3122 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3133 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3123 if f in copied:
3134 if f in copied:
3124 repo.dirstate.copy(copied[f], f)
3135 repo.dirstate.copy(copied[f], f)
3125
3136
3126 def command(table):
3137 def command(table):
3127 """Returns a function object to be used as a decorator for making commands.
3138 """Returns a function object to be used as a decorator for making commands.
3128
3139
3129 This function receives a command table as its argument. The table should
3140 This function receives a command table as its argument. The table should
3130 be a dict.
3141 be a dict.
3131
3142
3132 The returned function can be used as a decorator for adding commands
3143 The returned function can be used as a decorator for adding commands
3133 to that command table. This function accepts multiple arguments to define
3144 to that command table. This function accepts multiple arguments to define
3134 a command.
3145 a command.
3135
3146
3136 The first argument is the command name.
3147 The first argument is the command name.
3137
3148
3138 The options argument is an iterable of tuples defining command arguments.
3149 The options argument is an iterable of tuples defining command arguments.
3139 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
3150 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
3140
3151
3141 The synopsis argument defines a short, one line summary of how to use the
3152 The synopsis argument defines a short, one line summary of how to use the
3142 command. This shows up in the help output.
3153 command. This shows up in the help output.
3143
3154
3144 The norepo argument defines whether the command does not require a
3155 The norepo argument defines whether the command does not require a
3145 local repository. Most commands operate against a repository, thus the
3156 local repository. Most commands operate against a repository, thus the
3146 default is False.
3157 default is False.
3147
3158
3148 The optionalrepo argument defines whether the command optionally requires
3159 The optionalrepo argument defines whether the command optionally requires
3149 a local repository.
3160 a local repository.
3150
3161
3151 The inferrepo argument defines whether to try to find a repository from the
3162 The inferrepo argument defines whether to try to find a repository from the
3152 command line arguments. If True, arguments will be examined for potential
3163 command line arguments. If True, arguments will be examined for potential
3153 repository locations. See ``findrepo()``. If a repository is found, it
3164 repository locations. See ``findrepo()``. If a repository is found, it
3154 will be used.
3165 will be used.
3155 """
3166 """
3156 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
3167 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
3157 inferrepo=False):
3168 inferrepo=False):
3158 def decorator(func):
3169 def decorator(func):
3159 if synopsis:
3170 if synopsis:
3160 table[name] = func, list(options), synopsis
3171 table[name] = func, list(options), synopsis
3161 else:
3172 else:
3162 table[name] = func, list(options)
3173 table[name] = func, list(options)
3163
3174
3164 if norepo:
3175 if norepo:
3165 # Avoid import cycle.
3176 # Avoid import cycle.
3166 import commands
3177 import commands
3167 commands.norepo += ' %s' % ' '.join(parsealiases(name))
3178 commands.norepo += ' %s' % ' '.join(parsealiases(name))
3168
3179
3169 if optionalrepo:
3180 if optionalrepo:
3170 import commands
3181 import commands
3171 commands.optionalrepo += ' %s' % ' '.join(parsealiases(name))
3182 commands.optionalrepo += ' %s' % ' '.join(parsealiases(name))
3172
3183
3173 if inferrepo:
3184 if inferrepo:
3174 import commands
3185 import commands
3175 commands.inferrepo += ' %s' % ' '.join(parsealiases(name))
3186 commands.inferrepo += ' %s' % ' '.join(parsealiases(name))
3176
3187
3177 return func
3188 return func
3178 return decorator
3189 return decorator
3179
3190
3180 return cmd
3191 return cmd
3181
3192
3182 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3193 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3183 # commands.outgoing. "missing" is "missing" of the result of
3194 # commands.outgoing. "missing" is "missing" of the result of
3184 # "findcommonoutgoing()"
3195 # "findcommonoutgoing()"
3185 outgoinghooks = util.hooks()
3196 outgoinghooks = util.hooks()
3186
3197
3187 # a list of (ui, repo) functions called by commands.summary
3198 # a list of (ui, repo) functions called by commands.summary
3188 summaryhooks = util.hooks()
3199 summaryhooks = util.hooks()
3189
3200
3190 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3201 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3191 #
3202 #
3192 # functions should return tuple of booleans below, if 'changes' is None:
3203 # functions should return tuple of booleans below, if 'changes' is None:
3193 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3204 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3194 #
3205 #
3195 # otherwise, 'changes' is a tuple of tuples below:
3206 # otherwise, 'changes' is a tuple of tuples below:
3196 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3207 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3197 # - (desturl, destbranch, destpeer, outgoing)
3208 # - (desturl, destbranch, destpeer, outgoing)
3198 summaryremotehooks = util.hooks()
3209 summaryremotehooks = util.hooks()
3199
3210
3200 # A list of state files kept by multistep operations like graft.
3211 # A list of state files kept by multistep operations like graft.
3201 # Since graft cannot be aborted, it is considered 'clearable' by update.
3212 # Since graft cannot be aborted, it is considered 'clearable' by update.
3202 # note: bisect is intentionally excluded
3213 # note: bisect is intentionally excluded
3203 # (state file, clearable, allowcommit, error, hint)
3214 # (state file, clearable, allowcommit, error, hint)
3204 unfinishedstates = [
3215 unfinishedstates = [
3205 ('graftstate', True, False, _('graft in progress'),
3216 ('graftstate', True, False, _('graft in progress'),
3206 _("use 'hg graft --continue' or 'hg update' to abort")),
3217 _("use 'hg graft --continue' or 'hg update' to abort")),
3207 ('updatestate', True, False, _('last update was interrupted'),
3218 ('updatestate', True, False, _('last update was interrupted'),
3208 _("use 'hg update' to get a consistent checkout"))
3219 _("use 'hg update' to get a consistent checkout"))
3209 ]
3220 ]
3210
3221
3211 def checkunfinished(repo, commit=False):
3222 def checkunfinished(repo, commit=False):
3212 '''Look for an unfinished multistep operation, like graft, and abort
3223 '''Look for an unfinished multistep operation, like graft, and abort
3213 if found. It's probably good to check this right before
3224 if found. It's probably good to check this right before
3214 bailifchanged().
3225 bailifchanged().
3215 '''
3226 '''
3216 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3227 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3217 if commit and allowcommit:
3228 if commit and allowcommit:
3218 continue
3229 continue
3219 if repo.vfs.exists(f):
3230 if repo.vfs.exists(f):
3220 raise util.Abort(msg, hint=hint)
3231 raise util.Abort(msg, hint=hint)
3221
3232
3222 def clearunfinished(repo):
3233 def clearunfinished(repo):
3223 '''Check for unfinished operations (as above), and clear the ones
3234 '''Check for unfinished operations (as above), and clear the ones
3224 that are clearable.
3235 that are clearable.
3225 '''
3236 '''
3226 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3237 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3227 if not clearable and repo.vfs.exists(f):
3238 if not clearable and repo.vfs.exists(f):
3228 raise util.Abort(msg, hint=hint)
3239 raise util.Abort(msg, hint=hint)
3229 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3240 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3230 if clearable and repo.vfs.exists(f):
3241 if clearable and repo.vfs.exists(f):
3231 util.unlink(repo.join(f))
3242 util.unlink(repo.join(f))
@@ -1,6367 +1,6367 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 (DEPRECATED)'), _('TEMPLATE')),
108 _('display with template (DEPRECATED)'), _('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 ]
151 ]
152
152
153 mergetoolopts = [
153 mergetoolopts = [
154 ('t', 'tool', '', _('specify merge tool')),
154 ('t', 'tool', '', _('specify merge tool')),
155 ]
155 ]
156
156
157 similarityopts = [
157 similarityopts = [
158 ('s', 'similarity', '',
158 ('s', 'similarity', '',
159 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
159 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
160 ]
160 ]
161
161
162 subrepoopts = [
162 subrepoopts = [
163 ('S', 'subrepos', None,
163 ('S', 'subrepos', None,
164 _('recurse into subrepositories'))
164 _('recurse into subrepositories'))
165 ]
165 ]
166
166
167 # Commands start here, listed alphabetically
167 # Commands start here, listed alphabetically
168
168
169 @command('^add',
169 @command('^add',
170 walkopts + subrepoopts + dryrunopts,
170 walkopts + subrepoopts + dryrunopts,
171 _('[OPTION]... [FILE]...'),
171 _('[OPTION]... [FILE]...'),
172 inferrepo=True)
172 inferrepo=True)
173 def add(ui, repo, *pats, **opts):
173 def add(ui, repo, *pats, **opts):
174 """add the specified files on the next commit
174 """add the specified files on the next commit
175
175
176 Schedule files to be version controlled and added to the
176 Schedule files to be version controlled and added to the
177 repository.
177 repository.
178
178
179 The files will be added to the repository at the next commit. To
179 The files will be added to the repository at the next commit. To
180 undo an add before that, see :hg:`forget`.
180 undo an add before that, see :hg:`forget`.
181
181
182 If no names are given, add all files to the repository.
182 If no names are given, add all files to the repository.
183
183
184 .. container:: verbose
184 .. container:: verbose
185
185
186 An example showing how new (unknown) files are added
186 An example showing how new (unknown) files are added
187 automatically by :hg:`add`::
187 automatically by :hg:`add`::
188
188
189 $ ls
189 $ ls
190 foo.c
190 foo.c
191 $ hg status
191 $ hg status
192 ? foo.c
192 ? foo.c
193 $ hg add
193 $ hg add
194 adding foo.c
194 adding foo.c
195 $ hg status
195 $ hg status
196 A foo.c
196 A foo.c
197
197
198 Returns 0 if all files are successfully added.
198 Returns 0 if all files are successfully added.
199 """
199 """
200
200
201 m = scmutil.match(repo[None], pats, opts)
201 m = scmutil.match(repo[None], pats, opts)
202 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
202 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
203 return rejected and 1 or 0
203 return rejected and 1 or 0
204
204
205 @command('addremove',
205 @command('addremove',
206 similarityopts + subrepoopts + walkopts + dryrunopts,
206 similarityopts + subrepoopts + walkopts + dryrunopts,
207 _('[OPTION]... [FILE]...'),
207 _('[OPTION]... [FILE]...'),
208 inferrepo=True)
208 inferrepo=True)
209 def addremove(ui, repo, *pats, **opts):
209 def addremove(ui, repo, *pats, **opts):
210 """add all new files, delete all missing files
210 """add all new files, delete all missing files
211
211
212 Add all new files and remove all missing files from the
212 Add all new files and remove all missing files from the
213 repository.
213 repository.
214
214
215 New files are ignored if they match any of the patterns in
215 New files are ignored if they match any of the patterns in
216 ``.hgignore``. As with add, these changes take effect at the next
216 ``.hgignore``. As with add, these changes take effect at the next
217 commit.
217 commit.
218
218
219 Use the -s/--similarity option to detect renamed files. This
219 Use the -s/--similarity option to detect renamed files. This
220 option takes a percentage between 0 (disabled) and 100 (files must
220 option takes a percentage between 0 (disabled) and 100 (files must
221 be identical) as its parameter. With a parameter greater than 0,
221 be identical) as its parameter. With a parameter greater than 0,
222 this compares every removed file with every added file and records
222 this compares every removed file with every added file and records
223 those similar enough as renames. Detecting renamed files this way
223 those similar enough as renames. Detecting renamed files this way
224 can be expensive. After using this option, :hg:`status -C` can be
224 can be expensive. After using this option, :hg:`status -C` can be
225 used to check which files were identified as moved or renamed. If
225 used to check which files were identified as moved or renamed. If
226 not specified, -s/--similarity defaults to 100 and only renames of
226 not specified, -s/--similarity defaults to 100 and only renames of
227 identical files are detected.
227 identical files are detected.
228
228
229 Returns 0 if all files are successfully added.
229 Returns 0 if all files are successfully added.
230 """
230 """
231 try:
231 try:
232 sim = float(opts.get('similarity') or 100)
232 sim = float(opts.get('similarity') or 100)
233 except ValueError:
233 except ValueError:
234 raise util.Abort(_('similarity must be a number'))
234 raise util.Abort(_('similarity must be a number'))
235 if sim < 0 or sim > 100:
235 if sim < 0 or sim > 100:
236 raise util.Abort(_('similarity must be between 0 and 100'))
236 raise util.Abort(_('similarity must be between 0 and 100'))
237 matcher = scmutil.match(repo[None], pats, opts)
237 matcher = scmutil.match(repo[None], pats, opts)
238 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
238 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
239
239
240 @command('^annotate|blame',
240 @command('^annotate|blame',
241 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
241 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
242 ('', 'follow', None,
242 ('', 'follow', None,
243 _('follow copies/renames and list the filename (DEPRECATED)')),
243 _('follow copies/renames and list the filename (DEPRECATED)')),
244 ('', 'no-follow', None, _("don't follow copies and renames")),
244 ('', 'no-follow', None, _("don't follow copies and renames")),
245 ('a', 'text', None, _('treat all files as text')),
245 ('a', 'text', None, _('treat all files as text')),
246 ('u', 'user', None, _('list the author (long with -v)')),
246 ('u', 'user', None, _('list the author (long with -v)')),
247 ('f', 'file', None, _('list the filename')),
247 ('f', 'file', None, _('list the filename')),
248 ('d', 'date', None, _('list the date (short with -q)')),
248 ('d', 'date', None, _('list the date (short with -q)')),
249 ('n', 'number', None, _('list the revision number (default)')),
249 ('n', 'number', None, _('list the revision number (default)')),
250 ('c', 'changeset', None, _('list the changeset')),
250 ('c', 'changeset', None, _('list the changeset')),
251 ('l', 'line-number', None, _('show line number at the first appearance'))
251 ('l', 'line-number', None, _('show line number at the first appearance'))
252 ] + diffwsopts + walkopts + formatteropts,
252 ] + diffwsopts + walkopts + formatteropts,
253 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
253 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
254 inferrepo=True)
254 inferrepo=True)
255 def annotate(ui, repo, *pats, **opts):
255 def annotate(ui, repo, *pats, **opts):
256 """show changeset information by line for each file
256 """show changeset information by line for each file
257
257
258 List changes in files, showing the revision id responsible for
258 List changes in files, showing the revision id responsible for
259 each line
259 each line
260
260
261 This command is useful for discovering when a change was made and
261 This command is useful for discovering when a change was made and
262 by whom.
262 by whom.
263
263
264 Without the -a/--text option, annotate will avoid processing files
264 Without the -a/--text option, annotate will avoid processing files
265 it detects as binary. With -a, annotate will annotate the file
265 it detects as binary. With -a, annotate will annotate the file
266 anyway, although the results will probably be neither useful
266 anyway, although the results will probably be neither useful
267 nor desirable.
267 nor desirable.
268
268
269 Returns 0 on success.
269 Returns 0 on success.
270 """
270 """
271 if not pats:
271 if not pats:
272 raise util.Abort(_('at least one filename or pattern is required'))
272 raise util.Abort(_('at least one filename or pattern is required'))
273
273
274 if opts.get('follow'):
274 if opts.get('follow'):
275 # --follow is deprecated and now just an alias for -f/--file
275 # --follow is deprecated and now just an alias for -f/--file
276 # to mimic the behavior of Mercurial before version 1.5
276 # to mimic the behavior of Mercurial before version 1.5
277 opts['file'] = True
277 opts['file'] = True
278
278
279 fm = ui.formatter('annotate', opts)
279 fm = ui.formatter('annotate', opts)
280 if ui.quiet:
280 if ui.quiet:
281 datefunc = util.shortdate
281 datefunc = util.shortdate
282 else:
282 else:
283 datefunc = util.datestr
283 datefunc = util.datestr
284 hexfn = fm.hexfunc
284 hexfn = fm.hexfunc
285
285
286 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
286 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
287 ('number', ' ', lambda x: x[0].rev(), str),
287 ('number', ' ', lambda x: x[0].rev(), str),
288 ('changeset', ' ', lambda x: hexfn(x[0].node()), str),
288 ('changeset', ' ', lambda x: hexfn(x[0].node()), str),
289 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
289 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
290 ('file', ' ', lambda x: x[0].path(), str),
290 ('file', ' ', lambda x: x[0].path(), str),
291 ('line_number', ':', lambda x: x[1], str),
291 ('line_number', ':', lambda x: x[1], str),
292 ]
292 ]
293 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
293 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
294
294
295 if (not opts.get('user') and not opts.get('changeset')
295 if (not opts.get('user') and not opts.get('changeset')
296 and not opts.get('date') and not opts.get('file')):
296 and not opts.get('date') and not opts.get('file')):
297 opts['number'] = True
297 opts['number'] = True
298
298
299 linenumber = opts.get('line_number') is not None
299 linenumber = opts.get('line_number') is not None
300 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
300 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
301 raise util.Abort(_('at least one of -n/-c is required for -l'))
301 raise util.Abort(_('at least one of -n/-c is required for -l'))
302
302
303 if fm:
303 if fm:
304 def makefunc(get, fmt):
304 def makefunc(get, fmt):
305 return get
305 return get
306 else:
306 else:
307 def makefunc(get, fmt):
307 def makefunc(get, fmt):
308 return lambda x: fmt(get(x))
308 return lambda x: fmt(get(x))
309 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
309 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
310 if opts.get(op)]
310 if opts.get(op)]
311 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
311 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
312 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
312 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
313 if opts.get(op))
313 if opts.get(op))
314
314
315 def bad(x, y):
315 def bad(x, y):
316 raise util.Abort("%s: %s" % (x, y))
316 raise util.Abort("%s: %s" % (x, y))
317
317
318 ctx = scmutil.revsingle(repo, opts.get('rev'))
318 ctx = scmutil.revsingle(repo, opts.get('rev'))
319 m = scmutil.match(ctx, pats, opts)
319 m = scmutil.match(ctx, pats, opts)
320 m.bad = bad
320 m.bad = bad
321 follow = not opts.get('no_follow')
321 follow = not opts.get('no_follow')
322 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
322 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
323 whitespace=True)
323 whitespace=True)
324 for abs in ctx.walk(m):
324 for abs in ctx.walk(m):
325 fctx = ctx[abs]
325 fctx = ctx[abs]
326 if not opts.get('text') and util.binary(fctx.data()):
326 if not opts.get('text') and util.binary(fctx.data()):
327 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
327 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
328 continue
328 continue
329
329
330 lines = fctx.annotate(follow=follow, linenumber=linenumber,
330 lines = fctx.annotate(follow=follow, linenumber=linenumber,
331 diffopts=diffopts)
331 diffopts=diffopts)
332 formats = []
332 formats = []
333 pieces = []
333 pieces = []
334
334
335 for f, sep in funcmap:
335 for f, sep in funcmap:
336 l = [f(n) for n, dummy in lines]
336 l = [f(n) for n, dummy in lines]
337 if l:
337 if l:
338 if fm:
338 if fm:
339 formats.append(['%s' for x in l])
339 formats.append(['%s' for x in l])
340 else:
340 else:
341 sizes = [encoding.colwidth(x) for x in l]
341 sizes = [encoding.colwidth(x) for x in l]
342 ml = max(sizes)
342 ml = max(sizes)
343 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
343 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
344 pieces.append(l)
344 pieces.append(l)
345
345
346 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
346 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
347 fm.startitem()
347 fm.startitem()
348 fm.write(fields, "".join(f), *p)
348 fm.write(fields, "".join(f), *p)
349 fm.write('line', ": %s", l[1])
349 fm.write('line', ": %s", l[1])
350
350
351 if lines and not lines[-1][1].endswith('\n'):
351 if lines and not lines[-1][1].endswith('\n'):
352 fm.plain('\n')
352 fm.plain('\n')
353
353
354 fm.end()
354 fm.end()
355
355
356 @command('archive',
356 @command('archive',
357 [('', 'no-decode', None, _('do not pass files through decoders')),
357 [('', 'no-decode', None, _('do not pass files through decoders')),
358 ('p', 'prefix', '', _('directory prefix for files in archive'),
358 ('p', 'prefix', '', _('directory prefix for files in archive'),
359 _('PREFIX')),
359 _('PREFIX')),
360 ('r', 'rev', '', _('revision to distribute'), _('REV')),
360 ('r', 'rev', '', _('revision to distribute'), _('REV')),
361 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
361 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
362 ] + subrepoopts + walkopts,
362 ] + subrepoopts + walkopts,
363 _('[OPTION]... DEST'))
363 _('[OPTION]... DEST'))
364 def archive(ui, repo, dest, **opts):
364 def archive(ui, repo, dest, **opts):
365 '''create an unversioned archive of a repository revision
365 '''create an unversioned archive of a repository revision
366
366
367 By default, the revision used is the parent of the working
367 By default, the revision used is the parent of the working
368 directory; use -r/--rev to specify a different revision.
368 directory; use -r/--rev to specify a different revision.
369
369
370 The archive type is automatically detected based on file
370 The archive type is automatically detected based on file
371 extension (or override using -t/--type).
371 extension (or override using -t/--type).
372
372
373 .. container:: verbose
373 .. container:: verbose
374
374
375 Examples:
375 Examples:
376
376
377 - create a zip file containing the 1.0 release::
377 - create a zip file containing the 1.0 release::
378
378
379 hg archive -r 1.0 project-1.0.zip
379 hg archive -r 1.0 project-1.0.zip
380
380
381 - create a tarball excluding .hg files::
381 - create a tarball excluding .hg files::
382
382
383 hg archive project.tar.gz -X ".hg*"
383 hg archive project.tar.gz -X ".hg*"
384
384
385 Valid types are:
385 Valid types are:
386
386
387 :``files``: a directory full of files (default)
387 :``files``: a directory full of files (default)
388 :``tar``: tar archive, uncompressed
388 :``tar``: tar archive, uncompressed
389 :``tbz2``: tar archive, compressed using bzip2
389 :``tbz2``: tar archive, compressed using bzip2
390 :``tgz``: tar archive, compressed using gzip
390 :``tgz``: tar archive, compressed using gzip
391 :``uzip``: zip archive, uncompressed
391 :``uzip``: zip archive, uncompressed
392 :``zip``: zip archive, compressed using deflate
392 :``zip``: zip archive, compressed using deflate
393
393
394 The exact name of the destination archive or directory is given
394 The exact name of the destination archive or directory is given
395 using a format string; see :hg:`help export` for details.
395 using a format string; see :hg:`help export` for details.
396
396
397 Each member added to an archive file has a directory prefix
397 Each member added to an archive file has a directory prefix
398 prepended. Use -p/--prefix to specify a format string for the
398 prepended. Use -p/--prefix to specify a format string for the
399 prefix. The default is the basename of the archive, with suffixes
399 prefix. The default is the basename of the archive, with suffixes
400 removed.
400 removed.
401
401
402 Returns 0 on success.
402 Returns 0 on success.
403 '''
403 '''
404
404
405 ctx = scmutil.revsingle(repo, opts.get('rev'))
405 ctx = scmutil.revsingle(repo, opts.get('rev'))
406 if not ctx:
406 if not ctx:
407 raise util.Abort(_('no working directory: please specify a revision'))
407 raise util.Abort(_('no working directory: please specify a revision'))
408 node = ctx.node()
408 node = ctx.node()
409 dest = cmdutil.makefilename(repo, dest, node)
409 dest = cmdutil.makefilename(repo, dest, node)
410 if os.path.realpath(dest) == repo.root:
410 if os.path.realpath(dest) == repo.root:
411 raise util.Abort(_('repository root cannot be destination'))
411 raise util.Abort(_('repository root cannot be destination'))
412
412
413 kind = opts.get('type') or archival.guesskind(dest) or 'files'
413 kind = opts.get('type') or archival.guesskind(dest) or 'files'
414 prefix = opts.get('prefix')
414 prefix = opts.get('prefix')
415
415
416 if dest == '-':
416 if dest == '-':
417 if kind == 'files':
417 if kind == 'files':
418 raise util.Abort(_('cannot archive plain files to stdout'))
418 raise util.Abort(_('cannot archive plain files to stdout'))
419 dest = cmdutil.makefileobj(repo, dest)
419 dest = cmdutil.makefileobj(repo, dest)
420 if not prefix:
420 if not prefix:
421 prefix = os.path.basename(repo.root) + '-%h'
421 prefix = os.path.basename(repo.root) + '-%h'
422
422
423 prefix = cmdutil.makefilename(repo, prefix, node)
423 prefix = cmdutil.makefilename(repo, prefix, node)
424 matchfn = scmutil.match(ctx, [], opts)
424 matchfn = scmutil.match(ctx, [], opts)
425 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
425 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
426 matchfn, prefix, subrepos=opts.get('subrepos'))
426 matchfn, prefix, subrepos=opts.get('subrepos'))
427
427
428 @command('backout',
428 @command('backout',
429 [('', 'merge', None, _('merge with old dirstate parent after backout')),
429 [('', 'merge', None, _('merge with old dirstate parent after backout')),
430 ('', 'commit', None, _('commit if no conflicts were encountered')),
430 ('', 'commit', None, _('commit if no conflicts were encountered')),
431 ('', 'parent', '',
431 ('', 'parent', '',
432 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
432 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
433 ('r', 'rev', '', _('revision to backout'), _('REV')),
433 ('r', 'rev', '', _('revision to backout'), _('REV')),
434 ('e', 'edit', False, _('invoke editor on commit messages')),
434 ('e', 'edit', False, _('invoke editor on commit messages')),
435 ] + mergetoolopts + walkopts + commitopts + commitopts2,
435 ] + mergetoolopts + walkopts + commitopts + commitopts2,
436 _('[OPTION]... [-r] REV'))
436 _('[OPTION]... [-r] REV'))
437 def backout(ui, repo, node=None, rev=None, commit=False, **opts):
437 def backout(ui, repo, node=None, rev=None, commit=False, **opts):
438 '''reverse effect of earlier changeset
438 '''reverse effect of earlier changeset
439
439
440 Prepare a new changeset with the effect of REV undone in the
440 Prepare a new changeset with the effect of REV undone in the
441 current working directory.
441 current working directory.
442
442
443 If REV is the parent of the working directory, then this new changeset
443 If REV is the parent of the working directory, then this new changeset
444 is committed automatically. Otherwise, hg needs to merge the
444 is committed automatically. Otherwise, hg needs to merge the
445 changes and the merged result is left uncommitted.
445 changes and the merged result is left uncommitted.
446
446
447 .. note::
447 .. note::
448
448
449 backout cannot be used to fix either an unwanted or
449 backout cannot be used to fix either an unwanted or
450 incorrect merge.
450 incorrect merge.
451
451
452 .. container:: verbose
452 .. container:: verbose
453
453
454 By default, the pending changeset will have one parent,
454 By default, the pending changeset will have one parent,
455 maintaining a linear history. With --merge, the pending
455 maintaining a linear history. With --merge, the pending
456 changeset will instead have two parents: the old parent of the
456 changeset will instead have two parents: the old parent of the
457 working directory and a new child of REV that simply undoes REV.
457 working directory and a new child of REV that simply undoes REV.
458
458
459 Before version 1.7, the behavior without --merge was equivalent
459 Before version 1.7, the behavior without --merge was equivalent
460 to specifying --merge followed by :hg:`update --clean .` to
460 to specifying --merge followed by :hg:`update --clean .` to
461 cancel the merge and leave the child of REV as a head to be
461 cancel the merge and leave the child of REV as a head to be
462 merged separately.
462 merged separately.
463
463
464 See :hg:`help dates` for a list of formats valid for -d/--date.
464 See :hg:`help dates` for a list of formats valid for -d/--date.
465
465
466 Returns 0 on success, 1 if nothing to backout or there are unresolved
466 Returns 0 on success, 1 if nothing to backout or there are unresolved
467 files.
467 files.
468 '''
468 '''
469 if rev and node:
469 if rev and node:
470 raise util.Abort(_("please specify just one revision"))
470 raise util.Abort(_("please specify just one revision"))
471
471
472 if not rev:
472 if not rev:
473 rev = node
473 rev = node
474
474
475 if not rev:
475 if not rev:
476 raise util.Abort(_("please specify a revision to backout"))
476 raise util.Abort(_("please specify a revision to backout"))
477
477
478 date = opts.get('date')
478 date = opts.get('date')
479 if date:
479 if date:
480 opts['date'] = util.parsedate(date)
480 opts['date'] = util.parsedate(date)
481
481
482 cmdutil.checkunfinished(repo)
482 cmdutil.checkunfinished(repo)
483 cmdutil.bailifchanged(repo)
483 cmdutil.bailifchanged(repo)
484 node = scmutil.revsingle(repo, rev).node()
484 node = scmutil.revsingle(repo, rev).node()
485
485
486 op1, op2 = repo.dirstate.parents()
486 op1, op2 = repo.dirstate.parents()
487 if not repo.changelog.isancestor(node, op1):
487 if not repo.changelog.isancestor(node, op1):
488 raise util.Abort(_('cannot backout change that is not an ancestor'))
488 raise util.Abort(_('cannot backout change that is not an ancestor'))
489
489
490 p1, p2 = repo.changelog.parents(node)
490 p1, p2 = repo.changelog.parents(node)
491 if p1 == nullid:
491 if p1 == nullid:
492 raise util.Abort(_('cannot backout a change with no parents'))
492 raise util.Abort(_('cannot backout a change with no parents'))
493 if p2 != nullid:
493 if p2 != nullid:
494 if not opts.get('parent'):
494 if not opts.get('parent'):
495 raise util.Abort(_('cannot backout a merge changeset'))
495 raise util.Abort(_('cannot backout a merge changeset'))
496 p = repo.lookup(opts['parent'])
496 p = repo.lookup(opts['parent'])
497 if p not in (p1, p2):
497 if p not in (p1, p2):
498 raise util.Abort(_('%s is not a parent of %s') %
498 raise util.Abort(_('%s is not a parent of %s') %
499 (short(p), short(node)))
499 (short(p), short(node)))
500 parent = p
500 parent = p
501 else:
501 else:
502 if opts.get('parent'):
502 if opts.get('parent'):
503 raise util.Abort(_('cannot use --parent on non-merge changeset'))
503 raise util.Abort(_('cannot use --parent on non-merge changeset'))
504 parent = p1
504 parent = p1
505
505
506 # the backout should appear on the same branch
506 # the backout should appear on the same branch
507 wlock = repo.wlock()
507 wlock = repo.wlock()
508 try:
508 try:
509 branch = repo.dirstate.branch()
509 branch = repo.dirstate.branch()
510 bheads = repo.branchheads(branch)
510 bheads = repo.branchheads(branch)
511 rctx = scmutil.revsingle(repo, hex(parent))
511 rctx = scmutil.revsingle(repo, hex(parent))
512 if not opts.get('merge') and op1 != node:
512 if not opts.get('merge') and op1 != node:
513 try:
513 try:
514 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
514 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
515 'backout')
515 'backout')
516 repo.dirstate.beginparentchange()
516 repo.dirstate.beginparentchange()
517 stats = mergemod.update(repo, parent, True, True, False,
517 stats = mergemod.update(repo, parent, True, True, False,
518 node, False)
518 node, False)
519 repo.setparents(op1, op2)
519 repo.setparents(op1, op2)
520 repo.dirstate.endparentchange()
520 repo.dirstate.endparentchange()
521 hg._showstats(repo, stats)
521 hg._showstats(repo, stats)
522 if stats[3]:
522 if stats[3]:
523 repo.ui.status(_("use 'hg resolve' to retry unresolved "
523 repo.ui.status(_("use 'hg resolve' to retry unresolved "
524 "file merges\n"))
524 "file merges\n"))
525 return 1
525 return 1
526 elif not commit:
526 elif not commit:
527 msg = _("changeset %s backed out, "
527 msg = _("changeset %s backed out, "
528 "don't forget to commit.\n")
528 "don't forget to commit.\n")
529 ui.status(msg % short(node))
529 ui.status(msg % short(node))
530 return 0
530 return 0
531 finally:
531 finally:
532 ui.setconfig('ui', 'forcemerge', '', '')
532 ui.setconfig('ui', 'forcemerge', '', '')
533 else:
533 else:
534 hg.clean(repo, node, show_stats=False)
534 hg.clean(repo, node, show_stats=False)
535 repo.dirstate.setbranch(branch)
535 repo.dirstate.setbranch(branch)
536 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
536 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
537
537
538
538
539 def commitfunc(ui, repo, message, match, opts):
539 def commitfunc(ui, repo, message, match, opts):
540 editform = 'backout'
540 editform = 'backout'
541 e = cmdutil.getcommiteditor(editform=editform, **opts)
541 e = cmdutil.getcommiteditor(editform=editform, **opts)
542 if not message:
542 if not message:
543 # we don't translate commit messages
543 # we don't translate commit messages
544 message = "Backed out changeset %s" % short(node)
544 message = "Backed out changeset %s" % short(node)
545 e = cmdutil.getcommiteditor(edit=True, editform=editform)
545 e = cmdutil.getcommiteditor(edit=True, editform=editform)
546 return repo.commit(message, opts.get('user'), opts.get('date'),
546 return repo.commit(message, opts.get('user'), opts.get('date'),
547 match, editor=e)
547 match, editor=e)
548 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
548 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
549 if not newnode:
549 if not newnode:
550 ui.status(_("nothing changed\n"))
550 ui.status(_("nothing changed\n"))
551 return 1
551 return 1
552 cmdutil.commitstatus(repo, newnode, branch, bheads)
552 cmdutil.commitstatus(repo, newnode, branch, bheads)
553
553
554 def nice(node):
554 def nice(node):
555 return '%d:%s' % (repo.changelog.rev(node), short(node))
555 return '%d:%s' % (repo.changelog.rev(node), short(node))
556 ui.status(_('changeset %s backs out changeset %s\n') %
556 ui.status(_('changeset %s backs out changeset %s\n') %
557 (nice(repo.changelog.tip()), nice(node)))
557 (nice(repo.changelog.tip()), nice(node)))
558 if opts.get('merge') and op1 != node:
558 if opts.get('merge') and op1 != node:
559 hg.clean(repo, op1, show_stats=False)
559 hg.clean(repo, op1, show_stats=False)
560 ui.status(_('merging with changeset %s\n')
560 ui.status(_('merging with changeset %s\n')
561 % nice(repo.changelog.tip()))
561 % nice(repo.changelog.tip()))
562 try:
562 try:
563 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
563 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
564 'backout')
564 'backout')
565 return hg.merge(repo, hex(repo.changelog.tip()))
565 return hg.merge(repo, hex(repo.changelog.tip()))
566 finally:
566 finally:
567 ui.setconfig('ui', 'forcemerge', '', '')
567 ui.setconfig('ui', 'forcemerge', '', '')
568 finally:
568 finally:
569 wlock.release()
569 wlock.release()
570 return 0
570 return 0
571
571
572 @command('bisect',
572 @command('bisect',
573 [('r', 'reset', False, _('reset bisect state')),
573 [('r', 'reset', False, _('reset bisect state')),
574 ('g', 'good', False, _('mark changeset good')),
574 ('g', 'good', False, _('mark changeset good')),
575 ('b', 'bad', False, _('mark changeset bad')),
575 ('b', 'bad', False, _('mark changeset bad')),
576 ('s', 'skip', False, _('skip testing changeset')),
576 ('s', 'skip', False, _('skip testing changeset')),
577 ('e', 'extend', False, _('extend the bisect range')),
577 ('e', 'extend', False, _('extend the bisect range')),
578 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
578 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
579 ('U', 'noupdate', False, _('do not update to target'))],
579 ('U', 'noupdate', False, _('do not update to target'))],
580 _("[-gbsr] [-U] [-c CMD] [REV]"))
580 _("[-gbsr] [-U] [-c CMD] [REV]"))
581 def bisect(ui, repo, rev=None, extra=None, command=None,
581 def bisect(ui, repo, rev=None, extra=None, command=None,
582 reset=None, good=None, bad=None, skip=None, extend=None,
582 reset=None, good=None, bad=None, skip=None, extend=None,
583 noupdate=None):
583 noupdate=None):
584 """subdivision search of changesets
584 """subdivision search of changesets
585
585
586 This command helps to find changesets which introduce problems. To
586 This command helps to find changesets which introduce problems. To
587 use, mark the earliest changeset you know exhibits the problem as
587 use, mark the earliest changeset you know exhibits the problem as
588 bad, then mark the latest changeset which is free from the problem
588 bad, then mark the latest changeset which is free from the problem
589 as good. Bisect will update your working directory to a revision
589 as good. Bisect will update your working directory to a revision
590 for testing (unless the -U/--noupdate option is specified). Once
590 for testing (unless the -U/--noupdate option is specified). Once
591 you have performed tests, mark the working directory as good or
591 you have performed tests, mark the working directory as good or
592 bad, and bisect will either update to another candidate changeset
592 bad, and bisect will either update to another candidate changeset
593 or announce that it has found the bad revision.
593 or announce that it has found the bad revision.
594
594
595 As a shortcut, you can also use the revision argument to mark a
595 As a shortcut, you can also use the revision argument to mark a
596 revision as good or bad without checking it out first.
596 revision as good or bad without checking it out first.
597
597
598 If you supply a command, it will be used for automatic bisection.
598 If you supply a command, it will be used for automatic bisection.
599 The environment variable HG_NODE will contain the ID of the
599 The environment variable HG_NODE will contain the ID of the
600 changeset being tested. The exit status of the command will be
600 changeset being tested. The exit status of the command will be
601 used to mark revisions as good or bad: status 0 means good, 125
601 used to mark revisions as good or bad: status 0 means good, 125
602 means to skip the revision, 127 (command not found) will abort the
602 means to skip the revision, 127 (command not found) will abort the
603 bisection, and any other non-zero exit status means the revision
603 bisection, and any other non-zero exit status means the revision
604 is bad.
604 is bad.
605
605
606 .. container:: verbose
606 .. container:: verbose
607
607
608 Some examples:
608 Some examples:
609
609
610 - start a bisection with known bad revision 34, and good revision 12::
610 - start a bisection with known bad revision 34, and good revision 12::
611
611
612 hg bisect --bad 34
612 hg bisect --bad 34
613 hg bisect --good 12
613 hg bisect --good 12
614
614
615 - advance the current bisection by marking current revision as good or
615 - advance the current bisection by marking current revision as good or
616 bad::
616 bad::
617
617
618 hg bisect --good
618 hg bisect --good
619 hg bisect --bad
619 hg bisect --bad
620
620
621 - mark the current revision, or a known revision, to be skipped (e.g. if
621 - mark the current revision, or a known revision, to be skipped (e.g. if
622 that revision is not usable because of another issue)::
622 that revision is not usable because of another issue)::
623
623
624 hg bisect --skip
624 hg bisect --skip
625 hg bisect --skip 23
625 hg bisect --skip 23
626
626
627 - skip all revisions that do not touch directories ``foo`` or ``bar``::
627 - skip all revisions that do not touch directories ``foo`` or ``bar``::
628
628
629 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
629 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
630
630
631 - forget the current bisection::
631 - forget the current bisection::
632
632
633 hg bisect --reset
633 hg bisect --reset
634
634
635 - use 'make && make tests' to automatically find the first broken
635 - use 'make && make tests' to automatically find the first broken
636 revision::
636 revision::
637
637
638 hg bisect --reset
638 hg bisect --reset
639 hg bisect --bad 34
639 hg bisect --bad 34
640 hg bisect --good 12
640 hg bisect --good 12
641 hg bisect --command "make && make tests"
641 hg bisect --command "make && make tests"
642
642
643 - see all changesets whose states are already known in the current
643 - see all changesets whose states are already known in the current
644 bisection::
644 bisection::
645
645
646 hg log -r "bisect(pruned)"
646 hg log -r "bisect(pruned)"
647
647
648 - see the changeset currently being bisected (especially useful
648 - see the changeset currently being bisected (especially useful
649 if running with -U/--noupdate)::
649 if running with -U/--noupdate)::
650
650
651 hg log -r "bisect(current)"
651 hg log -r "bisect(current)"
652
652
653 - see all changesets that took part in the current bisection::
653 - see all changesets that took part in the current bisection::
654
654
655 hg log -r "bisect(range)"
655 hg log -r "bisect(range)"
656
656
657 - you can even get a nice graph::
657 - you can even get a nice graph::
658
658
659 hg log --graph -r "bisect(range)"
659 hg log --graph -r "bisect(range)"
660
660
661 See :hg:`help revsets` for more about the `bisect()` keyword.
661 See :hg:`help revsets` for more about the `bisect()` keyword.
662
662
663 Returns 0 on success.
663 Returns 0 on success.
664 """
664 """
665 def extendbisectrange(nodes, good):
665 def extendbisectrange(nodes, good):
666 # bisect is incomplete when it ends on a merge node and
666 # bisect is incomplete when it ends on a merge node and
667 # one of the parent was not checked.
667 # one of the parent was not checked.
668 parents = repo[nodes[0]].parents()
668 parents = repo[nodes[0]].parents()
669 if len(parents) > 1:
669 if len(parents) > 1:
670 if good:
670 if good:
671 side = state['bad']
671 side = state['bad']
672 else:
672 else:
673 side = state['good']
673 side = state['good']
674 num = len(set(i.node() for i in parents) & set(side))
674 num = len(set(i.node() for i in parents) & set(side))
675 if num == 1:
675 if num == 1:
676 return parents[0].ancestor(parents[1])
676 return parents[0].ancestor(parents[1])
677 return None
677 return None
678
678
679 def print_result(nodes, good):
679 def print_result(nodes, good):
680 displayer = cmdutil.show_changeset(ui, repo, {})
680 displayer = cmdutil.show_changeset(ui, repo, {})
681 if len(nodes) == 1:
681 if len(nodes) == 1:
682 # narrowed it down to a single revision
682 # narrowed it down to a single revision
683 if good:
683 if good:
684 ui.write(_("The first good revision is:\n"))
684 ui.write(_("The first good revision is:\n"))
685 else:
685 else:
686 ui.write(_("The first bad revision is:\n"))
686 ui.write(_("The first bad revision is:\n"))
687 displayer.show(repo[nodes[0]])
687 displayer.show(repo[nodes[0]])
688 extendnode = extendbisectrange(nodes, good)
688 extendnode = extendbisectrange(nodes, good)
689 if extendnode is not None:
689 if extendnode is not None:
690 ui.write(_('Not all ancestors of this changeset have been'
690 ui.write(_('Not all ancestors of this changeset have been'
691 ' checked.\nUse bisect --extend to continue the '
691 ' checked.\nUse bisect --extend to continue the '
692 'bisection from\nthe common ancestor, %s.\n')
692 'bisection from\nthe common ancestor, %s.\n')
693 % extendnode)
693 % extendnode)
694 else:
694 else:
695 # multiple possible revisions
695 # multiple possible revisions
696 if good:
696 if good:
697 ui.write(_("Due to skipped revisions, the first "
697 ui.write(_("Due to skipped revisions, the first "
698 "good revision could be any of:\n"))
698 "good revision could be any of:\n"))
699 else:
699 else:
700 ui.write(_("Due to skipped revisions, the first "
700 ui.write(_("Due to skipped revisions, the first "
701 "bad revision could be any of:\n"))
701 "bad revision could be any of:\n"))
702 for n in nodes:
702 for n in nodes:
703 displayer.show(repo[n])
703 displayer.show(repo[n])
704 displayer.close()
704 displayer.close()
705
705
706 def check_state(state, interactive=True):
706 def check_state(state, interactive=True):
707 if not state['good'] or not state['bad']:
707 if not state['good'] or not state['bad']:
708 if (good or bad or skip or reset) and interactive:
708 if (good or bad or skip or reset) and interactive:
709 return
709 return
710 if not state['good']:
710 if not state['good']:
711 raise util.Abort(_('cannot bisect (no known good revisions)'))
711 raise util.Abort(_('cannot bisect (no known good revisions)'))
712 else:
712 else:
713 raise util.Abort(_('cannot bisect (no known bad revisions)'))
713 raise util.Abort(_('cannot bisect (no known bad revisions)'))
714 return True
714 return True
715
715
716 # backward compatibility
716 # backward compatibility
717 if rev in "good bad reset init".split():
717 if rev in "good bad reset init".split():
718 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
718 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
719 cmd, rev, extra = rev, extra, None
719 cmd, rev, extra = rev, extra, None
720 if cmd == "good":
720 if cmd == "good":
721 good = True
721 good = True
722 elif cmd == "bad":
722 elif cmd == "bad":
723 bad = True
723 bad = True
724 else:
724 else:
725 reset = True
725 reset = True
726 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
726 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
727 raise util.Abort(_('incompatible arguments'))
727 raise util.Abort(_('incompatible arguments'))
728
728
729 cmdutil.checkunfinished(repo)
729 cmdutil.checkunfinished(repo)
730
730
731 if reset:
731 if reset:
732 p = repo.join("bisect.state")
732 p = repo.join("bisect.state")
733 if os.path.exists(p):
733 if os.path.exists(p):
734 os.unlink(p)
734 os.unlink(p)
735 return
735 return
736
736
737 state = hbisect.load_state(repo)
737 state = hbisect.load_state(repo)
738
738
739 if command:
739 if command:
740 changesets = 1
740 changesets = 1
741 if noupdate:
741 if noupdate:
742 try:
742 try:
743 node = state['current'][0]
743 node = state['current'][0]
744 except LookupError:
744 except LookupError:
745 raise util.Abort(_('current bisect revision is unknown - '
745 raise util.Abort(_('current bisect revision is unknown - '
746 'start a new bisect to fix'))
746 'start a new bisect to fix'))
747 else:
747 else:
748 node, p2 = repo.dirstate.parents()
748 node, p2 = repo.dirstate.parents()
749 if p2 != nullid:
749 if p2 != nullid:
750 raise util.Abort(_('current bisect revision is a merge'))
750 raise util.Abort(_('current bisect revision is a merge'))
751 try:
751 try:
752 while changesets:
752 while changesets:
753 # update state
753 # update state
754 state['current'] = [node]
754 state['current'] = [node]
755 hbisect.save_state(repo, state)
755 hbisect.save_state(repo, state)
756 status = ui.system(command, environ={'HG_NODE': hex(node)})
756 status = ui.system(command, environ={'HG_NODE': hex(node)})
757 if status == 125:
757 if status == 125:
758 transition = "skip"
758 transition = "skip"
759 elif status == 0:
759 elif status == 0:
760 transition = "good"
760 transition = "good"
761 # status < 0 means process was killed
761 # status < 0 means process was killed
762 elif status == 127:
762 elif status == 127:
763 raise util.Abort(_("failed to execute %s") % command)
763 raise util.Abort(_("failed to execute %s") % command)
764 elif status < 0:
764 elif status < 0:
765 raise util.Abort(_("%s killed") % command)
765 raise util.Abort(_("%s killed") % command)
766 else:
766 else:
767 transition = "bad"
767 transition = "bad"
768 ctx = scmutil.revsingle(repo, rev, node)
768 ctx = scmutil.revsingle(repo, rev, node)
769 rev = None # clear for future iterations
769 rev = None # clear for future iterations
770 state[transition].append(ctx.node())
770 state[transition].append(ctx.node())
771 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
771 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
772 check_state(state, interactive=False)
772 check_state(state, interactive=False)
773 # bisect
773 # bisect
774 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
774 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
775 # update to next check
775 # update to next check
776 node = nodes[0]
776 node = nodes[0]
777 if not noupdate:
777 if not noupdate:
778 cmdutil.bailifchanged(repo)
778 cmdutil.bailifchanged(repo)
779 hg.clean(repo, node, show_stats=False)
779 hg.clean(repo, node, show_stats=False)
780 finally:
780 finally:
781 state['current'] = [node]
781 state['current'] = [node]
782 hbisect.save_state(repo, state)
782 hbisect.save_state(repo, state)
783 print_result(nodes, bgood)
783 print_result(nodes, bgood)
784 return
784 return
785
785
786 # update state
786 # update state
787
787
788 if rev:
788 if rev:
789 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
789 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
790 else:
790 else:
791 nodes = [repo.lookup('.')]
791 nodes = [repo.lookup('.')]
792
792
793 if good or bad or skip:
793 if good or bad or skip:
794 if good:
794 if good:
795 state['good'] += nodes
795 state['good'] += nodes
796 elif bad:
796 elif bad:
797 state['bad'] += nodes
797 state['bad'] += nodes
798 elif skip:
798 elif skip:
799 state['skip'] += nodes
799 state['skip'] += nodes
800 hbisect.save_state(repo, state)
800 hbisect.save_state(repo, state)
801
801
802 if not check_state(state):
802 if not check_state(state):
803 return
803 return
804
804
805 # actually bisect
805 # actually bisect
806 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
806 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
807 if extend:
807 if extend:
808 if not changesets:
808 if not changesets:
809 extendnode = extendbisectrange(nodes, good)
809 extendnode = extendbisectrange(nodes, good)
810 if extendnode is not None:
810 if extendnode is not None:
811 ui.write(_("Extending search to changeset %d:%s\n")
811 ui.write(_("Extending search to changeset %d:%s\n")
812 % (extendnode.rev(), extendnode))
812 % (extendnode.rev(), extendnode))
813 state['current'] = [extendnode.node()]
813 state['current'] = [extendnode.node()]
814 hbisect.save_state(repo, state)
814 hbisect.save_state(repo, state)
815 if noupdate:
815 if noupdate:
816 return
816 return
817 cmdutil.bailifchanged(repo)
817 cmdutil.bailifchanged(repo)
818 return hg.clean(repo, extendnode.node())
818 return hg.clean(repo, extendnode.node())
819 raise util.Abort(_("nothing to extend"))
819 raise util.Abort(_("nothing to extend"))
820
820
821 if changesets == 0:
821 if changesets == 0:
822 print_result(nodes, good)
822 print_result(nodes, good)
823 else:
823 else:
824 assert len(nodes) == 1 # only a single node can be tested next
824 assert len(nodes) == 1 # only a single node can be tested next
825 node = nodes[0]
825 node = nodes[0]
826 # compute the approximate number of remaining tests
826 # compute the approximate number of remaining tests
827 tests, size = 0, 2
827 tests, size = 0, 2
828 while size <= changesets:
828 while size <= changesets:
829 tests, size = tests + 1, size * 2
829 tests, size = tests + 1, size * 2
830 rev = repo.changelog.rev(node)
830 rev = repo.changelog.rev(node)
831 ui.write(_("Testing changeset %d:%s "
831 ui.write(_("Testing changeset %d:%s "
832 "(%d changesets remaining, ~%d tests)\n")
832 "(%d changesets remaining, ~%d tests)\n")
833 % (rev, short(node), changesets, tests))
833 % (rev, short(node), changesets, tests))
834 state['current'] = [node]
834 state['current'] = [node]
835 hbisect.save_state(repo, state)
835 hbisect.save_state(repo, state)
836 if not noupdate:
836 if not noupdate:
837 cmdutil.bailifchanged(repo)
837 cmdutil.bailifchanged(repo)
838 return hg.clean(repo, node)
838 return hg.clean(repo, node)
839
839
840 @command('bookmarks|bookmark',
840 @command('bookmarks|bookmark',
841 [('f', 'force', False, _('force')),
841 [('f', 'force', False, _('force')),
842 ('r', 'rev', '', _('revision'), _('REV')),
842 ('r', 'rev', '', _('revision'), _('REV')),
843 ('d', 'delete', False, _('delete a given bookmark')),
843 ('d', 'delete', False, _('delete a given bookmark')),
844 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
844 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
845 ('i', 'inactive', False, _('mark a bookmark inactive')),
845 ('i', 'inactive', False, _('mark a bookmark inactive')),
846 ] + formatteropts,
846 ] + formatteropts,
847 _('hg bookmarks [OPTIONS]... [NAME]...'))
847 _('hg bookmarks [OPTIONS]... [NAME]...'))
848 def bookmark(ui, repo, *names, **opts):
848 def bookmark(ui, repo, *names, **opts):
849 '''create a new bookmark or list existing bookmarks
849 '''create a new bookmark or list existing bookmarks
850
850
851 Bookmarks are labels on changesets to help track lines of development.
851 Bookmarks are labels on changesets to help track lines of development.
852 Bookmarks are unversioned and can be moved, renamed and deleted.
852 Bookmarks are unversioned and can be moved, renamed and deleted.
853 Deleting or moving a bookmark has no effect on the associated changesets.
853 Deleting or moving a bookmark has no effect on the associated changesets.
854
854
855 Creating or updating to a bookmark causes it to be marked as 'active'.
855 Creating or updating to a bookmark causes it to be marked as 'active'.
856 The active bookmark is indicated with a '*'.
856 The active bookmark is indicated with a '*'.
857 When a commit is made, the active bookmark will advance to the new commit.
857 When a commit is made, the active bookmark will advance to the new commit.
858 A plain :hg:`update` will also advance an active bookmark, if possible.
858 A plain :hg:`update` will also advance an active bookmark, if possible.
859 Updating away from a bookmark will cause it to be deactivated.
859 Updating away from a bookmark will cause it to be deactivated.
860
860
861 Bookmarks can be pushed and pulled between repositories (see
861 Bookmarks can be pushed and pulled between repositories (see
862 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
862 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
863 diverged, a new 'divergent bookmark' of the form 'name@path' will
863 diverged, a new 'divergent bookmark' of the form 'name@path' will
864 be created. Using :hg:`merge` will resolve the divergence.
864 be created. Using :hg:`merge` will resolve the divergence.
865
865
866 A bookmark named '@' has the special property that :hg:`clone` will
866 A bookmark named '@' has the special property that :hg:`clone` will
867 check it out by default if it exists.
867 check it out by default if it exists.
868
868
869 .. container:: verbose
869 .. container:: verbose
870
870
871 Examples:
871 Examples:
872
872
873 - create an active bookmark for a new line of development::
873 - create an active bookmark for a new line of development::
874
874
875 hg book new-feature
875 hg book new-feature
876
876
877 - create an inactive bookmark as a place marker::
877 - create an inactive bookmark as a place marker::
878
878
879 hg book -i reviewed
879 hg book -i reviewed
880
880
881 - create an inactive bookmark on another changeset::
881 - create an inactive bookmark on another changeset::
882
882
883 hg book -r .^ tested
883 hg book -r .^ tested
884
884
885 - move the '@' bookmark from another branch::
885 - move the '@' bookmark from another branch::
886
886
887 hg book -f @
887 hg book -f @
888 '''
888 '''
889 force = opts.get('force')
889 force = opts.get('force')
890 rev = opts.get('rev')
890 rev = opts.get('rev')
891 delete = opts.get('delete')
891 delete = opts.get('delete')
892 rename = opts.get('rename')
892 rename = opts.get('rename')
893 inactive = opts.get('inactive')
893 inactive = opts.get('inactive')
894
894
895 def checkformat(mark):
895 def checkformat(mark):
896 mark = mark.strip()
896 mark = mark.strip()
897 if not mark:
897 if not mark:
898 raise util.Abort(_("bookmark names cannot consist entirely of "
898 raise util.Abort(_("bookmark names cannot consist entirely of "
899 "whitespace"))
899 "whitespace"))
900 scmutil.checknewlabel(repo, mark, 'bookmark')
900 scmutil.checknewlabel(repo, mark, 'bookmark')
901 return mark
901 return mark
902
902
903 def checkconflict(repo, mark, cur, force=False, target=None):
903 def checkconflict(repo, mark, cur, force=False, target=None):
904 if mark in marks and not force:
904 if mark in marks and not force:
905 if target:
905 if target:
906 if marks[mark] == target and target == cur:
906 if marks[mark] == target and target == cur:
907 # re-activating a bookmark
907 # re-activating a bookmark
908 return
908 return
909 anc = repo.changelog.ancestors([repo[target].rev()])
909 anc = repo.changelog.ancestors([repo[target].rev()])
910 bmctx = repo[marks[mark]]
910 bmctx = repo[marks[mark]]
911 divs = [repo[b].node() for b in marks
911 divs = [repo[b].node() for b in marks
912 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
912 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
913
913
914 # allow resolving a single divergent bookmark even if moving
914 # allow resolving a single divergent bookmark even if moving
915 # the bookmark across branches when a revision is specified
915 # the bookmark across branches when a revision is specified
916 # that contains a divergent bookmark
916 # that contains a divergent bookmark
917 if bmctx.rev() not in anc and target in divs:
917 if bmctx.rev() not in anc and target in divs:
918 bookmarks.deletedivergent(repo, [target], mark)
918 bookmarks.deletedivergent(repo, [target], mark)
919 return
919 return
920
920
921 deletefrom = [b for b in divs
921 deletefrom = [b for b in divs
922 if repo[b].rev() in anc or b == target]
922 if repo[b].rev() in anc or b == target]
923 bookmarks.deletedivergent(repo, deletefrom, mark)
923 bookmarks.deletedivergent(repo, deletefrom, mark)
924 if bookmarks.validdest(repo, bmctx, repo[target]):
924 if bookmarks.validdest(repo, bmctx, repo[target]):
925 ui.status(_("moving bookmark '%s' forward from %s\n") %
925 ui.status(_("moving bookmark '%s' forward from %s\n") %
926 (mark, short(bmctx.node())))
926 (mark, short(bmctx.node())))
927 return
927 return
928 raise util.Abort(_("bookmark '%s' already exists "
928 raise util.Abort(_("bookmark '%s' already exists "
929 "(use -f to force)") % mark)
929 "(use -f to force)") % mark)
930 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
930 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
931 and not force):
931 and not force):
932 raise util.Abort(
932 raise util.Abort(
933 _("a bookmark cannot have the name of an existing branch"))
933 _("a bookmark cannot have the name of an existing branch"))
934
934
935 if delete and rename:
935 if delete and rename:
936 raise util.Abort(_("--delete and --rename are incompatible"))
936 raise util.Abort(_("--delete and --rename are incompatible"))
937 if delete and rev:
937 if delete and rev:
938 raise util.Abort(_("--rev is incompatible with --delete"))
938 raise util.Abort(_("--rev is incompatible with --delete"))
939 if rename and rev:
939 if rename and rev:
940 raise util.Abort(_("--rev is incompatible with --rename"))
940 raise util.Abort(_("--rev is incompatible with --rename"))
941 if not names and (delete or rev):
941 if not names and (delete or rev):
942 raise util.Abort(_("bookmark name required"))
942 raise util.Abort(_("bookmark name required"))
943
943
944 if delete or rename or names or inactive:
944 if delete or rename or names or inactive:
945 wlock = repo.wlock()
945 wlock = repo.wlock()
946 try:
946 try:
947 cur = repo.changectx('.').node()
947 cur = repo.changectx('.').node()
948 marks = repo._bookmarks
948 marks = repo._bookmarks
949 if delete:
949 if delete:
950 for mark in names:
950 for mark in names:
951 if mark not in marks:
951 if mark not in marks:
952 raise util.Abort(_("bookmark '%s' does not exist") %
952 raise util.Abort(_("bookmark '%s' does not exist") %
953 mark)
953 mark)
954 if mark == repo._bookmarkcurrent:
954 if mark == repo._bookmarkcurrent:
955 bookmarks.unsetcurrent(repo)
955 bookmarks.unsetcurrent(repo)
956 del marks[mark]
956 del marks[mark]
957 marks.write()
957 marks.write()
958
958
959 elif rename:
959 elif rename:
960 if not names:
960 if not names:
961 raise util.Abort(_("new bookmark name required"))
961 raise util.Abort(_("new bookmark name required"))
962 elif len(names) > 1:
962 elif len(names) > 1:
963 raise util.Abort(_("only one new bookmark name allowed"))
963 raise util.Abort(_("only one new bookmark name allowed"))
964 mark = checkformat(names[0])
964 mark = checkformat(names[0])
965 if rename not in marks:
965 if rename not in marks:
966 raise util.Abort(_("bookmark '%s' does not exist") % rename)
966 raise util.Abort(_("bookmark '%s' does not exist") % rename)
967 checkconflict(repo, mark, cur, force)
967 checkconflict(repo, mark, cur, force)
968 marks[mark] = marks[rename]
968 marks[mark] = marks[rename]
969 if repo._bookmarkcurrent == rename and not inactive:
969 if repo._bookmarkcurrent == rename and not inactive:
970 bookmarks.setcurrent(repo, mark)
970 bookmarks.setcurrent(repo, mark)
971 del marks[rename]
971 del marks[rename]
972 marks.write()
972 marks.write()
973
973
974 elif names:
974 elif names:
975 newact = None
975 newact = None
976 for mark in names:
976 for mark in names:
977 mark = checkformat(mark)
977 mark = checkformat(mark)
978 if newact is None:
978 if newact is None:
979 newact = mark
979 newact = mark
980 if inactive and mark == repo._bookmarkcurrent:
980 if inactive and mark == repo._bookmarkcurrent:
981 bookmarks.unsetcurrent(repo)
981 bookmarks.unsetcurrent(repo)
982 return
982 return
983 tgt = cur
983 tgt = cur
984 if rev:
984 if rev:
985 tgt = scmutil.revsingle(repo, rev).node()
985 tgt = scmutil.revsingle(repo, rev).node()
986 checkconflict(repo, mark, cur, force, tgt)
986 checkconflict(repo, mark, cur, force, tgt)
987 marks[mark] = tgt
987 marks[mark] = tgt
988 if not inactive and cur == marks[newact] and not rev:
988 if not inactive and cur == marks[newact] and not rev:
989 bookmarks.setcurrent(repo, newact)
989 bookmarks.setcurrent(repo, newact)
990 elif cur != tgt and newact == repo._bookmarkcurrent:
990 elif cur != tgt and newact == repo._bookmarkcurrent:
991 bookmarks.unsetcurrent(repo)
991 bookmarks.unsetcurrent(repo)
992 marks.write()
992 marks.write()
993
993
994 elif inactive:
994 elif inactive:
995 if len(marks) == 0:
995 if len(marks) == 0:
996 ui.status(_("no bookmarks set\n"))
996 ui.status(_("no bookmarks set\n"))
997 elif not repo._bookmarkcurrent:
997 elif not repo._bookmarkcurrent:
998 ui.status(_("no active bookmark\n"))
998 ui.status(_("no active bookmark\n"))
999 else:
999 else:
1000 bookmarks.unsetcurrent(repo)
1000 bookmarks.unsetcurrent(repo)
1001 finally:
1001 finally:
1002 wlock.release()
1002 wlock.release()
1003 else: # show bookmarks
1003 else: # show bookmarks
1004 fm = ui.formatter('bookmarks', opts)
1004 fm = ui.formatter('bookmarks', opts)
1005 hexfn = fm.hexfunc
1005 hexfn = fm.hexfunc
1006 marks = repo._bookmarks
1006 marks = repo._bookmarks
1007 if len(marks) == 0 and not fm:
1007 if len(marks) == 0 and not fm:
1008 ui.status(_("no bookmarks set\n"))
1008 ui.status(_("no bookmarks set\n"))
1009 for bmark, n in sorted(marks.iteritems()):
1009 for bmark, n in sorted(marks.iteritems()):
1010 current = repo._bookmarkcurrent
1010 current = repo._bookmarkcurrent
1011 if bmark == current:
1011 if bmark == current:
1012 prefix, label = '*', 'bookmarks.current'
1012 prefix, label = '*', 'bookmarks.current'
1013 else:
1013 else:
1014 prefix, label = ' ', ''
1014 prefix, label = ' ', ''
1015
1015
1016 fm.startitem()
1016 fm.startitem()
1017 if not ui.quiet:
1017 if not ui.quiet:
1018 fm.plain(' %s ' % prefix, label=label)
1018 fm.plain(' %s ' % prefix, label=label)
1019 fm.write('bookmark', '%s', bmark, label=label)
1019 fm.write('bookmark', '%s', bmark, label=label)
1020 pad = " " * (25 - encoding.colwidth(bmark))
1020 pad = " " * (25 - encoding.colwidth(bmark))
1021 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1021 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1022 repo.changelog.rev(n), hexfn(n), label=label)
1022 repo.changelog.rev(n), hexfn(n), label=label)
1023 fm.data(active=(bmark == current))
1023 fm.data(active=(bmark == current))
1024 fm.plain('\n')
1024 fm.plain('\n')
1025 fm.end()
1025 fm.end()
1026
1026
1027 @command('branch',
1027 @command('branch',
1028 [('f', 'force', None,
1028 [('f', 'force', None,
1029 _('set branch name even if it shadows an existing branch')),
1029 _('set branch name even if it shadows an existing branch')),
1030 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1030 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1031 _('[-fC] [NAME]'))
1031 _('[-fC] [NAME]'))
1032 def branch(ui, repo, label=None, **opts):
1032 def branch(ui, repo, label=None, **opts):
1033 """set or show the current branch name
1033 """set or show the current branch name
1034
1034
1035 .. note::
1035 .. note::
1036
1036
1037 Branch names are permanent and global. Use :hg:`bookmark` to create a
1037 Branch names are permanent and global. Use :hg:`bookmark` to create a
1038 light-weight bookmark instead. See :hg:`help glossary` for more
1038 light-weight bookmark instead. See :hg:`help glossary` for more
1039 information about named branches and bookmarks.
1039 information about named branches and bookmarks.
1040
1040
1041 With no argument, show the current branch name. With one argument,
1041 With no argument, show the current branch name. With one argument,
1042 set the working directory branch name (the branch will not exist
1042 set the working directory branch name (the branch will not exist
1043 in the repository until the next commit). Standard practice
1043 in the repository until the next commit). Standard practice
1044 recommends that primary development take place on the 'default'
1044 recommends that primary development take place on the 'default'
1045 branch.
1045 branch.
1046
1046
1047 Unless -f/--force is specified, branch will not let you set a
1047 Unless -f/--force is specified, branch will not let you set a
1048 branch name that already exists.
1048 branch name that already exists.
1049
1049
1050 Use -C/--clean to reset the working directory branch to that of
1050 Use -C/--clean to reset the working directory branch to that of
1051 the parent of the working directory, negating a previous branch
1051 the parent of the working directory, negating a previous branch
1052 change.
1052 change.
1053
1053
1054 Use the command :hg:`update` to switch to an existing branch. Use
1054 Use the command :hg:`update` to switch to an existing branch. Use
1055 :hg:`commit --close-branch` to mark this branch as closed.
1055 :hg:`commit --close-branch` to mark this branch as closed.
1056
1056
1057 Returns 0 on success.
1057 Returns 0 on success.
1058 """
1058 """
1059 if label:
1059 if label:
1060 label = label.strip()
1060 label = label.strip()
1061
1061
1062 if not opts.get('clean') and not label:
1062 if not opts.get('clean') and not label:
1063 ui.write("%s\n" % repo.dirstate.branch())
1063 ui.write("%s\n" % repo.dirstate.branch())
1064 return
1064 return
1065
1065
1066 wlock = repo.wlock()
1066 wlock = repo.wlock()
1067 try:
1067 try:
1068 if opts.get('clean'):
1068 if opts.get('clean'):
1069 label = repo[None].p1().branch()
1069 label = repo[None].p1().branch()
1070 repo.dirstate.setbranch(label)
1070 repo.dirstate.setbranch(label)
1071 ui.status(_('reset working directory to branch %s\n') % label)
1071 ui.status(_('reset working directory to branch %s\n') % label)
1072 elif label:
1072 elif label:
1073 if not opts.get('force') and label in repo.branchmap():
1073 if not opts.get('force') and label in repo.branchmap():
1074 if label not in [p.branch() for p in repo.parents()]:
1074 if label not in [p.branch() for p in repo.parents()]:
1075 raise util.Abort(_('a branch of the same name already'
1075 raise util.Abort(_('a branch of the same name already'
1076 ' exists'),
1076 ' exists'),
1077 # i18n: "it" refers to an existing branch
1077 # i18n: "it" refers to an existing branch
1078 hint=_("use 'hg update' to switch to it"))
1078 hint=_("use 'hg update' to switch to it"))
1079 scmutil.checknewlabel(repo, label, 'branch')
1079 scmutil.checknewlabel(repo, label, 'branch')
1080 repo.dirstate.setbranch(label)
1080 repo.dirstate.setbranch(label)
1081 ui.status(_('marked working directory as branch %s\n') % label)
1081 ui.status(_('marked working directory as branch %s\n') % label)
1082 ui.status(_('(branches are permanent and global, '
1082 ui.status(_('(branches are permanent and global, '
1083 'did you want a bookmark?)\n'))
1083 'did you want a bookmark?)\n'))
1084 finally:
1084 finally:
1085 wlock.release()
1085 wlock.release()
1086
1086
1087 @command('branches',
1087 @command('branches',
1088 [('a', 'active', False,
1088 [('a', 'active', False,
1089 _('show only branches that have unmerged heads (DEPRECATED)')),
1089 _('show only branches that have unmerged heads (DEPRECATED)')),
1090 ('c', 'closed', False, _('show normal and closed branches')),
1090 ('c', 'closed', False, _('show normal and closed branches')),
1091 ] + formatteropts,
1091 ] + formatteropts,
1092 _('[-ac]'))
1092 _('[-ac]'))
1093 def branches(ui, repo, active=False, closed=False, **opts):
1093 def branches(ui, repo, active=False, closed=False, **opts):
1094 """list repository named branches
1094 """list repository named branches
1095
1095
1096 List the repository's named branches, indicating which ones are
1096 List the repository's named branches, indicating which ones are
1097 inactive. If -c/--closed is specified, also list branches which have
1097 inactive. If -c/--closed is specified, also list branches which have
1098 been marked closed (see :hg:`commit --close-branch`).
1098 been marked closed (see :hg:`commit --close-branch`).
1099
1099
1100 Use the command :hg:`update` to switch to an existing branch.
1100 Use the command :hg:`update` to switch to an existing branch.
1101
1101
1102 Returns 0.
1102 Returns 0.
1103 """
1103 """
1104
1104
1105 fm = ui.formatter('branches', opts)
1105 fm = ui.formatter('branches', opts)
1106 hexfunc = fm.hexfunc
1106 hexfunc = fm.hexfunc
1107
1107
1108 allheads = set(repo.heads())
1108 allheads = set(repo.heads())
1109 branches = []
1109 branches = []
1110 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1110 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1111 isactive = not isclosed and bool(set(heads) & allheads)
1111 isactive = not isclosed and bool(set(heads) & allheads)
1112 branches.append((tag, repo[tip], isactive, not isclosed))
1112 branches.append((tag, repo[tip], isactive, not isclosed))
1113 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1113 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1114 reverse=True)
1114 reverse=True)
1115
1115
1116 for tag, ctx, isactive, isopen in branches:
1116 for tag, ctx, isactive, isopen in branches:
1117 if active and not isactive:
1117 if active and not isactive:
1118 continue
1118 continue
1119 if isactive:
1119 if isactive:
1120 label = 'branches.active'
1120 label = 'branches.active'
1121 notice = ''
1121 notice = ''
1122 elif not isopen:
1122 elif not isopen:
1123 if not closed:
1123 if not closed:
1124 continue
1124 continue
1125 label = 'branches.closed'
1125 label = 'branches.closed'
1126 notice = _(' (closed)')
1126 notice = _(' (closed)')
1127 else:
1127 else:
1128 label = 'branches.inactive'
1128 label = 'branches.inactive'
1129 notice = _(' (inactive)')
1129 notice = _(' (inactive)')
1130 current = (tag == repo.dirstate.branch())
1130 current = (tag == repo.dirstate.branch())
1131 if current:
1131 if current:
1132 label = 'branches.current'
1132 label = 'branches.current'
1133
1133
1134 fm.startitem()
1134 fm.startitem()
1135 fm.write('branch', '%s', tag, label=label)
1135 fm.write('branch', '%s', tag, label=label)
1136 rev = ctx.rev()
1136 rev = ctx.rev()
1137 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1137 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1138 fmt = ' ' * padsize + ' %d:%s'
1138 fmt = ' ' * padsize + ' %d:%s'
1139 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1139 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1140 label='log.changeset changeset.%s' % ctx.phasestr())
1140 label='log.changeset changeset.%s' % ctx.phasestr())
1141 fm.data(active=isactive, closed=not isopen, current=current)
1141 fm.data(active=isactive, closed=not isopen, current=current)
1142 if not ui.quiet:
1142 if not ui.quiet:
1143 fm.plain(notice)
1143 fm.plain(notice)
1144 fm.plain('\n')
1144 fm.plain('\n')
1145 fm.end()
1145 fm.end()
1146
1146
1147 @command('bundle',
1147 @command('bundle',
1148 [('f', 'force', None, _('run even when the destination is unrelated')),
1148 [('f', 'force', None, _('run even when the destination is unrelated')),
1149 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1149 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1150 _('REV')),
1150 _('REV')),
1151 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1151 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1152 _('BRANCH')),
1152 _('BRANCH')),
1153 ('', 'base', [],
1153 ('', 'base', [],
1154 _('a base changeset assumed to be available at the destination'),
1154 _('a base changeset assumed to be available at the destination'),
1155 _('REV')),
1155 _('REV')),
1156 ('a', 'all', None, _('bundle all changesets in the repository')),
1156 ('a', 'all', None, _('bundle all changesets in the repository')),
1157 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1157 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1158 ] + remoteopts,
1158 ] + remoteopts,
1159 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1159 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1160 def bundle(ui, repo, fname, dest=None, **opts):
1160 def bundle(ui, repo, fname, dest=None, **opts):
1161 """create a changegroup file
1161 """create a changegroup file
1162
1162
1163 Generate a compressed changegroup file collecting changesets not
1163 Generate a compressed changegroup file collecting changesets not
1164 known to be in another repository.
1164 known to be in another repository.
1165
1165
1166 If you omit the destination repository, then hg assumes the
1166 If you omit the destination repository, then hg assumes the
1167 destination will have all the nodes you specify with --base
1167 destination will have all the nodes you specify with --base
1168 parameters. To create a bundle containing all changesets, use
1168 parameters. To create a bundle containing all changesets, use
1169 -a/--all (or --base null).
1169 -a/--all (or --base null).
1170
1170
1171 You can change compression method with the -t/--type option.
1171 You can change compression method with the -t/--type option.
1172 The available compression methods are: none, bzip2, and
1172 The available compression methods are: none, bzip2, and
1173 gzip (by default, bundles are compressed using bzip2).
1173 gzip (by default, bundles are compressed using bzip2).
1174
1174
1175 The bundle file can then be transferred using conventional means
1175 The bundle file can then be transferred using conventional means
1176 and applied to another repository with the unbundle or pull
1176 and applied to another repository with the unbundle or pull
1177 command. This is useful when direct push and pull are not
1177 command. This is useful when direct push and pull are not
1178 available or when exporting an entire repository is undesirable.
1178 available or when exporting an entire repository is undesirable.
1179
1179
1180 Applying bundles preserves all changeset contents including
1180 Applying bundles preserves all changeset contents including
1181 permissions, copy/rename information, and revision history.
1181 permissions, copy/rename information, and revision history.
1182
1182
1183 Returns 0 on success, 1 if no changes found.
1183 Returns 0 on success, 1 if no changes found.
1184 """
1184 """
1185 revs = None
1185 revs = None
1186 if 'rev' in opts:
1186 if 'rev' in opts:
1187 revs = scmutil.revrange(repo, opts['rev'])
1187 revs = scmutil.revrange(repo, opts['rev'])
1188
1188
1189 bundletype = opts.get('type', 'bzip2').lower()
1189 bundletype = opts.get('type', 'bzip2').lower()
1190 btypes = {'none': 'HG10UN',
1190 btypes = {'none': 'HG10UN',
1191 'bzip2': 'HG10BZ',
1191 'bzip2': 'HG10BZ',
1192 'gzip': 'HG10GZ',
1192 'gzip': 'HG10GZ',
1193 'bundle2': 'HG2Y'}
1193 'bundle2': 'HG2Y'}
1194 bundletype = btypes.get(bundletype)
1194 bundletype = btypes.get(bundletype)
1195 if bundletype not in changegroup.bundletypes:
1195 if bundletype not in changegroup.bundletypes:
1196 raise util.Abort(_('unknown bundle type specified with --type'))
1196 raise util.Abort(_('unknown bundle type specified with --type'))
1197
1197
1198 if opts.get('all'):
1198 if opts.get('all'):
1199 base = ['null']
1199 base = ['null']
1200 else:
1200 else:
1201 base = scmutil.revrange(repo, opts.get('base'))
1201 base = scmutil.revrange(repo, opts.get('base'))
1202 # TODO: get desired bundlecaps from command line.
1202 # TODO: get desired bundlecaps from command line.
1203 bundlecaps = None
1203 bundlecaps = None
1204 if base:
1204 if base:
1205 if dest:
1205 if dest:
1206 raise util.Abort(_("--base is incompatible with specifying "
1206 raise util.Abort(_("--base is incompatible with specifying "
1207 "a destination"))
1207 "a destination"))
1208 common = [repo.lookup(rev) for rev in base]
1208 common = [repo.lookup(rev) for rev in base]
1209 heads = revs and map(repo.lookup, revs) or revs
1209 heads = revs and map(repo.lookup, revs) or revs
1210 cg = changegroup.getchangegroup(repo, 'bundle', heads=heads,
1210 cg = changegroup.getchangegroup(repo, 'bundle', heads=heads,
1211 common=common, bundlecaps=bundlecaps)
1211 common=common, bundlecaps=bundlecaps)
1212 outgoing = None
1212 outgoing = None
1213 else:
1213 else:
1214 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1214 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1215 dest, branches = hg.parseurl(dest, opts.get('branch'))
1215 dest, branches = hg.parseurl(dest, opts.get('branch'))
1216 other = hg.peer(repo, opts, dest)
1216 other = hg.peer(repo, opts, dest)
1217 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1217 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1218 heads = revs and map(repo.lookup, revs) or revs
1218 heads = revs and map(repo.lookup, revs) or revs
1219 outgoing = discovery.findcommonoutgoing(repo, other,
1219 outgoing = discovery.findcommonoutgoing(repo, other,
1220 onlyheads=heads,
1220 onlyheads=heads,
1221 force=opts.get('force'),
1221 force=opts.get('force'),
1222 portable=True)
1222 portable=True)
1223 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1223 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1224 bundlecaps)
1224 bundlecaps)
1225 if not cg:
1225 if not cg:
1226 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1226 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1227 return 1
1227 return 1
1228
1228
1229 changegroup.writebundle(ui, cg, fname, bundletype)
1229 changegroup.writebundle(ui, cg, fname, bundletype)
1230
1230
1231 @command('cat',
1231 @command('cat',
1232 [('o', 'output', '',
1232 [('o', 'output', '',
1233 _('print output to file with formatted name'), _('FORMAT')),
1233 _('print output to file with formatted name'), _('FORMAT')),
1234 ('r', 'rev', '', _('print the given revision'), _('REV')),
1234 ('r', 'rev', '', _('print the given revision'), _('REV')),
1235 ('', 'decode', None, _('apply any matching decode filter')),
1235 ('', 'decode', None, _('apply any matching decode filter')),
1236 ] + walkopts,
1236 ] + walkopts,
1237 _('[OPTION]... FILE...'),
1237 _('[OPTION]... FILE...'),
1238 inferrepo=True)
1238 inferrepo=True)
1239 def cat(ui, repo, file1, *pats, **opts):
1239 def cat(ui, repo, file1, *pats, **opts):
1240 """output the current or given revision of files
1240 """output the current or given revision of files
1241
1241
1242 Print the specified files as they were at the given revision. If
1242 Print the specified files as they were at the given revision. If
1243 no revision is given, the parent of the working directory is used.
1243 no revision is given, the parent of the working directory is used.
1244
1244
1245 Output may be to a file, in which case the name of the file is
1245 Output may be to a file, in which case the name of the file is
1246 given using a format string. The formatting rules as follows:
1246 given using a format string. The formatting rules as follows:
1247
1247
1248 :``%%``: literal "%" character
1248 :``%%``: literal "%" character
1249 :``%s``: basename of file being printed
1249 :``%s``: basename of file being printed
1250 :``%d``: dirname of file being printed, or '.' if in repository root
1250 :``%d``: dirname of file being printed, or '.' if in repository root
1251 :``%p``: root-relative path name of file being printed
1251 :``%p``: root-relative path name of file being printed
1252 :``%H``: changeset hash (40 hexadecimal digits)
1252 :``%H``: changeset hash (40 hexadecimal digits)
1253 :``%R``: changeset revision number
1253 :``%R``: changeset revision number
1254 :``%h``: short-form changeset hash (12 hexadecimal digits)
1254 :``%h``: short-form changeset hash (12 hexadecimal digits)
1255 :``%r``: zero-padded changeset revision number
1255 :``%r``: zero-padded changeset revision number
1256 :``%b``: basename of the exporting repository
1256 :``%b``: basename of the exporting repository
1257
1257
1258 Returns 0 on success.
1258 Returns 0 on success.
1259 """
1259 """
1260 ctx = scmutil.revsingle(repo, opts.get('rev'))
1260 ctx = scmutil.revsingle(repo, opts.get('rev'))
1261 m = scmutil.match(ctx, (file1,) + pats, opts)
1261 m = scmutil.match(ctx, (file1,) + pats, opts)
1262
1262
1263 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1263 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1264
1264
1265 @command('^clone',
1265 @command('^clone',
1266 [('U', 'noupdate', None, _('the clone will include an empty working '
1266 [('U', 'noupdate', None, _('the clone will include an empty working '
1267 'directory (only a repository)')),
1267 'directory (only a repository)')),
1268 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1268 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1269 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1269 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1270 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1270 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1271 ('', 'pull', None, _('use pull protocol to copy metadata')),
1271 ('', 'pull', None, _('use pull protocol to copy metadata')),
1272 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1272 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1273 ] + remoteopts,
1273 ] + remoteopts,
1274 _('[OPTION]... SOURCE [DEST]'),
1274 _('[OPTION]... SOURCE [DEST]'),
1275 norepo=True)
1275 norepo=True)
1276 def clone(ui, source, dest=None, **opts):
1276 def clone(ui, source, dest=None, **opts):
1277 """make a copy of an existing repository
1277 """make a copy of an existing repository
1278
1278
1279 Create a copy of an existing repository in a new directory.
1279 Create a copy of an existing repository in a new directory.
1280
1280
1281 If no destination directory name is specified, it defaults to the
1281 If no destination directory name is specified, it defaults to the
1282 basename of the source.
1282 basename of the source.
1283
1283
1284 The location of the source is added to the new repository's
1284 The location of the source is added to the new repository's
1285 ``.hg/hgrc`` file, as the default to be used for future pulls.
1285 ``.hg/hgrc`` file, as the default to be used for future pulls.
1286
1286
1287 Only local paths and ``ssh://`` URLs are supported as
1287 Only local paths and ``ssh://`` URLs are supported as
1288 destinations. For ``ssh://`` destinations, no working directory or
1288 destinations. For ``ssh://`` destinations, no working directory or
1289 ``.hg/hgrc`` will be created on the remote side.
1289 ``.hg/hgrc`` will be created on the remote side.
1290
1290
1291 To pull only a subset of changesets, specify one or more revisions
1291 To pull only a subset of changesets, specify one or more revisions
1292 identifiers with -r/--rev or branches with -b/--branch. The
1292 identifiers with -r/--rev or branches with -b/--branch. The
1293 resulting clone will contain only the specified changesets and
1293 resulting clone will contain only the specified changesets and
1294 their ancestors. These options (or 'clone src#rev dest') imply
1294 their ancestors. These options (or 'clone src#rev dest') imply
1295 --pull, even for local source repositories. Note that specifying a
1295 --pull, even for local source repositories. Note that specifying a
1296 tag will include the tagged changeset but not the changeset
1296 tag will include the tagged changeset but not the changeset
1297 containing the tag.
1297 containing the tag.
1298
1298
1299 If the source repository has a bookmark called '@' set, that
1299 If the source repository has a bookmark called '@' set, that
1300 revision will be checked out in the new repository by default.
1300 revision will be checked out in the new repository by default.
1301
1301
1302 To check out a particular version, use -u/--update, or
1302 To check out a particular version, use -u/--update, or
1303 -U/--noupdate to create a clone with no working directory.
1303 -U/--noupdate to create a clone with no working directory.
1304
1304
1305 .. container:: verbose
1305 .. container:: verbose
1306
1306
1307 For efficiency, hardlinks are used for cloning whenever the
1307 For efficiency, hardlinks are used for cloning whenever the
1308 source and destination are on the same filesystem (note this
1308 source and destination are on the same filesystem (note this
1309 applies only to the repository data, not to the working
1309 applies only to the repository data, not to the working
1310 directory). Some filesystems, such as AFS, implement hardlinking
1310 directory). Some filesystems, such as AFS, implement hardlinking
1311 incorrectly, but do not report errors. In these cases, use the
1311 incorrectly, but do not report errors. In these cases, use the
1312 --pull option to avoid hardlinking.
1312 --pull option to avoid hardlinking.
1313
1313
1314 In some cases, you can clone repositories and the working
1314 In some cases, you can clone repositories and the working
1315 directory using full hardlinks with ::
1315 directory using full hardlinks with ::
1316
1316
1317 $ cp -al REPO REPOCLONE
1317 $ cp -al REPO REPOCLONE
1318
1318
1319 This is the fastest way to clone, but it is not always safe. The
1319 This is the fastest way to clone, but it is not always safe. The
1320 operation is not atomic (making sure REPO is not modified during
1320 operation is not atomic (making sure REPO is not modified during
1321 the operation is up to you) and you have to make sure your
1321 the operation is up to you) and you have to make sure your
1322 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1322 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1323 so). Also, this is not compatible with certain extensions that
1323 so). Also, this is not compatible with certain extensions that
1324 place their metadata under the .hg directory, such as mq.
1324 place their metadata under the .hg directory, such as mq.
1325
1325
1326 Mercurial will update the working directory to the first applicable
1326 Mercurial will update the working directory to the first applicable
1327 revision from this list:
1327 revision from this list:
1328
1328
1329 a) null if -U or the source repository has no changesets
1329 a) null if -U or the source repository has no changesets
1330 b) if -u . and the source repository is local, the first parent of
1330 b) if -u . and the source repository is local, the first parent of
1331 the source repository's working directory
1331 the source repository's working directory
1332 c) the changeset specified with -u (if a branch name, this means the
1332 c) the changeset specified with -u (if a branch name, this means the
1333 latest head of that branch)
1333 latest head of that branch)
1334 d) the changeset specified with -r
1334 d) the changeset specified with -r
1335 e) the tipmost head specified with -b
1335 e) the tipmost head specified with -b
1336 f) the tipmost head specified with the url#branch source syntax
1336 f) the tipmost head specified with the url#branch source syntax
1337 g) the revision marked with the '@' bookmark, if present
1337 g) the revision marked with the '@' bookmark, if present
1338 h) the tipmost head of the default branch
1338 h) the tipmost head of the default branch
1339 i) tip
1339 i) tip
1340
1340
1341 Examples:
1341 Examples:
1342
1342
1343 - clone a remote repository to a new directory named hg/::
1343 - clone a remote repository to a new directory named hg/::
1344
1344
1345 hg clone http://selenic.com/hg
1345 hg clone http://selenic.com/hg
1346
1346
1347 - create a lightweight local clone::
1347 - create a lightweight local clone::
1348
1348
1349 hg clone project/ project-feature/
1349 hg clone project/ project-feature/
1350
1350
1351 - clone from an absolute path on an ssh server (note double-slash)::
1351 - clone from an absolute path on an ssh server (note double-slash)::
1352
1352
1353 hg clone ssh://user@server//home/projects/alpha/
1353 hg clone ssh://user@server//home/projects/alpha/
1354
1354
1355 - do a high-speed clone over a LAN while checking out a
1355 - do a high-speed clone over a LAN while checking out a
1356 specified version::
1356 specified version::
1357
1357
1358 hg clone --uncompressed http://server/repo -u 1.5
1358 hg clone --uncompressed http://server/repo -u 1.5
1359
1359
1360 - create a repository without changesets after a particular revision::
1360 - create a repository without changesets after a particular revision::
1361
1361
1362 hg clone -r 04e544 experimental/ good/
1362 hg clone -r 04e544 experimental/ good/
1363
1363
1364 - clone (and track) a particular named branch::
1364 - clone (and track) a particular named branch::
1365
1365
1366 hg clone http://selenic.com/hg#stable
1366 hg clone http://selenic.com/hg#stable
1367
1367
1368 See :hg:`help urls` for details on specifying URLs.
1368 See :hg:`help urls` for details on specifying URLs.
1369
1369
1370 Returns 0 on success.
1370 Returns 0 on success.
1371 """
1371 """
1372 if opts.get('noupdate') and opts.get('updaterev'):
1372 if opts.get('noupdate') and opts.get('updaterev'):
1373 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1373 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1374
1374
1375 r = hg.clone(ui, opts, source, dest,
1375 r = hg.clone(ui, opts, source, dest,
1376 pull=opts.get('pull'),
1376 pull=opts.get('pull'),
1377 stream=opts.get('uncompressed'),
1377 stream=opts.get('uncompressed'),
1378 rev=opts.get('rev'),
1378 rev=opts.get('rev'),
1379 update=opts.get('updaterev') or not opts.get('noupdate'),
1379 update=opts.get('updaterev') or not opts.get('noupdate'),
1380 branch=opts.get('branch'))
1380 branch=opts.get('branch'))
1381
1381
1382 return r is None
1382 return r is None
1383
1383
1384 @command('^commit|ci',
1384 @command('^commit|ci',
1385 [('A', 'addremove', None,
1385 [('A', 'addremove', None,
1386 _('mark new/missing files as added/removed before committing')),
1386 _('mark new/missing files as added/removed before committing')),
1387 ('', 'close-branch', None,
1387 ('', 'close-branch', None,
1388 _('mark a branch as closed, hiding it from the branch list')),
1388 _('mark a branch as closed, hiding it from the branch list')),
1389 ('', 'amend', None, _('amend the parent of the working directory')),
1389 ('', 'amend', None, _('amend the parent of the working directory')),
1390 ('s', 'secret', None, _('use the secret phase for committing')),
1390 ('s', 'secret', None, _('use the secret phase for committing')),
1391 ('e', 'edit', None, _('invoke editor on commit messages')),
1391 ('e', 'edit', None, _('invoke editor on commit messages')),
1392 ('i', 'interactive', None, _('use interactive mode')),
1392 ('i', 'interactive', None, _('use interactive mode')),
1393 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1393 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1394 _('[OPTION]... [FILE]...'),
1394 _('[OPTION]... [FILE]...'),
1395 inferrepo=True)
1395 inferrepo=True)
1396 def commit(ui, repo, *pats, **opts):
1396 def commit(ui, repo, *pats, **opts):
1397 """commit the specified files or all outstanding changes
1397 """commit the specified files or all outstanding changes
1398
1398
1399 Commit changes to the given files into the repository. Unlike a
1399 Commit changes to the given files into the repository. Unlike a
1400 centralized SCM, this operation is a local operation. See
1400 centralized SCM, this operation is a local operation. See
1401 :hg:`push` for a way to actively distribute your changes.
1401 :hg:`push` for a way to actively distribute your changes.
1402
1402
1403 If a list of files is omitted, all changes reported by :hg:`status`
1403 If a list of files is omitted, all changes reported by :hg:`status`
1404 will be committed.
1404 will be committed.
1405
1405
1406 If you are committing the result of a merge, do not provide any
1406 If you are committing the result of a merge, do not provide any
1407 filenames or -I/-X filters.
1407 filenames or -I/-X filters.
1408
1408
1409 If no commit message is specified, Mercurial starts your
1409 If no commit message is specified, Mercurial starts your
1410 configured editor where you can enter a message. In case your
1410 configured editor where you can enter a message. In case your
1411 commit fails, you will find a backup of your message in
1411 commit fails, you will find a backup of your message in
1412 ``.hg/last-message.txt``.
1412 ``.hg/last-message.txt``.
1413
1413
1414 The --amend flag can be used to amend the parent of the
1414 The --amend flag can be used to amend the parent of the
1415 working directory with a new commit that contains the changes
1415 working directory with a new commit that contains the changes
1416 in the parent in addition to those currently reported by :hg:`status`,
1416 in the parent in addition to those currently reported by :hg:`status`,
1417 if there are any. The old commit is stored in a backup bundle in
1417 if there are any. The old commit is stored in a backup bundle in
1418 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1418 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1419 on how to restore it).
1419 on how to restore it).
1420
1420
1421 Message, user and date are taken from the amended commit unless
1421 Message, user and date are taken from the amended commit unless
1422 specified. When a message isn't specified on the command line,
1422 specified. When a message isn't specified on the command line,
1423 the editor will open with the message of the amended commit.
1423 the editor will open with the message of the amended commit.
1424
1424
1425 It is not possible to amend public changesets (see :hg:`help phases`)
1425 It is not possible to amend public changesets (see :hg:`help phases`)
1426 or changesets that have children.
1426 or changesets that have children.
1427
1427
1428 See :hg:`help dates` for a list of formats valid for -d/--date.
1428 See :hg:`help dates` for a list of formats valid for -d/--date.
1429
1429
1430 Returns 0 on success, 1 if nothing changed.
1430 Returns 0 on success, 1 if nothing changed.
1431 """
1431 """
1432 if opts.get('interactive'):
1432 if opts.get('interactive'):
1433 opts.pop('interactive')
1433 opts.pop('interactive')
1434 cmdutil.dorecord(ui, repo, commit, 'commit', False,
1434 cmdutil.dorecord(ui, repo, commit, 'commit', False,
1435 cmdutil.recordfilter, *pats, **opts)
1435 cmdutil.recordfilter, *pats, **opts)
1436 return
1436 return
1437
1437
1438 if opts.get('subrepos'):
1438 if opts.get('subrepos'):
1439 if opts.get('amend'):
1439 if opts.get('amend'):
1440 raise util.Abort(_('cannot amend with --subrepos'))
1440 raise util.Abort(_('cannot amend with --subrepos'))
1441 # Let --subrepos on the command line override config setting.
1441 # Let --subrepos on the command line override config setting.
1442 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1442 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1443
1443
1444 cmdutil.checkunfinished(repo, commit=True)
1444 cmdutil.checkunfinished(repo, commit=True)
1445
1445
1446 branch = repo[None].branch()
1446 branch = repo[None].branch()
1447 bheads = repo.branchheads(branch)
1447 bheads = repo.branchheads(branch)
1448
1448
1449 extra = {}
1449 extra = {}
1450 if opts.get('close_branch'):
1450 if opts.get('close_branch'):
1451 extra['close'] = 1
1451 extra['close'] = 1
1452
1452
1453 if not bheads:
1453 if not bheads:
1454 raise util.Abort(_('can only close branch heads'))
1454 raise util.Abort(_('can only close branch heads'))
1455 elif opts.get('amend'):
1455 elif opts.get('amend'):
1456 if repo.parents()[0].p1().branch() != branch and \
1456 if repo.parents()[0].p1().branch() != branch and \
1457 repo.parents()[0].p2().branch() != branch:
1457 repo.parents()[0].p2().branch() != branch:
1458 raise util.Abort(_('can only close branch heads'))
1458 raise util.Abort(_('can only close branch heads'))
1459
1459
1460 if opts.get('amend'):
1460 if opts.get('amend'):
1461 if ui.configbool('ui', 'commitsubrepos'):
1461 if ui.configbool('ui', 'commitsubrepos'):
1462 raise util.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1462 raise util.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1463
1463
1464 old = repo['.']
1464 old = repo['.']
1465 if not old.mutable():
1465 if not old.mutable():
1466 raise util.Abort(_('cannot amend public changesets'))
1466 raise util.Abort(_('cannot amend public changesets'))
1467 if len(repo[None].parents()) > 1:
1467 if len(repo[None].parents()) > 1:
1468 raise util.Abort(_('cannot amend while merging'))
1468 raise util.Abort(_('cannot amend while merging'))
1469 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1469 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1470 if not allowunstable and old.children():
1470 if not allowunstable and old.children():
1471 raise util.Abort(_('cannot amend changeset with children'))
1471 raise util.Abort(_('cannot amend changeset with children'))
1472
1472
1473 # commitfunc is used only for temporary amend commit by cmdutil.amend
1473 # commitfunc is used only for temporary amend commit by cmdutil.amend
1474 def commitfunc(ui, repo, message, match, opts):
1474 def commitfunc(ui, repo, message, match, opts):
1475 return repo.commit(message,
1475 return repo.commit(message,
1476 opts.get('user') or old.user(),
1476 opts.get('user') or old.user(),
1477 opts.get('date') or old.date(),
1477 opts.get('date') or old.date(),
1478 match,
1478 match,
1479 extra=extra)
1479 extra=extra)
1480
1480
1481 current = repo._bookmarkcurrent
1481 current = repo._bookmarkcurrent
1482 marks = old.bookmarks()
1482 marks = old.bookmarks()
1483 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1483 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1484 if node == old.node():
1484 if node == old.node():
1485 ui.status(_("nothing changed\n"))
1485 ui.status(_("nothing changed\n"))
1486 return 1
1486 return 1
1487 elif marks:
1487 elif marks:
1488 ui.debug('moving bookmarks %r from %s to %s\n' %
1488 ui.debug('moving bookmarks %r from %s to %s\n' %
1489 (marks, old.hex(), hex(node)))
1489 (marks, old.hex(), hex(node)))
1490 newmarks = repo._bookmarks
1490 newmarks = repo._bookmarks
1491 for bm in marks:
1491 for bm in marks:
1492 newmarks[bm] = node
1492 newmarks[bm] = node
1493 if bm == current:
1493 if bm == current:
1494 bookmarks.setcurrent(repo, bm)
1494 bookmarks.setcurrent(repo, bm)
1495 newmarks.write()
1495 newmarks.write()
1496 else:
1496 else:
1497 def commitfunc(ui, repo, message, match, opts):
1497 def commitfunc(ui, repo, message, match, opts):
1498 backup = ui.backupconfig('phases', 'new-commit')
1498 backup = ui.backupconfig('phases', 'new-commit')
1499 baseui = repo.baseui
1499 baseui = repo.baseui
1500 basebackup = baseui.backupconfig('phases', 'new-commit')
1500 basebackup = baseui.backupconfig('phases', 'new-commit')
1501 try:
1501 try:
1502 if opts.get('secret'):
1502 if opts.get('secret'):
1503 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1503 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1504 # Propagate to subrepos
1504 # Propagate to subrepos
1505 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1505 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1506
1506
1507 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1507 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1508 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1508 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1509 return repo.commit(message, opts.get('user'), opts.get('date'),
1509 return repo.commit(message, opts.get('user'), opts.get('date'),
1510 match,
1510 match,
1511 editor=editor,
1511 editor=editor,
1512 extra=extra)
1512 extra=extra)
1513 finally:
1513 finally:
1514 ui.restoreconfig(backup)
1514 ui.restoreconfig(backup)
1515 repo.baseui.restoreconfig(basebackup)
1515 repo.baseui.restoreconfig(basebackup)
1516
1516
1517
1517
1518 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1518 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1519
1519
1520 if not node:
1520 if not node:
1521 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1521 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1522 if stat[3]:
1522 if stat[3]:
1523 ui.status(_("nothing changed (%d missing files, see "
1523 ui.status(_("nothing changed (%d missing files, see "
1524 "'hg status')\n") % len(stat[3]))
1524 "'hg status')\n") % len(stat[3]))
1525 else:
1525 else:
1526 ui.status(_("nothing changed\n"))
1526 ui.status(_("nothing changed\n"))
1527 return 1
1527 return 1
1528
1528
1529 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1529 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1530
1530
1531 @command('config|showconfig|debugconfig',
1531 @command('config|showconfig|debugconfig',
1532 [('u', 'untrusted', None, _('show untrusted configuration options')),
1532 [('u', 'untrusted', None, _('show untrusted configuration options')),
1533 ('e', 'edit', None, _('edit user config')),
1533 ('e', 'edit', None, _('edit user config')),
1534 ('l', 'local', None, _('edit repository config')),
1534 ('l', 'local', None, _('edit repository config')),
1535 ('g', 'global', None, _('edit global config'))],
1535 ('g', 'global', None, _('edit global config'))],
1536 _('[-u] [NAME]...'),
1536 _('[-u] [NAME]...'),
1537 optionalrepo=True)
1537 optionalrepo=True)
1538 def config(ui, repo, *values, **opts):
1538 def config(ui, repo, *values, **opts):
1539 """show combined config settings from all hgrc files
1539 """show combined config settings from all hgrc files
1540
1540
1541 With no arguments, print names and values of all config items.
1541 With no arguments, print names and values of all config items.
1542
1542
1543 With one argument of the form section.name, print just the value
1543 With one argument of the form section.name, print just the value
1544 of that config item.
1544 of that config item.
1545
1545
1546 With multiple arguments, print names and values of all config
1546 With multiple arguments, print names and values of all config
1547 items with matching section names.
1547 items with matching section names.
1548
1548
1549 With --edit, start an editor on the user-level config file. With
1549 With --edit, start an editor on the user-level config file. With
1550 --global, edit the system-wide config file. With --local, edit the
1550 --global, edit the system-wide config file. With --local, edit the
1551 repository-level config file.
1551 repository-level config file.
1552
1552
1553 With --debug, the source (filename and line number) is printed
1553 With --debug, the source (filename and line number) is printed
1554 for each config item.
1554 for each config item.
1555
1555
1556 See :hg:`help config` for more information about config files.
1556 See :hg:`help config` for more information about config files.
1557
1557
1558 Returns 0 on success, 1 if NAME does not exist.
1558 Returns 0 on success, 1 if NAME does not exist.
1559
1559
1560 """
1560 """
1561
1561
1562 if opts.get('edit') or opts.get('local') or opts.get('global'):
1562 if opts.get('edit') or opts.get('local') or opts.get('global'):
1563 if opts.get('local') and opts.get('global'):
1563 if opts.get('local') and opts.get('global'):
1564 raise util.Abort(_("can't use --local and --global together"))
1564 raise util.Abort(_("can't use --local and --global together"))
1565
1565
1566 if opts.get('local'):
1566 if opts.get('local'):
1567 if not repo:
1567 if not repo:
1568 raise util.Abort(_("can't use --local outside a repository"))
1568 raise util.Abort(_("can't use --local outside a repository"))
1569 paths = [repo.join('hgrc')]
1569 paths = [repo.join('hgrc')]
1570 elif opts.get('global'):
1570 elif opts.get('global'):
1571 paths = scmutil.systemrcpath()
1571 paths = scmutil.systemrcpath()
1572 else:
1572 else:
1573 paths = scmutil.userrcpath()
1573 paths = scmutil.userrcpath()
1574
1574
1575 for f in paths:
1575 for f in paths:
1576 if os.path.exists(f):
1576 if os.path.exists(f):
1577 break
1577 break
1578 else:
1578 else:
1579 if opts.get('global'):
1579 if opts.get('global'):
1580 samplehgrc = uimod.samplehgrcs['global']
1580 samplehgrc = uimod.samplehgrcs['global']
1581 elif opts.get('local'):
1581 elif opts.get('local'):
1582 samplehgrc = uimod.samplehgrcs['local']
1582 samplehgrc = uimod.samplehgrcs['local']
1583 else:
1583 else:
1584 samplehgrc = uimod.samplehgrcs['user']
1584 samplehgrc = uimod.samplehgrcs['user']
1585
1585
1586 f = paths[0]
1586 f = paths[0]
1587 fp = open(f, "w")
1587 fp = open(f, "w")
1588 fp.write(samplehgrc)
1588 fp.write(samplehgrc)
1589 fp.close()
1589 fp.close()
1590
1590
1591 editor = ui.geteditor()
1591 editor = ui.geteditor()
1592 ui.system("%s \"%s\"" % (editor, f),
1592 ui.system("%s \"%s\"" % (editor, f),
1593 onerr=util.Abort, errprefix=_("edit failed"))
1593 onerr=util.Abort, errprefix=_("edit failed"))
1594 return
1594 return
1595
1595
1596 for f in scmutil.rcpath():
1596 for f in scmutil.rcpath():
1597 ui.debug('read config from: %s\n' % f)
1597 ui.debug('read config from: %s\n' % f)
1598 untrusted = bool(opts.get('untrusted'))
1598 untrusted = bool(opts.get('untrusted'))
1599 if values:
1599 if values:
1600 sections = [v for v in values if '.' not in v]
1600 sections = [v for v in values if '.' not in v]
1601 items = [v for v in values if '.' in v]
1601 items = [v for v in values if '.' in v]
1602 if len(items) > 1 or items and sections:
1602 if len(items) > 1 or items and sections:
1603 raise util.Abort(_('only one config item permitted'))
1603 raise util.Abort(_('only one config item permitted'))
1604 matched = False
1604 matched = False
1605 for section, name, value in ui.walkconfig(untrusted=untrusted):
1605 for section, name, value in ui.walkconfig(untrusted=untrusted):
1606 value = str(value).replace('\n', '\\n')
1606 value = str(value).replace('\n', '\\n')
1607 sectname = section + '.' + name
1607 sectname = section + '.' + name
1608 if values:
1608 if values:
1609 for v in values:
1609 for v in values:
1610 if v == section:
1610 if v == section:
1611 ui.debug('%s: ' %
1611 ui.debug('%s: ' %
1612 ui.configsource(section, name, untrusted))
1612 ui.configsource(section, name, untrusted))
1613 ui.write('%s=%s\n' % (sectname, value))
1613 ui.write('%s=%s\n' % (sectname, value))
1614 matched = True
1614 matched = True
1615 elif v == sectname:
1615 elif v == sectname:
1616 ui.debug('%s: ' %
1616 ui.debug('%s: ' %
1617 ui.configsource(section, name, untrusted))
1617 ui.configsource(section, name, untrusted))
1618 ui.write(value, '\n')
1618 ui.write(value, '\n')
1619 matched = True
1619 matched = True
1620 else:
1620 else:
1621 ui.debug('%s: ' %
1621 ui.debug('%s: ' %
1622 ui.configsource(section, name, untrusted))
1622 ui.configsource(section, name, untrusted))
1623 ui.write('%s=%s\n' % (sectname, value))
1623 ui.write('%s=%s\n' % (sectname, value))
1624 matched = True
1624 matched = True
1625 if matched:
1625 if matched:
1626 return 0
1626 return 0
1627 return 1
1627 return 1
1628
1628
1629 @command('copy|cp',
1629 @command('copy|cp',
1630 [('A', 'after', None, _('record a copy that has already occurred')),
1630 [('A', 'after', None, _('record a copy that has already occurred')),
1631 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1631 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1632 ] + walkopts + dryrunopts,
1632 ] + walkopts + dryrunopts,
1633 _('[OPTION]... [SOURCE]... DEST'))
1633 _('[OPTION]... [SOURCE]... DEST'))
1634 def copy(ui, repo, *pats, **opts):
1634 def copy(ui, repo, *pats, **opts):
1635 """mark files as copied for the next commit
1635 """mark files as copied for the next commit
1636
1636
1637 Mark dest as having copies of source files. If dest is a
1637 Mark dest as having copies of source files. If dest is a
1638 directory, copies are put in that directory. If dest is a file,
1638 directory, copies are put in that directory. If dest is a file,
1639 the source must be a single file.
1639 the source must be a single file.
1640
1640
1641 By default, this command copies the contents of files as they
1641 By default, this command copies the contents of files as they
1642 exist in the working directory. If invoked with -A/--after, the
1642 exist in the working directory. If invoked with -A/--after, the
1643 operation is recorded, but no copying is performed.
1643 operation is recorded, but no copying is performed.
1644
1644
1645 This command takes effect with the next commit. To undo a copy
1645 This command takes effect with the next commit. To undo a copy
1646 before that, see :hg:`revert`.
1646 before that, see :hg:`revert`.
1647
1647
1648 Returns 0 on success, 1 if errors are encountered.
1648 Returns 0 on success, 1 if errors are encountered.
1649 """
1649 """
1650 wlock = repo.wlock(False)
1650 wlock = repo.wlock(False)
1651 try:
1651 try:
1652 return cmdutil.copy(ui, repo, pats, opts)
1652 return cmdutil.copy(ui, repo, pats, opts)
1653 finally:
1653 finally:
1654 wlock.release()
1654 wlock.release()
1655
1655
1656 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1656 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1657 def debugancestor(ui, repo, *args):
1657 def debugancestor(ui, repo, *args):
1658 """find the ancestor revision of two revisions in a given index"""
1658 """find the ancestor revision of two revisions in a given index"""
1659 if len(args) == 3:
1659 if len(args) == 3:
1660 index, rev1, rev2 = args
1660 index, rev1, rev2 = args
1661 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1661 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1662 lookup = r.lookup
1662 lookup = r.lookup
1663 elif len(args) == 2:
1663 elif len(args) == 2:
1664 if not repo:
1664 if not repo:
1665 raise util.Abort(_("there is no Mercurial repository here "
1665 raise util.Abort(_("there is no Mercurial repository here "
1666 "(.hg not found)"))
1666 "(.hg not found)"))
1667 rev1, rev2 = args
1667 rev1, rev2 = args
1668 r = repo.changelog
1668 r = repo.changelog
1669 lookup = repo.lookup
1669 lookup = repo.lookup
1670 else:
1670 else:
1671 raise util.Abort(_('either two or three arguments required'))
1671 raise util.Abort(_('either two or three arguments required'))
1672 a = r.ancestor(lookup(rev1), lookup(rev2))
1672 a = r.ancestor(lookup(rev1), lookup(rev2))
1673 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1673 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1674
1674
1675 @command('debugbuilddag',
1675 @command('debugbuilddag',
1676 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1676 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1677 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1677 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1678 ('n', 'new-file', None, _('add new file at each rev'))],
1678 ('n', 'new-file', None, _('add new file at each rev'))],
1679 _('[OPTION]... [TEXT]'))
1679 _('[OPTION]... [TEXT]'))
1680 def debugbuilddag(ui, repo, text=None,
1680 def debugbuilddag(ui, repo, text=None,
1681 mergeable_file=False,
1681 mergeable_file=False,
1682 overwritten_file=False,
1682 overwritten_file=False,
1683 new_file=False):
1683 new_file=False):
1684 """builds a repo with a given DAG from scratch in the current empty repo
1684 """builds a repo with a given DAG from scratch in the current empty repo
1685
1685
1686 The description of the DAG is read from stdin if not given on the
1686 The description of the DAG is read from stdin if not given on the
1687 command line.
1687 command line.
1688
1688
1689 Elements:
1689 Elements:
1690
1690
1691 - "+n" is a linear run of n nodes based on the current default parent
1691 - "+n" is a linear run of n nodes based on the current default parent
1692 - "." is a single node based on the current default parent
1692 - "." is a single node based on the current default parent
1693 - "$" resets the default parent to null (implied at the start);
1693 - "$" resets the default parent to null (implied at the start);
1694 otherwise the default parent is always the last node created
1694 otherwise the default parent is always the last node created
1695 - "<p" sets the default parent to the backref p
1695 - "<p" sets the default parent to the backref p
1696 - "*p" is a fork at parent p, which is a backref
1696 - "*p" is a fork at parent p, which is a backref
1697 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1697 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1698 - "/p2" is a merge of the preceding node and p2
1698 - "/p2" is a merge of the preceding node and p2
1699 - ":tag" defines a local tag for the preceding node
1699 - ":tag" defines a local tag for the preceding node
1700 - "@branch" sets the named branch for subsequent nodes
1700 - "@branch" sets the named branch for subsequent nodes
1701 - "#...\\n" is a comment up to the end of the line
1701 - "#...\\n" is a comment up to the end of the line
1702
1702
1703 Whitespace between the above elements is ignored.
1703 Whitespace between the above elements is ignored.
1704
1704
1705 A backref is either
1705 A backref is either
1706
1706
1707 - a number n, which references the node curr-n, where curr is the current
1707 - a number n, which references the node curr-n, where curr is the current
1708 node, or
1708 node, or
1709 - the name of a local tag you placed earlier using ":tag", or
1709 - the name of a local tag you placed earlier using ":tag", or
1710 - empty to denote the default parent.
1710 - empty to denote the default parent.
1711
1711
1712 All string valued-elements are either strictly alphanumeric, or must
1712 All string valued-elements are either strictly alphanumeric, or must
1713 be enclosed in double quotes ("..."), with "\\" as escape character.
1713 be enclosed in double quotes ("..."), with "\\" as escape character.
1714 """
1714 """
1715
1715
1716 if text is None:
1716 if text is None:
1717 ui.status(_("reading DAG from stdin\n"))
1717 ui.status(_("reading DAG from stdin\n"))
1718 text = ui.fin.read()
1718 text = ui.fin.read()
1719
1719
1720 cl = repo.changelog
1720 cl = repo.changelog
1721 if len(cl) > 0:
1721 if len(cl) > 0:
1722 raise util.Abort(_('repository is not empty'))
1722 raise util.Abort(_('repository is not empty'))
1723
1723
1724 # determine number of revs in DAG
1724 # determine number of revs in DAG
1725 total = 0
1725 total = 0
1726 for type, data in dagparser.parsedag(text):
1726 for type, data in dagparser.parsedag(text):
1727 if type == 'n':
1727 if type == 'n':
1728 total += 1
1728 total += 1
1729
1729
1730 if mergeable_file:
1730 if mergeable_file:
1731 linesperrev = 2
1731 linesperrev = 2
1732 # make a file with k lines per rev
1732 # make a file with k lines per rev
1733 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1733 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1734 initialmergedlines.append("")
1734 initialmergedlines.append("")
1735
1735
1736 tags = []
1736 tags = []
1737
1737
1738 lock = tr = None
1738 lock = tr = None
1739 try:
1739 try:
1740 lock = repo.lock()
1740 lock = repo.lock()
1741 tr = repo.transaction("builddag")
1741 tr = repo.transaction("builddag")
1742
1742
1743 at = -1
1743 at = -1
1744 atbranch = 'default'
1744 atbranch = 'default'
1745 nodeids = []
1745 nodeids = []
1746 id = 0
1746 id = 0
1747 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1747 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1748 for type, data in dagparser.parsedag(text):
1748 for type, data in dagparser.parsedag(text):
1749 if type == 'n':
1749 if type == 'n':
1750 ui.note(('node %s\n' % str(data)))
1750 ui.note(('node %s\n' % str(data)))
1751 id, ps = data
1751 id, ps = data
1752
1752
1753 files = []
1753 files = []
1754 fctxs = {}
1754 fctxs = {}
1755
1755
1756 p2 = None
1756 p2 = None
1757 if mergeable_file:
1757 if mergeable_file:
1758 fn = "mf"
1758 fn = "mf"
1759 p1 = repo[ps[0]]
1759 p1 = repo[ps[0]]
1760 if len(ps) > 1:
1760 if len(ps) > 1:
1761 p2 = repo[ps[1]]
1761 p2 = repo[ps[1]]
1762 pa = p1.ancestor(p2)
1762 pa = p1.ancestor(p2)
1763 base, local, other = [x[fn].data() for x in (pa, p1,
1763 base, local, other = [x[fn].data() for x in (pa, p1,
1764 p2)]
1764 p2)]
1765 m3 = simplemerge.Merge3Text(base, local, other)
1765 m3 = simplemerge.Merge3Text(base, local, other)
1766 ml = [l.strip() for l in m3.merge_lines()]
1766 ml = [l.strip() for l in m3.merge_lines()]
1767 ml.append("")
1767 ml.append("")
1768 elif at > 0:
1768 elif at > 0:
1769 ml = p1[fn].data().split("\n")
1769 ml = p1[fn].data().split("\n")
1770 else:
1770 else:
1771 ml = initialmergedlines
1771 ml = initialmergedlines
1772 ml[id * linesperrev] += " r%i" % id
1772 ml[id * linesperrev] += " r%i" % id
1773 mergedtext = "\n".join(ml)
1773 mergedtext = "\n".join(ml)
1774 files.append(fn)
1774 files.append(fn)
1775 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
1775 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
1776
1776
1777 if overwritten_file:
1777 if overwritten_file:
1778 fn = "of"
1778 fn = "of"
1779 files.append(fn)
1779 files.append(fn)
1780 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1780 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1781
1781
1782 if new_file:
1782 if new_file:
1783 fn = "nf%i" % id
1783 fn = "nf%i" % id
1784 files.append(fn)
1784 files.append(fn)
1785 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1785 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1786 if len(ps) > 1:
1786 if len(ps) > 1:
1787 if not p2:
1787 if not p2:
1788 p2 = repo[ps[1]]
1788 p2 = repo[ps[1]]
1789 for fn in p2:
1789 for fn in p2:
1790 if fn.startswith("nf"):
1790 if fn.startswith("nf"):
1791 files.append(fn)
1791 files.append(fn)
1792 fctxs[fn] = p2[fn]
1792 fctxs[fn] = p2[fn]
1793
1793
1794 def fctxfn(repo, cx, path):
1794 def fctxfn(repo, cx, path):
1795 return fctxs.get(path)
1795 return fctxs.get(path)
1796
1796
1797 if len(ps) == 0 or ps[0] < 0:
1797 if len(ps) == 0 or ps[0] < 0:
1798 pars = [None, None]
1798 pars = [None, None]
1799 elif len(ps) == 1:
1799 elif len(ps) == 1:
1800 pars = [nodeids[ps[0]], None]
1800 pars = [nodeids[ps[0]], None]
1801 else:
1801 else:
1802 pars = [nodeids[p] for p in ps]
1802 pars = [nodeids[p] for p in ps]
1803 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1803 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1804 date=(id, 0),
1804 date=(id, 0),
1805 user="debugbuilddag",
1805 user="debugbuilddag",
1806 extra={'branch': atbranch})
1806 extra={'branch': atbranch})
1807 nodeid = repo.commitctx(cx)
1807 nodeid = repo.commitctx(cx)
1808 nodeids.append(nodeid)
1808 nodeids.append(nodeid)
1809 at = id
1809 at = id
1810 elif type == 'l':
1810 elif type == 'l':
1811 id, name = data
1811 id, name = data
1812 ui.note(('tag %s\n' % name))
1812 ui.note(('tag %s\n' % name))
1813 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1813 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1814 elif type == 'a':
1814 elif type == 'a':
1815 ui.note(('branch %s\n' % data))
1815 ui.note(('branch %s\n' % data))
1816 atbranch = data
1816 atbranch = data
1817 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1817 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1818 tr.close()
1818 tr.close()
1819
1819
1820 if tags:
1820 if tags:
1821 repo.vfs.write("localtags", "".join(tags))
1821 repo.vfs.write("localtags", "".join(tags))
1822 finally:
1822 finally:
1823 ui.progress(_('building'), None)
1823 ui.progress(_('building'), None)
1824 release(tr, lock)
1824 release(tr, lock)
1825
1825
1826 @command('debugbundle',
1826 @command('debugbundle',
1827 [('a', 'all', None, _('show all details'))],
1827 [('a', 'all', None, _('show all details'))],
1828 _('FILE'),
1828 _('FILE'),
1829 norepo=True)
1829 norepo=True)
1830 def debugbundle(ui, bundlepath, all=None, **opts):
1830 def debugbundle(ui, bundlepath, all=None, **opts):
1831 """lists the contents of a bundle"""
1831 """lists the contents of a bundle"""
1832 f = hg.openpath(ui, bundlepath)
1832 f = hg.openpath(ui, bundlepath)
1833 try:
1833 try:
1834 gen = exchange.readbundle(ui, f, bundlepath)
1834 gen = exchange.readbundle(ui, f, bundlepath)
1835 if isinstance(gen, bundle2.unbundle20):
1835 if isinstance(gen, bundle2.unbundle20):
1836 return _debugbundle2(ui, gen, all=all, **opts)
1836 return _debugbundle2(ui, gen, all=all, **opts)
1837 if all:
1837 if all:
1838 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1838 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1839
1839
1840 def showchunks(named):
1840 def showchunks(named):
1841 ui.write("\n%s\n" % named)
1841 ui.write("\n%s\n" % named)
1842 chain = None
1842 chain = None
1843 while True:
1843 while True:
1844 chunkdata = gen.deltachunk(chain)
1844 chunkdata = gen.deltachunk(chain)
1845 if not chunkdata:
1845 if not chunkdata:
1846 break
1846 break
1847 node = chunkdata['node']
1847 node = chunkdata['node']
1848 p1 = chunkdata['p1']
1848 p1 = chunkdata['p1']
1849 p2 = chunkdata['p2']
1849 p2 = chunkdata['p2']
1850 cs = chunkdata['cs']
1850 cs = chunkdata['cs']
1851 deltabase = chunkdata['deltabase']
1851 deltabase = chunkdata['deltabase']
1852 delta = chunkdata['delta']
1852 delta = chunkdata['delta']
1853 ui.write("%s %s %s %s %s %s\n" %
1853 ui.write("%s %s %s %s %s %s\n" %
1854 (hex(node), hex(p1), hex(p2),
1854 (hex(node), hex(p1), hex(p2),
1855 hex(cs), hex(deltabase), len(delta)))
1855 hex(cs), hex(deltabase), len(delta)))
1856 chain = node
1856 chain = node
1857
1857
1858 chunkdata = gen.changelogheader()
1858 chunkdata = gen.changelogheader()
1859 showchunks("changelog")
1859 showchunks("changelog")
1860 chunkdata = gen.manifestheader()
1860 chunkdata = gen.manifestheader()
1861 showchunks("manifest")
1861 showchunks("manifest")
1862 while True:
1862 while True:
1863 chunkdata = gen.filelogheader()
1863 chunkdata = gen.filelogheader()
1864 if not chunkdata:
1864 if not chunkdata:
1865 break
1865 break
1866 fname = chunkdata['filename']
1866 fname = chunkdata['filename']
1867 showchunks(fname)
1867 showchunks(fname)
1868 else:
1868 else:
1869 if isinstance(gen, bundle2.unbundle20):
1869 if isinstance(gen, bundle2.unbundle20):
1870 raise util.Abort(_('use debugbundle2 for this file'))
1870 raise util.Abort(_('use debugbundle2 for this file'))
1871 chunkdata = gen.changelogheader()
1871 chunkdata = gen.changelogheader()
1872 chain = None
1872 chain = None
1873 while True:
1873 while True:
1874 chunkdata = gen.deltachunk(chain)
1874 chunkdata = gen.deltachunk(chain)
1875 if not chunkdata:
1875 if not chunkdata:
1876 break
1876 break
1877 node = chunkdata['node']
1877 node = chunkdata['node']
1878 ui.write("%s\n" % hex(node))
1878 ui.write("%s\n" % hex(node))
1879 chain = node
1879 chain = node
1880 finally:
1880 finally:
1881 f.close()
1881 f.close()
1882
1882
1883 def _debugbundle2(ui, gen, **opts):
1883 def _debugbundle2(ui, gen, **opts):
1884 """lists the contents of a bundle2"""
1884 """lists the contents of a bundle2"""
1885 if not isinstance(gen, bundle2.unbundle20):
1885 if not isinstance(gen, bundle2.unbundle20):
1886 raise util.Abort(_('not a bundle2 file'))
1886 raise util.Abort(_('not a bundle2 file'))
1887 ui.write(('Stream params: %s\n' % repr(gen.params)))
1887 ui.write(('Stream params: %s\n' % repr(gen.params)))
1888 for part in gen.iterparts():
1888 for part in gen.iterparts():
1889 ui.write('%s -- %r\n' % (part.type, repr(part.params)))
1889 ui.write('%s -- %r\n' % (part.type, repr(part.params)))
1890 if part.type == 'b2x:changegroup':
1890 if part.type == 'b2x:changegroup':
1891 version = part.params.get('version', '01')
1891 version = part.params.get('version', '01')
1892 cg = changegroup.packermap[version][1](part, 'UN')
1892 cg = changegroup.packermap[version][1](part, 'UN')
1893 chunkdata = cg.changelogheader()
1893 chunkdata = cg.changelogheader()
1894 chain = None
1894 chain = None
1895 while True:
1895 while True:
1896 chunkdata = cg.deltachunk(chain)
1896 chunkdata = cg.deltachunk(chain)
1897 if not chunkdata:
1897 if not chunkdata:
1898 break
1898 break
1899 node = chunkdata['node']
1899 node = chunkdata['node']
1900 ui.write(" %s\n" % hex(node))
1900 ui.write(" %s\n" % hex(node))
1901 chain = node
1901 chain = node
1902
1902
1903 @command('debugcheckstate', [], '')
1903 @command('debugcheckstate', [], '')
1904 def debugcheckstate(ui, repo):
1904 def debugcheckstate(ui, repo):
1905 """validate the correctness of the current dirstate"""
1905 """validate the correctness of the current dirstate"""
1906 parent1, parent2 = repo.dirstate.parents()
1906 parent1, parent2 = repo.dirstate.parents()
1907 m1 = repo[parent1].manifest()
1907 m1 = repo[parent1].manifest()
1908 m2 = repo[parent2].manifest()
1908 m2 = repo[parent2].manifest()
1909 errors = 0
1909 errors = 0
1910 for f in repo.dirstate:
1910 for f in repo.dirstate:
1911 state = repo.dirstate[f]
1911 state = repo.dirstate[f]
1912 if state in "nr" and f not in m1:
1912 if state in "nr" and f not in m1:
1913 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1913 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1914 errors += 1
1914 errors += 1
1915 if state in "a" and f in m1:
1915 if state in "a" and f in m1:
1916 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1916 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1917 errors += 1
1917 errors += 1
1918 if state in "m" and f not in m1 and f not in m2:
1918 if state in "m" and f not in m1 and f not in m2:
1919 ui.warn(_("%s in state %s, but not in either manifest\n") %
1919 ui.warn(_("%s in state %s, but not in either manifest\n") %
1920 (f, state))
1920 (f, state))
1921 errors += 1
1921 errors += 1
1922 for f in m1:
1922 for f in m1:
1923 state = repo.dirstate[f]
1923 state = repo.dirstate[f]
1924 if state not in "nrm":
1924 if state not in "nrm":
1925 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1925 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1926 errors += 1
1926 errors += 1
1927 if errors:
1927 if errors:
1928 error = _(".hg/dirstate inconsistent with current parent's manifest")
1928 error = _(".hg/dirstate inconsistent with current parent's manifest")
1929 raise util.Abort(error)
1929 raise util.Abort(error)
1930
1930
1931 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1931 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1932 def debugcommands(ui, cmd='', *args):
1932 def debugcommands(ui, cmd='', *args):
1933 """list all available commands and options"""
1933 """list all available commands and options"""
1934 for cmd, vals in sorted(table.iteritems()):
1934 for cmd, vals in sorted(table.iteritems()):
1935 cmd = cmd.split('|')[0].strip('^')
1935 cmd = cmd.split('|')[0].strip('^')
1936 opts = ', '.join([i[1] for i in vals[1]])
1936 opts = ', '.join([i[1] for i in vals[1]])
1937 ui.write('%s: %s\n' % (cmd, opts))
1937 ui.write('%s: %s\n' % (cmd, opts))
1938
1938
1939 @command('debugcomplete',
1939 @command('debugcomplete',
1940 [('o', 'options', None, _('show the command options'))],
1940 [('o', 'options', None, _('show the command options'))],
1941 _('[-o] CMD'),
1941 _('[-o] CMD'),
1942 norepo=True)
1942 norepo=True)
1943 def debugcomplete(ui, cmd='', **opts):
1943 def debugcomplete(ui, cmd='', **opts):
1944 """returns the completion list associated with the given command"""
1944 """returns the completion list associated with the given command"""
1945
1945
1946 if opts.get('options'):
1946 if opts.get('options'):
1947 options = []
1947 options = []
1948 otables = [globalopts]
1948 otables = [globalopts]
1949 if cmd:
1949 if cmd:
1950 aliases, entry = cmdutil.findcmd(cmd, table, False)
1950 aliases, entry = cmdutil.findcmd(cmd, table, False)
1951 otables.append(entry[1])
1951 otables.append(entry[1])
1952 for t in otables:
1952 for t in otables:
1953 for o in t:
1953 for o in t:
1954 if "(DEPRECATED)" in o[3]:
1954 if "(DEPRECATED)" in o[3]:
1955 continue
1955 continue
1956 if o[0]:
1956 if o[0]:
1957 options.append('-%s' % o[0])
1957 options.append('-%s' % o[0])
1958 options.append('--%s' % o[1])
1958 options.append('--%s' % o[1])
1959 ui.write("%s\n" % "\n".join(options))
1959 ui.write("%s\n" % "\n".join(options))
1960 return
1960 return
1961
1961
1962 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1962 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1963 if ui.verbose:
1963 if ui.verbose:
1964 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1964 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1965 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1965 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1966
1966
1967 @command('debugdag',
1967 @command('debugdag',
1968 [('t', 'tags', None, _('use tags as labels')),
1968 [('t', 'tags', None, _('use tags as labels')),
1969 ('b', 'branches', None, _('annotate with branch names')),
1969 ('b', 'branches', None, _('annotate with branch names')),
1970 ('', 'dots', None, _('use dots for runs')),
1970 ('', 'dots', None, _('use dots for runs')),
1971 ('s', 'spaces', None, _('separate elements by spaces'))],
1971 ('s', 'spaces', None, _('separate elements by spaces'))],
1972 _('[OPTION]... [FILE [REV]...]'),
1972 _('[OPTION]... [FILE [REV]...]'),
1973 optionalrepo=True)
1973 optionalrepo=True)
1974 def debugdag(ui, repo, file_=None, *revs, **opts):
1974 def debugdag(ui, repo, file_=None, *revs, **opts):
1975 """format the changelog or an index DAG as a concise textual description
1975 """format the changelog or an index DAG as a concise textual description
1976
1976
1977 If you pass a revlog index, the revlog's DAG is emitted. If you list
1977 If you pass a revlog index, the revlog's DAG is emitted. If you list
1978 revision numbers, they get labeled in the output as rN.
1978 revision numbers, they get labeled in the output as rN.
1979
1979
1980 Otherwise, the changelog DAG of the current repo is emitted.
1980 Otherwise, the changelog DAG of the current repo is emitted.
1981 """
1981 """
1982 spaces = opts.get('spaces')
1982 spaces = opts.get('spaces')
1983 dots = opts.get('dots')
1983 dots = opts.get('dots')
1984 if file_:
1984 if file_:
1985 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1985 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1986 revs = set((int(r) for r in revs))
1986 revs = set((int(r) for r in revs))
1987 def events():
1987 def events():
1988 for r in rlog:
1988 for r in rlog:
1989 yield 'n', (r, list(p for p in rlog.parentrevs(r)
1989 yield 'n', (r, list(p for p in rlog.parentrevs(r)
1990 if p != -1))
1990 if p != -1))
1991 if r in revs:
1991 if r in revs:
1992 yield 'l', (r, "r%i" % r)
1992 yield 'l', (r, "r%i" % r)
1993 elif repo:
1993 elif repo:
1994 cl = repo.changelog
1994 cl = repo.changelog
1995 tags = opts.get('tags')
1995 tags = opts.get('tags')
1996 branches = opts.get('branches')
1996 branches = opts.get('branches')
1997 if tags:
1997 if tags:
1998 labels = {}
1998 labels = {}
1999 for l, n in repo.tags().items():
1999 for l, n in repo.tags().items():
2000 labels.setdefault(cl.rev(n), []).append(l)
2000 labels.setdefault(cl.rev(n), []).append(l)
2001 def events():
2001 def events():
2002 b = "default"
2002 b = "default"
2003 for r in cl:
2003 for r in cl:
2004 if branches:
2004 if branches:
2005 newb = cl.read(cl.node(r))[5]['branch']
2005 newb = cl.read(cl.node(r))[5]['branch']
2006 if newb != b:
2006 if newb != b:
2007 yield 'a', newb
2007 yield 'a', newb
2008 b = newb
2008 b = newb
2009 yield 'n', (r, list(p for p in cl.parentrevs(r)
2009 yield 'n', (r, list(p for p in cl.parentrevs(r)
2010 if p != -1))
2010 if p != -1))
2011 if tags:
2011 if tags:
2012 ls = labels.get(r)
2012 ls = labels.get(r)
2013 if ls:
2013 if ls:
2014 for l in ls:
2014 for l in ls:
2015 yield 'l', (r, l)
2015 yield 'l', (r, l)
2016 else:
2016 else:
2017 raise util.Abort(_('need repo for changelog dag'))
2017 raise util.Abort(_('need repo for changelog dag'))
2018
2018
2019 for line in dagparser.dagtextlines(events(),
2019 for line in dagparser.dagtextlines(events(),
2020 addspaces=spaces,
2020 addspaces=spaces,
2021 wraplabels=True,
2021 wraplabels=True,
2022 wrapannotations=True,
2022 wrapannotations=True,
2023 wrapnonlinear=dots,
2023 wrapnonlinear=dots,
2024 usedots=dots,
2024 usedots=dots,
2025 maxlinewidth=70):
2025 maxlinewidth=70):
2026 ui.write(line)
2026 ui.write(line)
2027 ui.write("\n")
2027 ui.write("\n")
2028
2028
2029 @command('debugdata',
2029 @command('debugdata',
2030 [('c', 'changelog', False, _('open changelog')),
2030 [('c', 'changelog', False, _('open changelog')),
2031 ('m', 'manifest', False, _('open manifest'))],
2031 ('m', 'manifest', False, _('open manifest'))],
2032 _('-c|-m|FILE REV'))
2032 _('-c|-m|FILE REV'))
2033 def debugdata(ui, repo, file_, rev=None, **opts):
2033 def debugdata(ui, repo, file_, rev=None, **opts):
2034 """dump the contents of a data file revision"""
2034 """dump the contents of a data file revision"""
2035 if opts.get('changelog') or opts.get('manifest'):
2035 if opts.get('changelog') or opts.get('manifest'):
2036 file_, rev = None, file_
2036 file_, rev = None, file_
2037 elif rev is None:
2037 elif rev is None:
2038 raise error.CommandError('debugdata', _('invalid arguments'))
2038 raise error.CommandError('debugdata', _('invalid arguments'))
2039 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
2039 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
2040 try:
2040 try:
2041 ui.write(r.revision(r.lookup(rev)))
2041 ui.write(r.revision(r.lookup(rev)))
2042 except KeyError:
2042 except KeyError:
2043 raise util.Abort(_('invalid revision identifier %s') % rev)
2043 raise util.Abort(_('invalid revision identifier %s') % rev)
2044
2044
2045 @command('debugdate',
2045 @command('debugdate',
2046 [('e', 'extended', None, _('try extended date formats'))],
2046 [('e', 'extended', None, _('try extended date formats'))],
2047 _('[-e] DATE [RANGE]'),
2047 _('[-e] DATE [RANGE]'),
2048 norepo=True, optionalrepo=True)
2048 norepo=True, optionalrepo=True)
2049 def debugdate(ui, date, range=None, **opts):
2049 def debugdate(ui, date, range=None, **opts):
2050 """parse and display a date"""
2050 """parse and display a date"""
2051 if opts["extended"]:
2051 if opts["extended"]:
2052 d = util.parsedate(date, util.extendeddateformats)
2052 d = util.parsedate(date, util.extendeddateformats)
2053 else:
2053 else:
2054 d = util.parsedate(date)
2054 d = util.parsedate(date)
2055 ui.write(("internal: %s %s\n") % d)
2055 ui.write(("internal: %s %s\n") % d)
2056 ui.write(("standard: %s\n") % util.datestr(d))
2056 ui.write(("standard: %s\n") % util.datestr(d))
2057 if range:
2057 if range:
2058 m = util.matchdate(range)
2058 m = util.matchdate(range)
2059 ui.write(("match: %s\n") % m(d[0]))
2059 ui.write(("match: %s\n") % m(d[0]))
2060
2060
2061 @command('debugdiscovery',
2061 @command('debugdiscovery',
2062 [('', 'old', None, _('use old-style discovery')),
2062 [('', 'old', None, _('use old-style discovery')),
2063 ('', 'nonheads', None,
2063 ('', 'nonheads', None,
2064 _('use old-style discovery with non-heads included')),
2064 _('use old-style discovery with non-heads included')),
2065 ] + remoteopts,
2065 ] + remoteopts,
2066 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
2066 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
2067 def debugdiscovery(ui, repo, remoteurl="default", **opts):
2067 def debugdiscovery(ui, repo, remoteurl="default", **opts):
2068 """runs the changeset discovery protocol in isolation"""
2068 """runs the changeset discovery protocol in isolation"""
2069 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
2069 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
2070 opts.get('branch'))
2070 opts.get('branch'))
2071 remote = hg.peer(repo, opts, remoteurl)
2071 remote = hg.peer(repo, opts, remoteurl)
2072 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
2072 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
2073
2073
2074 # make sure tests are repeatable
2074 # make sure tests are repeatable
2075 random.seed(12323)
2075 random.seed(12323)
2076
2076
2077 def doit(localheads, remoteheads, remote=remote):
2077 def doit(localheads, remoteheads, remote=remote):
2078 if opts.get('old'):
2078 if opts.get('old'):
2079 if localheads:
2079 if localheads:
2080 raise util.Abort('cannot use localheads with old style '
2080 raise util.Abort('cannot use localheads with old style '
2081 'discovery')
2081 'discovery')
2082 if not util.safehasattr(remote, 'branches'):
2082 if not util.safehasattr(remote, 'branches'):
2083 # enable in-client legacy support
2083 # enable in-client legacy support
2084 remote = localrepo.locallegacypeer(remote.local())
2084 remote = localrepo.locallegacypeer(remote.local())
2085 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2085 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2086 force=True)
2086 force=True)
2087 common = set(common)
2087 common = set(common)
2088 if not opts.get('nonheads'):
2088 if not opts.get('nonheads'):
2089 ui.write(("unpruned common: %s\n") %
2089 ui.write(("unpruned common: %s\n") %
2090 " ".join(sorted(short(n) for n in common)))
2090 " ".join(sorted(short(n) for n in common)))
2091 dag = dagutil.revlogdag(repo.changelog)
2091 dag = dagutil.revlogdag(repo.changelog)
2092 all = dag.ancestorset(dag.internalizeall(common))
2092 all = dag.ancestorset(dag.internalizeall(common))
2093 common = dag.externalizeall(dag.headsetofconnecteds(all))
2093 common = dag.externalizeall(dag.headsetofconnecteds(all))
2094 else:
2094 else:
2095 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2095 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2096 common = set(common)
2096 common = set(common)
2097 rheads = set(hds)
2097 rheads = set(hds)
2098 lheads = set(repo.heads())
2098 lheads = set(repo.heads())
2099 ui.write(("common heads: %s\n") %
2099 ui.write(("common heads: %s\n") %
2100 " ".join(sorted(short(n) for n in common)))
2100 " ".join(sorted(short(n) for n in common)))
2101 if lheads <= common:
2101 if lheads <= common:
2102 ui.write(("local is subset\n"))
2102 ui.write(("local is subset\n"))
2103 elif rheads <= common:
2103 elif rheads <= common:
2104 ui.write(("remote is subset\n"))
2104 ui.write(("remote is subset\n"))
2105
2105
2106 serverlogs = opts.get('serverlog')
2106 serverlogs = opts.get('serverlog')
2107 if serverlogs:
2107 if serverlogs:
2108 for filename in serverlogs:
2108 for filename in serverlogs:
2109 logfile = open(filename, 'r')
2109 logfile = open(filename, 'r')
2110 try:
2110 try:
2111 line = logfile.readline()
2111 line = logfile.readline()
2112 while line:
2112 while line:
2113 parts = line.strip().split(';')
2113 parts = line.strip().split(';')
2114 op = parts[1]
2114 op = parts[1]
2115 if op == 'cg':
2115 if op == 'cg':
2116 pass
2116 pass
2117 elif op == 'cgss':
2117 elif op == 'cgss':
2118 doit(parts[2].split(' '), parts[3].split(' '))
2118 doit(parts[2].split(' '), parts[3].split(' '))
2119 elif op == 'unb':
2119 elif op == 'unb':
2120 doit(parts[3].split(' '), parts[2].split(' '))
2120 doit(parts[3].split(' '), parts[2].split(' '))
2121 line = logfile.readline()
2121 line = logfile.readline()
2122 finally:
2122 finally:
2123 logfile.close()
2123 logfile.close()
2124
2124
2125 else:
2125 else:
2126 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2126 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2127 opts.get('remote_head'))
2127 opts.get('remote_head'))
2128 localrevs = opts.get('local_head')
2128 localrevs = opts.get('local_head')
2129 doit(localrevs, remoterevs)
2129 doit(localrevs, remoterevs)
2130
2130
2131 @command('debugfileset',
2131 @command('debugfileset',
2132 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2132 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2133 _('[-r REV] FILESPEC'))
2133 _('[-r REV] FILESPEC'))
2134 def debugfileset(ui, repo, expr, **opts):
2134 def debugfileset(ui, repo, expr, **opts):
2135 '''parse and apply a fileset specification'''
2135 '''parse and apply a fileset specification'''
2136 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2136 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2137 if ui.verbose:
2137 if ui.verbose:
2138 tree = fileset.parse(expr)[0]
2138 tree = fileset.parse(expr)[0]
2139 ui.note(tree, "\n")
2139 ui.note(tree, "\n")
2140
2140
2141 for f in ctx.getfileset(expr):
2141 for f in ctx.getfileset(expr):
2142 ui.write("%s\n" % f)
2142 ui.write("%s\n" % f)
2143
2143
2144 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2144 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2145 def debugfsinfo(ui, path="."):
2145 def debugfsinfo(ui, path="."):
2146 """show information detected about current filesystem"""
2146 """show information detected about current filesystem"""
2147 util.writefile('.debugfsinfo', '')
2147 util.writefile('.debugfsinfo', '')
2148 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2148 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2149 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2149 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2150 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2150 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2151 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2151 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2152 and 'yes' or 'no'))
2152 and 'yes' or 'no'))
2153 os.unlink('.debugfsinfo')
2153 os.unlink('.debugfsinfo')
2154
2154
2155 @command('debuggetbundle',
2155 @command('debuggetbundle',
2156 [('H', 'head', [], _('id of head node'), _('ID')),
2156 [('H', 'head', [], _('id of head node'), _('ID')),
2157 ('C', 'common', [], _('id of common node'), _('ID')),
2157 ('C', 'common', [], _('id of common node'), _('ID')),
2158 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2158 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2159 _('REPO FILE [-H|-C ID]...'),
2159 _('REPO FILE [-H|-C ID]...'),
2160 norepo=True)
2160 norepo=True)
2161 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2161 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2162 """retrieves a bundle from a repo
2162 """retrieves a bundle from a repo
2163
2163
2164 Every ID must be a full-length hex node id string. Saves the bundle to the
2164 Every ID must be a full-length hex node id string. Saves the bundle to the
2165 given file.
2165 given file.
2166 """
2166 """
2167 repo = hg.peer(ui, opts, repopath)
2167 repo = hg.peer(ui, opts, repopath)
2168 if not repo.capable('getbundle'):
2168 if not repo.capable('getbundle'):
2169 raise util.Abort("getbundle() not supported by target repository")
2169 raise util.Abort("getbundle() not supported by target repository")
2170 args = {}
2170 args = {}
2171 if common:
2171 if common:
2172 args['common'] = [bin(s) for s in common]
2172 args['common'] = [bin(s) for s in common]
2173 if head:
2173 if head:
2174 args['heads'] = [bin(s) for s in head]
2174 args['heads'] = [bin(s) for s in head]
2175 # TODO: get desired bundlecaps from command line.
2175 # TODO: get desired bundlecaps from command line.
2176 args['bundlecaps'] = None
2176 args['bundlecaps'] = None
2177 bundle = repo.getbundle('debug', **args)
2177 bundle = repo.getbundle('debug', **args)
2178
2178
2179 bundletype = opts.get('type', 'bzip2').lower()
2179 bundletype = opts.get('type', 'bzip2').lower()
2180 btypes = {'none': 'HG10UN',
2180 btypes = {'none': 'HG10UN',
2181 'bzip2': 'HG10BZ',
2181 'bzip2': 'HG10BZ',
2182 'gzip': 'HG10GZ',
2182 'gzip': 'HG10GZ',
2183 'bundle2': 'HG2Y'}
2183 'bundle2': 'HG2Y'}
2184 bundletype = btypes.get(bundletype)
2184 bundletype = btypes.get(bundletype)
2185 if bundletype not in changegroup.bundletypes:
2185 if bundletype not in changegroup.bundletypes:
2186 raise util.Abort(_('unknown bundle type specified with --type'))
2186 raise util.Abort(_('unknown bundle type specified with --type'))
2187 changegroup.writebundle(ui, bundle, bundlepath, bundletype)
2187 changegroup.writebundle(ui, bundle, bundlepath, bundletype)
2188
2188
2189 @command('debugignore', [], '')
2189 @command('debugignore', [], '')
2190 def debugignore(ui, repo, *values, **opts):
2190 def debugignore(ui, repo, *values, **opts):
2191 """display the combined ignore pattern"""
2191 """display the combined ignore pattern"""
2192 ignore = repo.dirstate._ignore
2192 ignore = repo.dirstate._ignore
2193 includepat = getattr(ignore, 'includepat', None)
2193 includepat = getattr(ignore, 'includepat', None)
2194 if includepat is not None:
2194 if includepat is not None:
2195 ui.write("%s\n" % includepat)
2195 ui.write("%s\n" % includepat)
2196 else:
2196 else:
2197 raise util.Abort(_("no ignore patterns found"))
2197 raise util.Abort(_("no ignore patterns found"))
2198
2198
2199 @command('debugindex',
2199 @command('debugindex',
2200 [('c', 'changelog', False, _('open changelog')),
2200 [('c', 'changelog', False, _('open changelog')),
2201 ('m', 'manifest', False, _('open manifest')),
2201 ('m', 'manifest', False, _('open manifest')),
2202 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2202 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2203 _('[-f FORMAT] -c|-m|FILE'),
2203 _('[-f FORMAT] -c|-m|FILE'),
2204 optionalrepo=True)
2204 optionalrepo=True)
2205 def debugindex(ui, repo, file_=None, **opts):
2205 def debugindex(ui, repo, file_=None, **opts):
2206 """dump the contents of an index file"""
2206 """dump the contents of an index file"""
2207 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2207 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2208 format = opts.get('format', 0)
2208 format = opts.get('format', 0)
2209 if format not in (0, 1):
2209 if format not in (0, 1):
2210 raise util.Abort(_("unknown format %d") % format)
2210 raise util.Abort(_("unknown format %d") % format)
2211
2211
2212 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2212 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2213 if generaldelta:
2213 if generaldelta:
2214 basehdr = ' delta'
2214 basehdr = ' delta'
2215 else:
2215 else:
2216 basehdr = ' base'
2216 basehdr = ' base'
2217
2217
2218 if ui.debugflag:
2218 if ui.debugflag:
2219 shortfn = hex
2219 shortfn = hex
2220 else:
2220 else:
2221 shortfn = short
2221 shortfn = short
2222
2222
2223 # There might not be anything in r, so have a sane default
2223 # There might not be anything in r, so have a sane default
2224 idlen = 12
2224 idlen = 12
2225 for i in r:
2225 for i in r:
2226 idlen = len(shortfn(r.node(i)))
2226 idlen = len(shortfn(r.node(i)))
2227 break
2227 break
2228
2228
2229 if format == 0:
2229 if format == 0:
2230 ui.write(" rev offset length " + basehdr + " linkrev"
2230 ui.write(" rev offset length " + basehdr + " linkrev"
2231 " %s %s p2\n" % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2231 " %s %s p2\n" % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2232 elif format == 1:
2232 elif format == 1:
2233 ui.write(" rev flag offset length"
2233 ui.write(" rev flag offset length"
2234 " size " + basehdr + " link p1 p2"
2234 " size " + basehdr + " link p1 p2"
2235 " %s\n" % "nodeid".rjust(idlen))
2235 " %s\n" % "nodeid".rjust(idlen))
2236
2236
2237 for i in r:
2237 for i in r:
2238 node = r.node(i)
2238 node = r.node(i)
2239 if generaldelta:
2239 if generaldelta:
2240 base = r.deltaparent(i)
2240 base = r.deltaparent(i)
2241 else:
2241 else:
2242 base = r.chainbase(i)
2242 base = r.chainbase(i)
2243 if format == 0:
2243 if format == 0:
2244 try:
2244 try:
2245 pp = r.parents(node)
2245 pp = r.parents(node)
2246 except Exception:
2246 except Exception:
2247 pp = [nullid, nullid]
2247 pp = [nullid, nullid]
2248 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2248 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2249 i, r.start(i), r.length(i), base, r.linkrev(i),
2249 i, r.start(i), r.length(i), base, r.linkrev(i),
2250 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2250 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2251 elif format == 1:
2251 elif format == 1:
2252 pr = r.parentrevs(i)
2252 pr = r.parentrevs(i)
2253 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2253 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2254 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2254 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2255 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2255 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2256
2256
2257 @command('debugindexdot', [], _('FILE'), optionalrepo=True)
2257 @command('debugindexdot', [], _('FILE'), optionalrepo=True)
2258 def debugindexdot(ui, repo, file_):
2258 def debugindexdot(ui, repo, file_):
2259 """dump an index DAG as a graphviz dot file"""
2259 """dump an index DAG as a graphviz dot file"""
2260 r = None
2260 r = None
2261 if repo:
2261 if repo:
2262 filelog = repo.file(file_)
2262 filelog = repo.file(file_)
2263 if len(filelog):
2263 if len(filelog):
2264 r = filelog
2264 r = filelog
2265 if not r:
2265 if not r:
2266 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2266 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2267 ui.write(("digraph G {\n"))
2267 ui.write(("digraph G {\n"))
2268 for i in r:
2268 for i in r:
2269 node = r.node(i)
2269 node = r.node(i)
2270 pp = r.parents(node)
2270 pp = r.parents(node)
2271 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2271 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2272 if pp[1] != nullid:
2272 if pp[1] != nullid:
2273 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2273 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2274 ui.write("}\n")
2274 ui.write("}\n")
2275
2275
2276 @command('debuginstall', [], '', norepo=True)
2276 @command('debuginstall', [], '', norepo=True)
2277 def debuginstall(ui):
2277 def debuginstall(ui):
2278 '''test Mercurial installation
2278 '''test Mercurial installation
2279
2279
2280 Returns 0 on success.
2280 Returns 0 on success.
2281 '''
2281 '''
2282
2282
2283 def writetemp(contents):
2283 def writetemp(contents):
2284 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2284 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2285 f = os.fdopen(fd, "wb")
2285 f = os.fdopen(fd, "wb")
2286 f.write(contents)
2286 f.write(contents)
2287 f.close()
2287 f.close()
2288 return name
2288 return name
2289
2289
2290 problems = 0
2290 problems = 0
2291
2291
2292 # encoding
2292 # encoding
2293 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2293 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2294 try:
2294 try:
2295 encoding.fromlocal("test")
2295 encoding.fromlocal("test")
2296 except util.Abort, inst:
2296 except util.Abort, inst:
2297 ui.write(" %s\n" % inst)
2297 ui.write(" %s\n" % inst)
2298 ui.write(_(" (check that your locale is properly set)\n"))
2298 ui.write(_(" (check that your locale is properly set)\n"))
2299 problems += 1
2299 problems += 1
2300
2300
2301 # Python
2301 # Python
2302 ui.status(_("checking Python executable (%s)\n") % sys.executable)
2302 ui.status(_("checking Python executable (%s)\n") % sys.executable)
2303 ui.status(_("checking Python version (%s)\n")
2303 ui.status(_("checking Python version (%s)\n")
2304 % ("%s.%s.%s" % sys.version_info[:3]))
2304 % ("%s.%s.%s" % sys.version_info[:3]))
2305 ui.status(_("checking Python lib (%s)...\n")
2305 ui.status(_("checking Python lib (%s)...\n")
2306 % os.path.dirname(os.__file__))
2306 % os.path.dirname(os.__file__))
2307
2307
2308 # compiled modules
2308 # compiled modules
2309 ui.status(_("checking installed modules (%s)...\n")
2309 ui.status(_("checking installed modules (%s)...\n")
2310 % os.path.dirname(__file__))
2310 % os.path.dirname(__file__))
2311 try:
2311 try:
2312 import bdiff, mpatch, base85, osutil
2312 import bdiff, mpatch, base85, osutil
2313 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2313 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2314 except Exception, inst:
2314 except Exception, inst:
2315 ui.write(" %s\n" % inst)
2315 ui.write(" %s\n" % inst)
2316 ui.write(_(" One or more extensions could not be found"))
2316 ui.write(_(" One or more extensions could not be found"))
2317 ui.write(_(" (check that you compiled the extensions)\n"))
2317 ui.write(_(" (check that you compiled the extensions)\n"))
2318 problems += 1
2318 problems += 1
2319
2319
2320 # templates
2320 # templates
2321 import templater
2321 import templater
2322 p = templater.templatepaths()
2322 p = templater.templatepaths()
2323 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2323 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2324 if p:
2324 if p:
2325 m = templater.templatepath("map-cmdline.default")
2325 m = templater.templatepath("map-cmdline.default")
2326 if m:
2326 if m:
2327 # template found, check if it is working
2327 # template found, check if it is working
2328 try:
2328 try:
2329 templater.templater(m)
2329 templater.templater(m)
2330 except Exception, inst:
2330 except Exception, inst:
2331 ui.write(" %s\n" % inst)
2331 ui.write(" %s\n" % inst)
2332 p = None
2332 p = None
2333 else:
2333 else:
2334 ui.write(_(" template 'default' not found\n"))
2334 ui.write(_(" template 'default' not found\n"))
2335 p = None
2335 p = None
2336 else:
2336 else:
2337 ui.write(_(" no template directories found\n"))
2337 ui.write(_(" no template directories found\n"))
2338 if not p:
2338 if not p:
2339 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2339 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2340 problems += 1
2340 problems += 1
2341
2341
2342 # editor
2342 # editor
2343 ui.status(_("checking commit editor...\n"))
2343 ui.status(_("checking commit editor...\n"))
2344 editor = ui.geteditor()
2344 editor = ui.geteditor()
2345 cmdpath = util.findexe(shlex.split(editor)[0])
2345 cmdpath = util.findexe(shlex.split(editor)[0])
2346 if not cmdpath:
2346 if not cmdpath:
2347 if editor == 'vi':
2347 if editor == 'vi':
2348 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2348 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2349 ui.write(_(" (specify a commit editor in your configuration"
2349 ui.write(_(" (specify a commit editor in your configuration"
2350 " file)\n"))
2350 " file)\n"))
2351 else:
2351 else:
2352 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2352 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2353 ui.write(_(" (specify a commit editor in your configuration"
2353 ui.write(_(" (specify a commit editor in your configuration"
2354 " file)\n"))
2354 " file)\n"))
2355 problems += 1
2355 problems += 1
2356
2356
2357 # check username
2357 # check username
2358 ui.status(_("checking username...\n"))
2358 ui.status(_("checking username...\n"))
2359 try:
2359 try:
2360 ui.username()
2360 ui.username()
2361 except util.Abort, e:
2361 except util.Abort, e:
2362 ui.write(" %s\n" % e)
2362 ui.write(" %s\n" % e)
2363 ui.write(_(" (specify a username in your configuration file)\n"))
2363 ui.write(_(" (specify a username in your configuration file)\n"))
2364 problems += 1
2364 problems += 1
2365
2365
2366 if not problems:
2366 if not problems:
2367 ui.status(_("no problems detected\n"))
2367 ui.status(_("no problems detected\n"))
2368 else:
2368 else:
2369 ui.write(_("%s problems detected,"
2369 ui.write(_("%s problems detected,"
2370 " please check your install!\n") % problems)
2370 " please check your install!\n") % problems)
2371
2371
2372 return problems
2372 return problems
2373
2373
2374 @command('debugknown', [], _('REPO ID...'), norepo=True)
2374 @command('debugknown', [], _('REPO ID...'), norepo=True)
2375 def debugknown(ui, repopath, *ids, **opts):
2375 def debugknown(ui, repopath, *ids, **opts):
2376 """test whether node ids are known to a repo
2376 """test whether node ids are known to a repo
2377
2377
2378 Every ID must be a full-length hex node id string. Returns a list of 0s
2378 Every ID must be a full-length hex node id string. Returns a list of 0s
2379 and 1s indicating unknown/known.
2379 and 1s indicating unknown/known.
2380 """
2380 """
2381 repo = hg.peer(ui, opts, repopath)
2381 repo = hg.peer(ui, opts, repopath)
2382 if not repo.capable('known'):
2382 if not repo.capable('known'):
2383 raise util.Abort("known() not supported by target repository")
2383 raise util.Abort("known() not supported by target repository")
2384 flags = repo.known([bin(s) for s in ids])
2384 flags = repo.known([bin(s) for s in ids])
2385 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2385 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2386
2386
2387 @command('debuglabelcomplete', [], _('LABEL...'))
2387 @command('debuglabelcomplete', [], _('LABEL...'))
2388 def debuglabelcomplete(ui, repo, *args):
2388 def debuglabelcomplete(ui, repo, *args):
2389 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2389 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2390 debugnamecomplete(ui, repo, *args)
2390 debugnamecomplete(ui, repo, *args)
2391
2391
2392 @command('debugnamecomplete', [], _('NAME...'))
2392 @command('debugnamecomplete', [], _('NAME...'))
2393 def debugnamecomplete(ui, repo, *args):
2393 def debugnamecomplete(ui, repo, *args):
2394 '''complete "names" - tags, open branch names, bookmark names'''
2394 '''complete "names" - tags, open branch names, bookmark names'''
2395
2395
2396 names = set()
2396 names = set()
2397 # since we previously only listed open branches, we will handle that
2397 # since we previously only listed open branches, we will handle that
2398 # specially (after this for loop)
2398 # specially (after this for loop)
2399 for name, ns in repo.names.iteritems():
2399 for name, ns in repo.names.iteritems():
2400 if name != 'branches':
2400 if name != 'branches':
2401 names.update(ns.listnames(repo))
2401 names.update(ns.listnames(repo))
2402 names.update(tag for (tag, heads, tip, closed)
2402 names.update(tag for (tag, heads, tip, closed)
2403 in repo.branchmap().iterbranches() if not closed)
2403 in repo.branchmap().iterbranches() if not closed)
2404 completions = set()
2404 completions = set()
2405 if not args:
2405 if not args:
2406 args = ['']
2406 args = ['']
2407 for a in args:
2407 for a in args:
2408 completions.update(n for n in names if n.startswith(a))
2408 completions.update(n for n in names if n.startswith(a))
2409 ui.write('\n'.join(sorted(completions)))
2409 ui.write('\n'.join(sorted(completions)))
2410 ui.write('\n')
2410 ui.write('\n')
2411
2411
2412 @command('debuglocks',
2412 @command('debuglocks',
2413 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2413 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2414 ('W', 'force-wlock', None,
2414 ('W', 'force-wlock', None,
2415 _('free the working state lock (DANGEROUS)'))],
2415 _('free the working state lock (DANGEROUS)'))],
2416 _('[OPTION]...'))
2416 _('[OPTION]...'))
2417 def debuglocks(ui, repo, **opts):
2417 def debuglocks(ui, repo, **opts):
2418 """show or modify state of locks
2418 """show or modify state of locks
2419
2419
2420 By default, this command will show which locks are held. This
2420 By default, this command will show which locks are held. This
2421 includes the user and process holding the lock, the amount of time
2421 includes the user and process holding the lock, the amount of time
2422 the lock has been held, and the machine name where the process is
2422 the lock has been held, and the machine name where the process is
2423 running if it's not local.
2423 running if it's not local.
2424
2424
2425 Locks protect the integrity of Mercurial's data, so should be
2425 Locks protect the integrity of Mercurial's data, so should be
2426 treated with care. System crashes or other interruptions may cause
2426 treated with care. System crashes or other interruptions may cause
2427 locks to not be properly released, though Mercurial will usually
2427 locks to not be properly released, though Mercurial will usually
2428 detect and remove such stale locks automatically.
2428 detect and remove such stale locks automatically.
2429
2429
2430 However, detecting stale locks may not always be possible (for
2430 However, detecting stale locks may not always be possible (for
2431 instance, on a shared filesystem). Removing locks may also be
2431 instance, on a shared filesystem). Removing locks may also be
2432 blocked by filesystem permissions.
2432 blocked by filesystem permissions.
2433
2433
2434 Returns 0 if no locks are held.
2434 Returns 0 if no locks are held.
2435
2435
2436 """
2436 """
2437
2437
2438 if opts.get('force_lock'):
2438 if opts.get('force_lock'):
2439 repo.svfs.unlink('lock')
2439 repo.svfs.unlink('lock')
2440 if opts.get('force_wlock'):
2440 if opts.get('force_wlock'):
2441 repo.vfs.unlink('wlock')
2441 repo.vfs.unlink('wlock')
2442 if opts.get('force_lock') or opts.get('force_lock'):
2442 if opts.get('force_lock') or opts.get('force_lock'):
2443 return 0
2443 return 0
2444
2444
2445 now = time.time()
2445 now = time.time()
2446 held = 0
2446 held = 0
2447
2447
2448 def report(vfs, name, method):
2448 def report(vfs, name, method):
2449 # this causes stale locks to get reaped for more accurate reporting
2449 # this causes stale locks to get reaped for more accurate reporting
2450 try:
2450 try:
2451 l = method(False)
2451 l = method(False)
2452 except error.LockHeld:
2452 except error.LockHeld:
2453 l = None
2453 l = None
2454
2454
2455 if l:
2455 if l:
2456 l.release()
2456 l.release()
2457 else:
2457 else:
2458 try:
2458 try:
2459 stat = repo.svfs.lstat(name)
2459 stat = repo.svfs.lstat(name)
2460 age = now - stat.st_mtime
2460 age = now - stat.st_mtime
2461 user = util.username(stat.st_uid)
2461 user = util.username(stat.st_uid)
2462 locker = vfs.readlock(name)
2462 locker = vfs.readlock(name)
2463 if ":" in locker:
2463 if ":" in locker:
2464 host, pid = locker.split(':')
2464 host, pid = locker.split(':')
2465 if host == socket.gethostname():
2465 if host == socket.gethostname():
2466 locker = 'user %s, process %s' % (user, pid)
2466 locker = 'user %s, process %s' % (user, pid)
2467 else:
2467 else:
2468 locker = 'user %s, process %s, host %s' \
2468 locker = 'user %s, process %s, host %s' \
2469 % (user, pid, host)
2469 % (user, pid, host)
2470 ui.write("%-6s %s (%ds)\n" % (name + ":", locker, age))
2470 ui.write("%-6s %s (%ds)\n" % (name + ":", locker, age))
2471 return 1
2471 return 1
2472 except OSError, e:
2472 except OSError, e:
2473 if e.errno != errno.ENOENT:
2473 if e.errno != errno.ENOENT:
2474 raise
2474 raise
2475
2475
2476 ui.write("%-6s free\n" % (name + ":"))
2476 ui.write("%-6s free\n" % (name + ":"))
2477 return 0
2477 return 0
2478
2478
2479 held += report(repo.svfs, "lock", repo.lock)
2479 held += report(repo.svfs, "lock", repo.lock)
2480 held += report(repo.vfs, "wlock", repo.wlock)
2480 held += report(repo.vfs, "wlock", repo.wlock)
2481
2481
2482 return held
2482 return held
2483
2483
2484 @command('debugobsolete',
2484 @command('debugobsolete',
2485 [('', 'flags', 0, _('markers flag')),
2485 [('', 'flags', 0, _('markers flag')),
2486 ('', 'record-parents', False,
2486 ('', 'record-parents', False,
2487 _('record parent information for the precursor')),
2487 _('record parent information for the precursor')),
2488 ('r', 'rev', [], _('display markers relevant to REV')),
2488 ('r', 'rev', [], _('display markers relevant to REV')),
2489 ] + commitopts2,
2489 ] + commitopts2,
2490 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2490 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2491 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2491 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2492 """create arbitrary obsolete marker
2492 """create arbitrary obsolete marker
2493
2493
2494 With no arguments, displays the list of obsolescence markers."""
2494 With no arguments, displays the list of obsolescence markers."""
2495
2495
2496 def parsenodeid(s):
2496 def parsenodeid(s):
2497 try:
2497 try:
2498 # We do not use revsingle/revrange functions here to accept
2498 # We do not use revsingle/revrange functions here to accept
2499 # arbitrary node identifiers, possibly not present in the
2499 # arbitrary node identifiers, possibly not present in the
2500 # local repository.
2500 # local repository.
2501 n = bin(s)
2501 n = bin(s)
2502 if len(n) != len(nullid):
2502 if len(n) != len(nullid):
2503 raise TypeError()
2503 raise TypeError()
2504 return n
2504 return n
2505 except TypeError:
2505 except TypeError:
2506 raise util.Abort('changeset references must be full hexadecimal '
2506 raise util.Abort('changeset references must be full hexadecimal '
2507 'node identifiers')
2507 'node identifiers')
2508
2508
2509 if precursor is not None:
2509 if precursor is not None:
2510 if opts['rev']:
2510 if opts['rev']:
2511 raise util.Abort('cannot select revision when creating marker')
2511 raise util.Abort('cannot select revision when creating marker')
2512 metadata = {}
2512 metadata = {}
2513 metadata['user'] = opts['user'] or ui.username()
2513 metadata['user'] = opts['user'] or ui.username()
2514 succs = tuple(parsenodeid(succ) for succ in successors)
2514 succs = tuple(parsenodeid(succ) for succ in successors)
2515 l = repo.lock()
2515 l = repo.lock()
2516 try:
2516 try:
2517 tr = repo.transaction('debugobsolete')
2517 tr = repo.transaction('debugobsolete')
2518 try:
2518 try:
2519 try:
2519 try:
2520 date = opts.get('date')
2520 date = opts.get('date')
2521 if date:
2521 if date:
2522 date = util.parsedate(date)
2522 date = util.parsedate(date)
2523 else:
2523 else:
2524 date = None
2524 date = None
2525 prec = parsenodeid(precursor)
2525 prec = parsenodeid(precursor)
2526 parents = None
2526 parents = None
2527 if opts['record_parents']:
2527 if opts['record_parents']:
2528 if prec not in repo.unfiltered():
2528 if prec not in repo.unfiltered():
2529 raise util.Abort('cannot used --record-parents on '
2529 raise util.Abort('cannot used --record-parents on '
2530 'unknown changesets')
2530 'unknown changesets')
2531 parents = repo.unfiltered()[prec].parents()
2531 parents = repo.unfiltered()[prec].parents()
2532 parents = tuple(p.node() for p in parents)
2532 parents = tuple(p.node() for p in parents)
2533 repo.obsstore.create(tr, prec, succs, opts['flags'],
2533 repo.obsstore.create(tr, prec, succs, opts['flags'],
2534 parents=parents, date=date,
2534 parents=parents, date=date,
2535 metadata=metadata)
2535 metadata=metadata)
2536 tr.close()
2536 tr.close()
2537 except ValueError, exc:
2537 except ValueError, exc:
2538 raise util.Abort(_('bad obsmarker input: %s') % exc)
2538 raise util.Abort(_('bad obsmarker input: %s') % exc)
2539 finally:
2539 finally:
2540 tr.release()
2540 tr.release()
2541 finally:
2541 finally:
2542 l.release()
2542 l.release()
2543 else:
2543 else:
2544 if opts['rev']:
2544 if opts['rev']:
2545 revs = scmutil.revrange(repo, opts['rev'])
2545 revs = scmutil.revrange(repo, opts['rev'])
2546 nodes = [repo[r].node() for r in revs]
2546 nodes = [repo[r].node() for r in revs]
2547 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2547 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2548 markers.sort(key=lambda x: x._data)
2548 markers.sort(key=lambda x: x._data)
2549 else:
2549 else:
2550 markers = obsolete.getmarkers(repo)
2550 markers = obsolete.getmarkers(repo)
2551
2551
2552 for m in markers:
2552 for m in markers:
2553 cmdutil.showmarker(ui, m)
2553 cmdutil.showmarker(ui, m)
2554
2554
2555 @command('debugpathcomplete',
2555 @command('debugpathcomplete',
2556 [('f', 'full', None, _('complete an entire path')),
2556 [('f', 'full', None, _('complete an entire path')),
2557 ('n', 'normal', None, _('show only normal files')),
2557 ('n', 'normal', None, _('show only normal files')),
2558 ('a', 'added', None, _('show only added files')),
2558 ('a', 'added', None, _('show only added files')),
2559 ('r', 'removed', None, _('show only removed files'))],
2559 ('r', 'removed', None, _('show only removed files'))],
2560 _('FILESPEC...'))
2560 _('FILESPEC...'))
2561 def debugpathcomplete(ui, repo, *specs, **opts):
2561 def debugpathcomplete(ui, repo, *specs, **opts):
2562 '''complete part or all of a tracked path
2562 '''complete part or all of a tracked path
2563
2563
2564 This command supports shells that offer path name completion. It
2564 This command supports shells that offer path name completion. It
2565 currently completes only files already known to the dirstate.
2565 currently completes only files already known to the dirstate.
2566
2566
2567 Completion extends only to the next path segment unless
2567 Completion extends only to the next path segment unless
2568 --full is specified, in which case entire paths are used.'''
2568 --full is specified, in which case entire paths are used.'''
2569
2569
2570 def complete(path, acceptable):
2570 def complete(path, acceptable):
2571 dirstate = repo.dirstate
2571 dirstate = repo.dirstate
2572 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2572 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2573 rootdir = repo.root + os.sep
2573 rootdir = repo.root + os.sep
2574 if spec != repo.root and not spec.startswith(rootdir):
2574 if spec != repo.root and not spec.startswith(rootdir):
2575 return [], []
2575 return [], []
2576 if os.path.isdir(spec):
2576 if os.path.isdir(spec):
2577 spec += '/'
2577 spec += '/'
2578 spec = spec[len(rootdir):]
2578 spec = spec[len(rootdir):]
2579 fixpaths = os.sep != '/'
2579 fixpaths = os.sep != '/'
2580 if fixpaths:
2580 if fixpaths:
2581 spec = spec.replace(os.sep, '/')
2581 spec = spec.replace(os.sep, '/')
2582 speclen = len(spec)
2582 speclen = len(spec)
2583 fullpaths = opts['full']
2583 fullpaths = opts['full']
2584 files, dirs = set(), set()
2584 files, dirs = set(), set()
2585 adddir, addfile = dirs.add, files.add
2585 adddir, addfile = dirs.add, files.add
2586 for f, st in dirstate.iteritems():
2586 for f, st in dirstate.iteritems():
2587 if f.startswith(spec) and st[0] in acceptable:
2587 if f.startswith(spec) and st[0] in acceptable:
2588 if fixpaths:
2588 if fixpaths:
2589 f = f.replace('/', os.sep)
2589 f = f.replace('/', os.sep)
2590 if fullpaths:
2590 if fullpaths:
2591 addfile(f)
2591 addfile(f)
2592 continue
2592 continue
2593 s = f.find(os.sep, speclen)
2593 s = f.find(os.sep, speclen)
2594 if s >= 0:
2594 if s >= 0:
2595 adddir(f[:s])
2595 adddir(f[:s])
2596 else:
2596 else:
2597 addfile(f)
2597 addfile(f)
2598 return files, dirs
2598 return files, dirs
2599
2599
2600 acceptable = ''
2600 acceptable = ''
2601 if opts['normal']:
2601 if opts['normal']:
2602 acceptable += 'nm'
2602 acceptable += 'nm'
2603 if opts['added']:
2603 if opts['added']:
2604 acceptable += 'a'
2604 acceptable += 'a'
2605 if opts['removed']:
2605 if opts['removed']:
2606 acceptable += 'r'
2606 acceptable += 'r'
2607 cwd = repo.getcwd()
2607 cwd = repo.getcwd()
2608 if not specs:
2608 if not specs:
2609 specs = ['.']
2609 specs = ['.']
2610
2610
2611 files, dirs = set(), set()
2611 files, dirs = set(), set()
2612 for spec in specs:
2612 for spec in specs:
2613 f, d = complete(spec, acceptable or 'nmar')
2613 f, d = complete(spec, acceptable or 'nmar')
2614 files.update(f)
2614 files.update(f)
2615 dirs.update(d)
2615 dirs.update(d)
2616 files.update(dirs)
2616 files.update(dirs)
2617 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2617 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2618 ui.write('\n')
2618 ui.write('\n')
2619
2619
2620 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2620 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2621 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2621 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2622 '''access the pushkey key/value protocol
2622 '''access the pushkey key/value protocol
2623
2623
2624 With two args, list the keys in the given namespace.
2624 With two args, list the keys in the given namespace.
2625
2625
2626 With five args, set a key to new if it currently is set to old.
2626 With five args, set a key to new if it currently is set to old.
2627 Reports success or failure.
2627 Reports success or failure.
2628 '''
2628 '''
2629
2629
2630 target = hg.peer(ui, {}, repopath)
2630 target = hg.peer(ui, {}, repopath)
2631 if keyinfo:
2631 if keyinfo:
2632 key, old, new = keyinfo
2632 key, old, new = keyinfo
2633 r = target.pushkey(namespace, key, old, new)
2633 r = target.pushkey(namespace, key, old, new)
2634 ui.status(str(r) + '\n')
2634 ui.status(str(r) + '\n')
2635 return not r
2635 return not r
2636 else:
2636 else:
2637 for k, v in sorted(target.listkeys(namespace).iteritems()):
2637 for k, v in sorted(target.listkeys(namespace).iteritems()):
2638 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2638 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2639 v.encode('string-escape')))
2639 v.encode('string-escape')))
2640
2640
2641 @command('debugpvec', [], _('A B'))
2641 @command('debugpvec', [], _('A B'))
2642 def debugpvec(ui, repo, a, b=None):
2642 def debugpvec(ui, repo, a, b=None):
2643 ca = scmutil.revsingle(repo, a)
2643 ca = scmutil.revsingle(repo, a)
2644 cb = scmutil.revsingle(repo, b)
2644 cb = scmutil.revsingle(repo, b)
2645 pa = pvec.ctxpvec(ca)
2645 pa = pvec.ctxpvec(ca)
2646 pb = pvec.ctxpvec(cb)
2646 pb = pvec.ctxpvec(cb)
2647 if pa == pb:
2647 if pa == pb:
2648 rel = "="
2648 rel = "="
2649 elif pa > pb:
2649 elif pa > pb:
2650 rel = ">"
2650 rel = ">"
2651 elif pa < pb:
2651 elif pa < pb:
2652 rel = "<"
2652 rel = "<"
2653 elif pa | pb:
2653 elif pa | pb:
2654 rel = "|"
2654 rel = "|"
2655 ui.write(_("a: %s\n") % pa)
2655 ui.write(_("a: %s\n") % pa)
2656 ui.write(_("b: %s\n") % pb)
2656 ui.write(_("b: %s\n") % pb)
2657 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2657 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2658 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2658 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2659 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2659 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2660 pa.distance(pb), rel))
2660 pa.distance(pb), rel))
2661
2661
2662 @command('debugrebuilddirstate|debugrebuildstate',
2662 @command('debugrebuilddirstate|debugrebuildstate',
2663 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2663 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2664 _('[-r REV]'))
2664 _('[-r REV]'))
2665 def debugrebuilddirstate(ui, repo, rev):
2665 def debugrebuilddirstate(ui, repo, rev):
2666 """rebuild the dirstate as it would look like for the given revision
2666 """rebuild the dirstate as it would look like for the given revision
2667
2667
2668 If no revision is specified the first current parent will be used.
2668 If no revision is specified the first current parent will be used.
2669
2669
2670 The dirstate will be set to the files of the given revision.
2670 The dirstate will be set to the files of the given revision.
2671 The actual working directory content or existing dirstate
2671 The actual working directory content or existing dirstate
2672 information such as adds or removes is not considered.
2672 information such as adds or removes is not considered.
2673
2673
2674 One use of this command is to make the next :hg:`status` invocation
2674 One use of this command is to make the next :hg:`status` invocation
2675 check the actual file content.
2675 check the actual file content.
2676 """
2676 """
2677 ctx = scmutil.revsingle(repo, rev)
2677 ctx = scmutil.revsingle(repo, rev)
2678 wlock = repo.wlock()
2678 wlock = repo.wlock()
2679 try:
2679 try:
2680 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2680 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2681 finally:
2681 finally:
2682 wlock.release()
2682 wlock.release()
2683
2683
2684 @command('debugrename',
2684 @command('debugrename',
2685 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2685 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2686 _('[-r REV] FILE'))
2686 _('[-r REV] FILE'))
2687 def debugrename(ui, repo, file1, *pats, **opts):
2687 def debugrename(ui, repo, file1, *pats, **opts):
2688 """dump rename information"""
2688 """dump rename information"""
2689
2689
2690 ctx = scmutil.revsingle(repo, opts.get('rev'))
2690 ctx = scmutil.revsingle(repo, opts.get('rev'))
2691 m = scmutil.match(ctx, (file1,) + pats, opts)
2691 m = scmutil.match(ctx, (file1,) + pats, opts)
2692 for abs in ctx.walk(m):
2692 for abs in ctx.walk(m):
2693 fctx = ctx[abs]
2693 fctx = ctx[abs]
2694 o = fctx.filelog().renamed(fctx.filenode())
2694 o = fctx.filelog().renamed(fctx.filenode())
2695 rel = m.rel(abs)
2695 rel = m.rel(abs)
2696 if o:
2696 if o:
2697 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2697 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2698 else:
2698 else:
2699 ui.write(_("%s not renamed\n") % rel)
2699 ui.write(_("%s not renamed\n") % rel)
2700
2700
2701 @command('debugrevlog',
2701 @command('debugrevlog',
2702 [('c', 'changelog', False, _('open changelog')),
2702 [('c', 'changelog', False, _('open changelog')),
2703 ('m', 'manifest', False, _('open manifest')),
2703 ('m', 'manifest', False, _('open manifest')),
2704 ('d', 'dump', False, _('dump index data'))],
2704 ('d', 'dump', False, _('dump index data'))],
2705 _('-c|-m|FILE'),
2705 _('-c|-m|FILE'),
2706 optionalrepo=True)
2706 optionalrepo=True)
2707 def debugrevlog(ui, repo, file_=None, **opts):
2707 def debugrevlog(ui, repo, file_=None, **opts):
2708 """show data and statistics about a revlog"""
2708 """show data and statistics about a revlog"""
2709 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2709 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2710
2710
2711 if opts.get("dump"):
2711 if opts.get("dump"):
2712 numrevs = len(r)
2712 numrevs = len(r)
2713 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2713 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2714 " rawsize totalsize compression heads chainlen\n")
2714 " rawsize totalsize compression heads chainlen\n")
2715 ts = 0
2715 ts = 0
2716 heads = set()
2716 heads = set()
2717
2717
2718 for rev in xrange(numrevs):
2718 for rev in xrange(numrevs):
2719 dbase = r.deltaparent(rev)
2719 dbase = r.deltaparent(rev)
2720 if dbase == -1:
2720 if dbase == -1:
2721 dbase = rev
2721 dbase = rev
2722 cbase = r.chainbase(rev)
2722 cbase = r.chainbase(rev)
2723 clen = r.chainlen(rev)
2723 clen = r.chainlen(rev)
2724 p1, p2 = r.parentrevs(rev)
2724 p1, p2 = r.parentrevs(rev)
2725 rs = r.rawsize(rev)
2725 rs = r.rawsize(rev)
2726 ts = ts + rs
2726 ts = ts + rs
2727 heads -= set(r.parentrevs(rev))
2727 heads -= set(r.parentrevs(rev))
2728 heads.add(rev)
2728 heads.add(rev)
2729 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2729 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2730 "%11d %5d %8d\n" %
2730 "%11d %5d %8d\n" %
2731 (rev, p1, p2, r.start(rev), r.end(rev),
2731 (rev, p1, p2, r.start(rev), r.end(rev),
2732 r.start(dbase), r.start(cbase),
2732 r.start(dbase), r.start(cbase),
2733 r.start(p1), r.start(p2),
2733 r.start(p1), r.start(p2),
2734 rs, ts, ts / r.end(rev), len(heads), clen))
2734 rs, ts, ts / r.end(rev), len(heads), clen))
2735 return 0
2735 return 0
2736
2736
2737 v = r.version
2737 v = r.version
2738 format = v & 0xFFFF
2738 format = v & 0xFFFF
2739 flags = []
2739 flags = []
2740 gdelta = False
2740 gdelta = False
2741 if v & revlog.REVLOGNGINLINEDATA:
2741 if v & revlog.REVLOGNGINLINEDATA:
2742 flags.append('inline')
2742 flags.append('inline')
2743 if v & revlog.REVLOGGENERALDELTA:
2743 if v & revlog.REVLOGGENERALDELTA:
2744 gdelta = True
2744 gdelta = True
2745 flags.append('generaldelta')
2745 flags.append('generaldelta')
2746 if not flags:
2746 if not flags:
2747 flags = ['(none)']
2747 flags = ['(none)']
2748
2748
2749 nummerges = 0
2749 nummerges = 0
2750 numfull = 0
2750 numfull = 0
2751 numprev = 0
2751 numprev = 0
2752 nump1 = 0
2752 nump1 = 0
2753 nump2 = 0
2753 nump2 = 0
2754 numother = 0
2754 numother = 0
2755 nump1prev = 0
2755 nump1prev = 0
2756 nump2prev = 0
2756 nump2prev = 0
2757 chainlengths = []
2757 chainlengths = []
2758
2758
2759 datasize = [None, 0, 0L]
2759 datasize = [None, 0, 0L]
2760 fullsize = [None, 0, 0L]
2760 fullsize = [None, 0, 0L]
2761 deltasize = [None, 0, 0L]
2761 deltasize = [None, 0, 0L]
2762
2762
2763 def addsize(size, l):
2763 def addsize(size, l):
2764 if l[0] is None or size < l[0]:
2764 if l[0] is None or size < l[0]:
2765 l[0] = size
2765 l[0] = size
2766 if size > l[1]:
2766 if size > l[1]:
2767 l[1] = size
2767 l[1] = size
2768 l[2] += size
2768 l[2] += size
2769
2769
2770 numrevs = len(r)
2770 numrevs = len(r)
2771 for rev in xrange(numrevs):
2771 for rev in xrange(numrevs):
2772 p1, p2 = r.parentrevs(rev)
2772 p1, p2 = r.parentrevs(rev)
2773 delta = r.deltaparent(rev)
2773 delta = r.deltaparent(rev)
2774 if format > 0:
2774 if format > 0:
2775 addsize(r.rawsize(rev), datasize)
2775 addsize(r.rawsize(rev), datasize)
2776 if p2 != nullrev:
2776 if p2 != nullrev:
2777 nummerges += 1
2777 nummerges += 1
2778 size = r.length(rev)
2778 size = r.length(rev)
2779 if delta == nullrev:
2779 if delta == nullrev:
2780 chainlengths.append(0)
2780 chainlengths.append(0)
2781 numfull += 1
2781 numfull += 1
2782 addsize(size, fullsize)
2782 addsize(size, fullsize)
2783 else:
2783 else:
2784 chainlengths.append(chainlengths[delta] + 1)
2784 chainlengths.append(chainlengths[delta] + 1)
2785 addsize(size, deltasize)
2785 addsize(size, deltasize)
2786 if delta == rev - 1:
2786 if delta == rev - 1:
2787 numprev += 1
2787 numprev += 1
2788 if delta == p1:
2788 if delta == p1:
2789 nump1prev += 1
2789 nump1prev += 1
2790 elif delta == p2:
2790 elif delta == p2:
2791 nump2prev += 1
2791 nump2prev += 1
2792 elif delta == p1:
2792 elif delta == p1:
2793 nump1 += 1
2793 nump1 += 1
2794 elif delta == p2:
2794 elif delta == p2:
2795 nump2 += 1
2795 nump2 += 1
2796 elif delta != nullrev:
2796 elif delta != nullrev:
2797 numother += 1
2797 numother += 1
2798
2798
2799 # Adjust size min value for empty cases
2799 # Adjust size min value for empty cases
2800 for size in (datasize, fullsize, deltasize):
2800 for size in (datasize, fullsize, deltasize):
2801 if size[0] is None:
2801 if size[0] is None:
2802 size[0] = 0
2802 size[0] = 0
2803
2803
2804 numdeltas = numrevs - numfull
2804 numdeltas = numrevs - numfull
2805 numoprev = numprev - nump1prev - nump2prev
2805 numoprev = numprev - nump1prev - nump2prev
2806 totalrawsize = datasize[2]
2806 totalrawsize = datasize[2]
2807 datasize[2] /= numrevs
2807 datasize[2] /= numrevs
2808 fulltotal = fullsize[2]
2808 fulltotal = fullsize[2]
2809 fullsize[2] /= numfull
2809 fullsize[2] /= numfull
2810 deltatotal = deltasize[2]
2810 deltatotal = deltasize[2]
2811 if numrevs - numfull > 0:
2811 if numrevs - numfull > 0:
2812 deltasize[2] /= numrevs - numfull
2812 deltasize[2] /= numrevs - numfull
2813 totalsize = fulltotal + deltatotal
2813 totalsize = fulltotal + deltatotal
2814 avgchainlen = sum(chainlengths) / numrevs
2814 avgchainlen = sum(chainlengths) / numrevs
2815 compratio = totalrawsize / totalsize
2815 compratio = totalrawsize / totalsize
2816
2816
2817 basedfmtstr = '%%%dd\n'
2817 basedfmtstr = '%%%dd\n'
2818 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2818 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2819
2819
2820 def dfmtstr(max):
2820 def dfmtstr(max):
2821 return basedfmtstr % len(str(max))
2821 return basedfmtstr % len(str(max))
2822 def pcfmtstr(max, padding=0):
2822 def pcfmtstr(max, padding=0):
2823 return basepcfmtstr % (len(str(max)), ' ' * padding)
2823 return basepcfmtstr % (len(str(max)), ' ' * padding)
2824
2824
2825 def pcfmt(value, total):
2825 def pcfmt(value, total):
2826 return (value, 100 * float(value) / total)
2826 return (value, 100 * float(value) / total)
2827
2827
2828 ui.write(('format : %d\n') % format)
2828 ui.write(('format : %d\n') % format)
2829 ui.write(('flags : %s\n') % ', '.join(flags))
2829 ui.write(('flags : %s\n') % ', '.join(flags))
2830
2830
2831 ui.write('\n')
2831 ui.write('\n')
2832 fmt = pcfmtstr(totalsize)
2832 fmt = pcfmtstr(totalsize)
2833 fmt2 = dfmtstr(totalsize)
2833 fmt2 = dfmtstr(totalsize)
2834 ui.write(('revisions : ') + fmt2 % numrevs)
2834 ui.write(('revisions : ') + fmt2 % numrevs)
2835 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2835 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2836 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2836 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2837 ui.write(('revisions : ') + fmt2 % numrevs)
2837 ui.write(('revisions : ') + fmt2 % numrevs)
2838 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2838 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2839 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2839 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2840 ui.write(('revision size : ') + fmt2 % totalsize)
2840 ui.write(('revision size : ') + fmt2 % totalsize)
2841 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2841 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2842 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2842 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2843
2843
2844 ui.write('\n')
2844 ui.write('\n')
2845 fmt = dfmtstr(max(avgchainlen, compratio))
2845 fmt = dfmtstr(max(avgchainlen, compratio))
2846 ui.write(('avg chain length : ') + fmt % avgchainlen)
2846 ui.write(('avg chain length : ') + fmt % avgchainlen)
2847 ui.write(('compression ratio : ') + fmt % compratio)
2847 ui.write(('compression ratio : ') + fmt % compratio)
2848
2848
2849 if format > 0:
2849 if format > 0:
2850 ui.write('\n')
2850 ui.write('\n')
2851 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2851 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2852 % tuple(datasize))
2852 % tuple(datasize))
2853 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2853 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2854 % tuple(fullsize))
2854 % tuple(fullsize))
2855 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2855 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2856 % tuple(deltasize))
2856 % tuple(deltasize))
2857
2857
2858 if numdeltas > 0:
2858 if numdeltas > 0:
2859 ui.write('\n')
2859 ui.write('\n')
2860 fmt = pcfmtstr(numdeltas)
2860 fmt = pcfmtstr(numdeltas)
2861 fmt2 = pcfmtstr(numdeltas, 4)
2861 fmt2 = pcfmtstr(numdeltas, 4)
2862 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2862 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2863 if numprev > 0:
2863 if numprev > 0:
2864 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2864 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2865 numprev))
2865 numprev))
2866 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2866 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2867 numprev))
2867 numprev))
2868 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2868 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2869 numprev))
2869 numprev))
2870 if gdelta:
2870 if gdelta:
2871 ui.write(('deltas against p1 : ')
2871 ui.write(('deltas against p1 : ')
2872 + fmt % pcfmt(nump1, numdeltas))
2872 + fmt % pcfmt(nump1, numdeltas))
2873 ui.write(('deltas against p2 : ')
2873 ui.write(('deltas against p2 : ')
2874 + fmt % pcfmt(nump2, numdeltas))
2874 + fmt % pcfmt(nump2, numdeltas))
2875 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2875 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2876 numdeltas))
2876 numdeltas))
2877
2877
2878 @command('debugrevspec',
2878 @command('debugrevspec',
2879 [('', 'optimize', None, _('print parsed tree after optimizing'))],
2879 [('', 'optimize', None, _('print parsed tree after optimizing'))],
2880 ('REVSPEC'))
2880 ('REVSPEC'))
2881 def debugrevspec(ui, repo, expr, **opts):
2881 def debugrevspec(ui, repo, expr, **opts):
2882 """parse and apply a revision specification
2882 """parse and apply a revision specification
2883
2883
2884 Use --verbose to print the parsed tree before and after aliases
2884 Use --verbose to print the parsed tree before and after aliases
2885 expansion.
2885 expansion.
2886 """
2886 """
2887 if ui.verbose:
2887 if ui.verbose:
2888 tree = revset.parse(expr)[0]
2888 tree = revset.parse(expr)[0]
2889 ui.note(revset.prettyformat(tree), "\n")
2889 ui.note(revset.prettyformat(tree), "\n")
2890 newtree = revset.findaliases(ui, tree)
2890 newtree = revset.findaliases(ui, tree)
2891 if newtree != tree:
2891 if newtree != tree:
2892 ui.note(revset.prettyformat(newtree), "\n")
2892 ui.note(revset.prettyformat(newtree), "\n")
2893 tree = newtree
2893 tree = newtree
2894 newtree = revset.foldconcat(tree)
2894 newtree = revset.foldconcat(tree)
2895 if newtree != tree:
2895 if newtree != tree:
2896 ui.note(revset.prettyformat(newtree), "\n")
2896 ui.note(revset.prettyformat(newtree), "\n")
2897 if opts["optimize"]:
2897 if opts["optimize"]:
2898 weight, optimizedtree = revset.optimize(newtree, True)
2898 weight, optimizedtree = revset.optimize(newtree, True)
2899 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
2899 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
2900 func = revset.match(ui, expr)
2900 func = revset.match(ui, expr)
2901 for c in func(repo):
2901 for c in func(repo):
2902 ui.write("%s\n" % c)
2902 ui.write("%s\n" % c)
2903
2903
2904 @command('debugsetparents', [], _('REV1 [REV2]'))
2904 @command('debugsetparents', [], _('REV1 [REV2]'))
2905 def debugsetparents(ui, repo, rev1, rev2=None):
2905 def debugsetparents(ui, repo, rev1, rev2=None):
2906 """manually set the parents of the current working directory
2906 """manually set the parents of the current working directory
2907
2907
2908 This is useful for writing repository conversion tools, but should
2908 This is useful for writing repository conversion tools, but should
2909 be used with care. For example, neither the working directory nor the
2909 be used with care. For example, neither the working directory nor the
2910 dirstate is updated, so file status may be incorrect after running this
2910 dirstate is updated, so file status may be incorrect after running this
2911 command.
2911 command.
2912
2912
2913 Returns 0 on success.
2913 Returns 0 on success.
2914 """
2914 """
2915
2915
2916 r1 = scmutil.revsingle(repo, rev1).node()
2916 r1 = scmutil.revsingle(repo, rev1).node()
2917 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2917 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2918
2918
2919 wlock = repo.wlock()
2919 wlock = repo.wlock()
2920 try:
2920 try:
2921 repo.dirstate.beginparentchange()
2921 repo.dirstate.beginparentchange()
2922 repo.setparents(r1, r2)
2922 repo.setparents(r1, r2)
2923 repo.dirstate.endparentchange()
2923 repo.dirstate.endparentchange()
2924 finally:
2924 finally:
2925 wlock.release()
2925 wlock.release()
2926
2926
2927 @command('debugdirstate|debugstate',
2927 @command('debugdirstate|debugstate',
2928 [('', 'nodates', None, _('do not display the saved mtime')),
2928 [('', 'nodates', None, _('do not display the saved mtime')),
2929 ('', 'datesort', None, _('sort by saved mtime'))],
2929 ('', 'datesort', None, _('sort by saved mtime'))],
2930 _('[OPTION]...'))
2930 _('[OPTION]...'))
2931 def debugstate(ui, repo, nodates=None, datesort=None):
2931 def debugstate(ui, repo, nodates=None, datesort=None):
2932 """show the contents of the current dirstate"""
2932 """show the contents of the current dirstate"""
2933 timestr = ""
2933 timestr = ""
2934 if datesort:
2934 if datesort:
2935 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2935 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2936 else:
2936 else:
2937 keyfunc = None # sort by filename
2937 keyfunc = None # sort by filename
2938 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2938 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2939 if ent[3] == -1:
2939 if ent[3] == -1:
2940 timestr = 'unset '
2940 timestr = 'unset '
2941 elif nodates:
2941 elif nodates:
2942 timestr = 'set '
2942 timestr = 'set '
2943 else:
2943 else:
2944 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2944 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2945 time.localtime(ent[3]))
2945 time.localtime(ent[3]))
2946 if ent[1] & 020000:
2946 if ent[1] & 020000:
2947 mode = 'lnk'
2947 mode = 'lnk'
2948 else:
2948 else:
2949 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2949 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2950 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2950 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2951 for f in repo.dirstate.copies():
2951 for f in repo.dirstate.copies():
2952 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2952 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2953
2953
2954 @command('debugsub',
2954 @command('debugsub',
2955 [('r', 'rev', '',
2955 [('r', 'rev', '',
2956 _('revision to check'), _('REV'))],
2956 _('revision to check'), _('REV'))],
2957 _('[-r REV] [REV]'))
2957 _('[-r REV] [REV]'))
2958 def debugsub(ui, repo, rev=None):
2958 def debugsub(ui, repo, rev=None):
2959 ctx = scmutil.revsingle(repo, rev, None)
2959 ctx = scmutil.revsingle(repo, rev, None)
2960 for k, v in sorted(ctx.substate.items()):
2960 for k, v in sorted(ctx.substate.items()):
2961 ui.write(('path %s\n') % k)
2961 ui.write(('path %s\n') % k)
2962 ui.write((' source %s\n') % v[0])
2962 ui.write((' source %s\n') % v[0])
2963 ui.write((' revision %s\n') % v[1])
2963 ui.write((' revision %s\n') % v[1])
2964
2964
2965 @command('debugsuccessorssets',
2965 @command('debugsuccessorssets',
2966 [],
2966 [],
2967 _('[REV]'))
2967 _('[REV]'))
2968 def debugsuccessorssets(ui, repo, *revs):
2968 def debugsuccessorssets(ui, repo, *revs):
2969 """show set of successors for revision
2969 """show set of successors for revision
2970
2970
2971 A successors set of changeset A is a consistent group of revisions that
2971 A successors set of changeset A is a consistent group of revisions that
2972 succeed A. It contains non-obsolete changesets only.
2972 succeed A. It contains non-obsolete changesets only.
2973
2973
2974 In most cases a changeset A has a single successors set containing a single
2974 In most cases a changeset A has a single successors set containing a single
2975 successor (changeset A replaced by A').
2975 successor (changeset A replaced by A').
2976
2976
2977 A changeset that is made obsolete with no successors are called "pruned".
2977 A changeset that is made obsolete with no successors are called "pruned".
2978 Such changesets have no successors sets at all.
2978 Such changesets have no successors sets at all.
2979
2979
2980 A changeset that has been "split" will have a successors set containing
2980 A changeset that has been "split" will have a successors set containing
2981 more than one successor.
2981 more than one successor.
2982
2982
2983 A changeset that has been rewritten in multiple different ways is called
2983 A changeset that has been rewritten in multiple different ways is called
2984 "divergent". Such changesets have multiple successor sets (each of which
2984 "divergent". Such changesets have multiple successor sets (each of which
2985 may also be split, i.e. have multiple successors).
2985 may also be split, i.e. have multiple successors).
2986
2986
2987 Results are displayed as follows::
2987 Results are displayed as follows::
2988
2988
2989 <rev1>
2989 <rev1>
2990 <successors-1A>
2990 <successors-1A>
2991 <rev2>
2991 <rev2>
2992 <successors-2A>
2992 <successors-2A>
2993 <successors-2B1> <successors-2B2> <successors-2B3>
2993 <successors-2B1> <successors-2B2> <successors-2B3>
2994
2994
2995 Here rev2 has two possible (i.e. divergent) successors sets. The first
2995 Here rev2 has two possible (i.e. divergent) successors sets. The first
2996 holds one element, whereas the second holds three (i.e. the changeset has
2996 holds one element, whereas the second holds three (i.e. the changeset has
2997 been split).
2997 been split).
2998 """
2998 """
2999 # passed to successorssets caching computation from one call to another
2999 # passed to successorssets caching computation from one call to another
3000 cache = {}
3000 cache = {}
3001 ctx2str = str
3001 ctx2str = str
3002 node2str = short
3002 node2str = short
3003 if ui.debug():
3003 if ui.debug():
3004 def ctx2str(ctx):
3004 def ctx2str(ctx):
3005 return ctx.hex()
3005 return ctx.hex()
3006 node2str = hex
3006 node2str = hex
3007 for rev in scmutil.revrange(repo, revs):
3007 for rev in scmutil.revrange(repo, revs):
3008 ctx = repo[rev]
3008 ctx = repo[rev]
3009 ui.write('%s\n'% ctx2str(ctx))
3009 ui.write('%s\n'% ctx2str(ctx))
3010 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
3010 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
3011 if succsset:
3011 if succsset:
3012 ui.write(' ')
3012 ui.write(' ')
3013 ui.write(node2str(succsset[0]))
3013 ui.write(node2str(succsset[0]))
3014 for node in succsset[1:]:
3014 for node in succsset[1:]:
3015 ui.write(' ')
3015 ui.write(' ')
3016 ui.write(node2str(node))
3016 ui.write(node2str(node))
3017 ui.write('\n')
3017 ui.write('\n')
3018
3018
3019 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
3019 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
3020 def debugwalk(ui, repo, *pats, **opts):
3020 def debugwalk(ui, repo, *pats, **opts):
3021 """show how files match on given patterns"""
3021 """show how files match on given patterns"""
3022 m = scmutil.match(repo[None], pats, opts)
3022 m = scmutil.match(repo[None], pats, opts)
3023 items = list(repo.walk(m))
3023 items = list(repo.walk(m))
3024 if not items:
3024 if not items:
3025 return
3025 return
3026 f = lambda fn: fn
3026 f = lambda fn: fn
3027 if ui.configbool('ui', 'slash') and os.sep != '/':
3027 if ui.configbool('ui', 'slash') and os.sep != '/':
3028 f = lambda fn: util.normpath(fn)
3028 f = lambda fn: util.normpath(fn)
3029 fmt = 'f %%-%ds %%-%ds %%s' % (
3029 fmt = 'f %%-%ds %%-%ds %%s' % (
3030 max([len(abs) for abs in items]),
3030 max([len(abs) for abs in items]),
3031 max([len(m.rel(abs)) for abs in items]))
3031 max([len(m.rel(abs)) for abs in items]))
3032 for abs in items:
3032 for abs in items:
3033 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3033 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3034 ui.write("%s\n" % line.rstrip())
3034 ui.write("%s\n" % line.rstrip())
3035
3035
3036 @command('debugwireargs',
3036 @command('debugwireargs',
3037 [('', 'three', '', 'three'),
3037 [('', 'three', '', 'three'),
3038 ('', 'four', '', 'four'),
3038 ('', 'four', '', 'four'),
3039 ('', 'five', '', 'five'),
3039 ('', 'five', '', 'five'),
3040 ] + remoteopts,
3040 ] + remoteopts,
3041 _('REPO [OPTIONS]... [ONE [TWO]]'),
3041 _('REPO [OPTIONS]... [ONE [TWO]]'),
3042 norepo=True)
3042 norepo=True)
3043 def debugwireargs(ui, repopath, *vals, **opts):
3043 def debugwireargs(ui, repopath, *vals, **opts):
3044 repo = hg.peer(ui, opts, repopath)
3044 repo = hg.peer(ui, opts, repopath)
3045 for opt in remoteopts:
3045 for opt in remoteopts:
3046 del opts[opt[1]]
3046 del opts[opt[1]]
3047 args = {}
3047 args = {}
3048 for k, v in opts.iteritems():
3048 for k, v in opts.iteritems():
3049 if v:
3049 if v:
3050 args[k] = v
3050 args[k] = v
3051 # run twice to check that we don't mess up the stream for the next command
3051 # run twice to check that we don't mess up the stream for the next command
3052 res1 = repo.debugwireargs(*vals, **args)
3052 res1 = repo.debugwireargs(*vals, **args)
3053 res2 = repo.debugwireargs(*vals, **args)
3053 res2 = repo.debugwireargs(*vals, **args)
3054 ui.write("%s\n" % res1)
3054 ui.write("%s\n" % res1)
3055 if res1 != res2:
3055 if res1 != res2:
3056 ui.warn("%s\n" % res2)
3056 ui.warn("%s\n" % res2)
3057
3057
3058 @command('^diff',
3058 @command('^diff',
3059 [('r', 'rev', [], _('revision'), _('REV')),
3059 [('r', 'rev', [], _('revision'), _('REV')),
3060 ('c', 'change', '', _('change made by revision'), _('REV'))
3060 ('c', 'change', '', _('change made by revision'), _('REV'))
3061 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3061 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3062 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3062 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3063 inferrepo=True)
3063 inferrepo=True)
3064 def diff(ui, repo, *pats, **opts):
3064 def diff(ui, repo, *pats, **opts):
3065 """diff repository (or selected files)
3065 """diff repository (or selected files)
3066
3066
3067 Show differences between revisions for the specified files.
3067 Show differences between revisions for the specified files.
3068
3068
3069 Differences between files are shown using the unified diff format.
3069 Differences between files are shown using the unified diff format.
3070
3070
3071 .. note::
3071 .. note::
3072
3072
3073 diff may generate unexpected results for merges, as it will
3073 diff may generate unexpected results for merges, as it will
3074 default to comparing against the working directory's first
3074 default to comparing against the working directory's first
3075 parent changeset if no revisions are specified.
3075 parent changeset if no revisions are specified.
3076
3076
3077 When two revision arguments are given, then changes are shown
3077 When two revision arguments are given, then changes are shown
3078 between those revisions. If only one revision is specified then
3078 between those revisions. If only one revision is specified then
3079 that revision is compared to the working directory, and, when no
3079 that revision is compared to the working directory, and, when no
3080 revisions are specified, the working directory files are compared
3080 revisions are specified, the working directory files are compared
3081 to its parent.
3081 to its parent.
3082
3082
3083 Alternatively you can specify -c/--change with a revision to see
3083 Alternatively you can specify -c/--change with a revision to see
3084 the changes in that changeset relative to its first parent.
3084 the changes in that changeset relative to its first parent.
3085
3085
3086 Without the -a/--text option, diff will avoid generating diffs of
3086 Without the -a/--text option, diff will avoid generating diffs of
3087 files it detects as binary. With -a, diff will generate a diff
3087 files it detects as binary. With -a, diff will generate a diff
3088 anyway, probably with undesirable results.
3088 anyway, probably with undesirable results.
3089
3089
3090 Use the -g/--git option to generate diffs in the git extended diff
3090 Use the -g/--git option to generate diffs in the git extended diff
3091 format. For more information, read :hg:`help diffs`.
3091 format. For more information, read :hg:`help diffs`.
3092
3092
3093 .. container:: verbose
3093 .. container:: verbose
3094
3094
3095 Examples:
3095 Examples:
3096
3096
3097 - compare a file in the current working directory to its parent::
3097 - compare a file in the current working directory to its parent::
3098
3098
3099 hg diff foo.c
3099 hg diff foo.c
3100
3100
3101 - compare two historical versions of a directory, with rename info::
3101 - compare two historical versions of a directory, with rename info::
3102
3102
3103 hg diff --git -r 1.0:1.2 lib/
3103 hg diff --git -r 1.0:1.2 lib/
3104
3104
3105 - get change stats relative to the last change on some date::
3105 - get change stats relative to the last change on some date::
3106
3106
3107 hg diff --stat -r "date('may 2')"
3107 hg diff --stat -r "date('may 2')"
3108
3108
3109 - diff all newly-added files that contain a keyword::
3109 - diff all newly-added files that contain a keyword::
3110
3110
3111 hg diff "set:added() and grep(GNU)"
3111 hg diff "set:added() and grep(GNU)"
3112
3112
3113 - compare a revision and its parents::
3113 - compare a revision and its parents::
3114
3114
3115 hg diff -c 9353 # compare against first parent
3115 hg diff -c 9353 # compare against first parent
3116 hg diff -r 9353^:9353 # same using revset syntax
3116 hg diff -r 9353^:9353 # same using revset syntax
3117 hg diff -r 9353^2:9353 # compare against the second parent
3117 hg diff -r 9353^2:9353 # compare against the second parent
3118
3118
3119 Returns 0 on success.
3119 Returns 0 on success.
3120 """
3120 """
3121
3121
3122 revs = opts.get('rev')
3122 revs = opts.get('rev')
3123 change = opts.get('change')
3123 change = opts.get('change')
3124 stat = opts.get('stat')
3124 stat = opts.get('stat')
3125 reverse = opts.get('reverse')
3125 reverse = opts.get('reverse')
3126
3126
3127 if revs and change:
3127 if revs and change:
3128 msg = _('cannot specify --rev and --change at the same time')
3128 msg = _('cannot specify --rev and --change at the same time')
3129 raise util.Abort(msg)
3129 raise util.Abort(msg)
3130 elif change:
3130 elif change:
3131 node2 = scmutil.revsingle(repo, change, None).node()
3131 node2 = scmutil.revsingle(repo, change, None).node()
3132 node1 = repo[node2].p1().node()
3132 node1 = repo[node2].p1().node()
3133 else:
3133 else:
3134 node1, node2 = scmutil.revpair(repo, revs)
3134 node1, node2 = scmutil.revpair(repo, revs)
3135
3135
3136 if reverse:
3136 if reverse:
3137 node1, node2 = node2, node1
3137 node1, node2 = node2, node1
3138
3138
3139 diffopts = patch.diffallopts(ui, opts)
3139 diffopts = patch.diffallopts(ui, opts)
3140 m = scmutil.match(repo[node2], pats, opts)
3140 m = scmutil.match(repo[node2], pats, opts)
3141 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3141 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3142 listsubrepos=opts.get('subrepos'))
3142 listsubrepos=opts.get('subrepos'))
3143
3143
3144 @command('^export',
3144 @command('^export',
3145 [('o', 'output', '',
3145 [('o', 'output', '',
3146 _('print output to file with formatted name'), _('FORMAT')),
3146 _('print output to file with formatted name'), _('FORMAT')),
3147 ('', 'switch-parent', None, _('diff against the second parent')),
3147 ('', 'switch-parent', None, _('diff against the second parent')),
3148 ('r', 'rev', [], _('revisions to export'), _('REV')),
3148 ('r', 'rev', [], _('revisions to export'), _('REV')),
3149 ] + diffopts,
3149 ] + diffopts,
3150 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3150 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3151 def export(ui, repo, *changesets, **opts):
3151 def export(ui, repo, *changesets, **opts):
3152 """dump the header and diffs for one or more changesets
3152 """dump the header and diffs for one or more changesets
3153
3153
3154 Print the changeset header and diffs for one or more revisions.
3154 Print the changeset header and diffs for one or more revisions.
3155 If no revision is given, the parent of the working directory is used.
3155 If no revision is given, the parent of the working directory is used.
3156
3156
3157 The information shown in the changeset header is: author, date,
3157 The information shown in the changeset header is: author, date,
3158 branch name (if non-default), changeset hash, parent(s) and commit
3158 branch name (if non-default), changeset hash, parent(s) and commit
3159 comment.
3159 comment.
3160
3160
3161 .. note::
3161 .. note::
3162
3162
3163 export may generate unexpected diff output for merge
3163 export may generate unexpected diff output for merge
3164 changesets, as it will compare the merge changeset against its
3164 changesets, as it will compare the merge changeset against its
3165 first parent only.
3165 first parent only.
3166
3166
3167 Output may be to a file, in which case the name of the file is
3167 Output may be to a file, in which case the name of the file is
3168 given using a format string. The formatting rules are as follows:
3168 given using a format string. The formatting rules are as follows:
3169
3169
3170 :``%%``: literal "%" character
3170 :``%%``: literal "%" character
3171 :``%H``: changeset hash (40 hexadecimal digits)
3171 :``%H``: changeset hash (40 hexadecimal digits)
3172 :``%N``: number of patches being generated
3172 :``%N``: number of patches being generated
3173 :``%R``: changeset revision number
3173 :``%R``: changeset revision number
3174 :``%b``: basename of the exporting repository
3174 :``%b``: basename of the exporting repository
3175 :``%h``: short-form changeset hash (12 hexadecimal digits)
3175 :``%h``: short-form changeset hash (12 hexadecimal digits)
3176 :``%m``: first line of the commit message (only alphanumeric characters)
3176 :``%m``: first line of the commit message (only alphanumeric characters)
3177 :``%n``: zero-padded sequence number, starting at 1
3177 :``%n``: zero-padded sequence number, starting at 1
3178 :``%r``: zero-padded changeset revision number
3178 :``%r``: zero-padded changeset revision number
3179
3179
3180 Without the -a/--text option, export will avoid generating diffs
3180 Without the -a/--text option, export will avoid generating diffs
3181 of files it detects as binary. With -a, export will generate a
3181 of files it detects as binary. With -a, export will generate a
3182 diff anyway, probably with undesirable results.
3182 diff anyway, probably with undesirable results.
3183
3183
3184 Use the -g/--git option to generate diffs in the git extended diff
3184 Use the -g/--git option to generate diffs in the git extended diff
3185 format. See :hg:`help diffs` for more information.
3185 format. See :hg:`help diffs` for more information.
3186
3186
3187 With the --switch-parent option, the diff will be against the
3187 With the --switch-parent option, the diff will be against the
3188 second parent. It can be useful to review a merge.
3188 second parent. It can be useful to review a merge.
3189
3189
3190 .. container:: verbose
3190 .. container:: verbose
3191
3191
3192 Examples:
3192 Examples:
3193
3193
3194 - use export and import to transplant a bugfix to the current
3194 - use export and import to transplant a bugfix to the current
3195 branch::
3195 branch::
3196
3196
3197 hg export -r 9353 | hg import -
3197 hg export -r 9353 | hg import -
3198
3198
3199 - export all the changesets between two revisions to a file with
3199 - export all the changesets between two revisions to a file with
3200 rename information::
3200 rename information::
3201
3201
3202 hg export --git -r 123:150 > changes.txt
3202 hg export --git -r 123:150 > changes.txt
3203
3203
3204 - split outgoing changes into a series of patches with
3204 - split outgoing changes into a series of patches with
3205 descriptive names::
3205 descriptive names::
3206
3206
3207 hg export -r "outgoing()" -o "%n-%m.patch"
3207 hg export -r "outgoing()" -o "%n-%m.patch"
3208
3208
3209 Returns 0 on success.
3209 Returns 0 on success.
3210 """
3210 """
3211 changesets += tuple(opts.get('rev', []))
3211 changesets += tuple(opts.get('rev', []))
3212 if not changesets:
3212 if not changesets:
3213 changesets = ['.']
3213 changesets = ['.']
3214 revs = scmutil.revrange(repo, changesets)
3214 revs = scmutil.revrange(repo, changesets)
3215 if not revs:
3215 if not revs:
3216 raise util.Abort(_("export requires at least one changeset"))
3216 raise util.Abort(_("export requires at least one changeset"))
3217 if len(revs) > 1:
3217 if len(revs) > 1:
3218 ui.note(_('exporting patches:\n'))
3218 ui.note(_('exporting patches:\n'))
3219 else:
3219 else:
3220 ui.note(_('exporting patch:\n'))
3220 ui.note(_('exporting patch:\n'))
3221 cmdutil.export(repo, revs, template=opts.get('output'),
3221 cmdutil.export(repo, revs, template=opts.get('output'),
3222 switch_parent=opts.get('switch_parent'),
3222 switch_parent=opts.get('switch_parent'),
3223 opts=patch.diffallopts(ui, opts))
3223 opts=patch.diffallopts(ui, opts))
3224
3224
3225 @command('files',
3225 @command('files',
3226 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3226 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3227 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3227 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3228 ] + walkopts + formatteropts,
3228 ] + walkopts + formatteropts + subrepoopts,
3229 _('[OPTION]... [PATTERN]...'))
3229 _('[OPTION]... [PATTERN]...'))
3230 def files(ui, repo, *pats, **opts):
3230 def files(ui, repo, *pats, **opts):
3231 """list tracked files
3231 """list tracked files
3232
3232
3233 Print files under Mercurial control in the working directory or
3233 Print files under Mercurial control in the working directory or
3234 specified revision whose names match the given patterns (excluding
3234 specified revision whose names match the given patterns (excluding
3235 removed files).
3235 removed files).
3236
3236
3237 If no patterns are given to match, this command prints the names
3237 If no patterns are given to match, this command prints the names
3238 of all files under Mercurial control in the working directory.
3238 of all files under Mercurial control in the working directory.
3239
3239
3240 .. container:: verbose
3240 .. container:: verbose
3241
3241
3242 Examples:
3242 Examples:
3243
3243
3244 - list all files under the current directory::
3244 - list all files under the current directory::
3245
3245
3246 hg files .
3246 hg files .
3247
3247
3248 - shows sizes and flags for current revision::
3248 - shows sizes and flags for current revision::
3249
3249
3250 hg files -vr .
3250 hg files -vr .
3251
3251
3252 - list all files named README::
3252 - list all files named README::
3253
3253
3254 hg files -I "**/README"
3254 hg files -I "**/README"
3255
3255
3256 - list all binary files::
3256 - list all binary files::
3257
3257
3258 hg files "set:binary()"
3258 hg files "set:binary()"
3259
3259
3260 - find files containing a regular expression::
3260 - find files containing a regular expression::
3261
3261
3262 hg files "set:grep('bob')"
3262 hg files "set:grep('bob')"
3263
3263
3264 - search tracked file contents with xargs and grep::
3264 - search tracked file contents with xargs and grep::
3265
3265
3266 hg files -0 | xargs -0 grep foo
3266 hg files -0 | xargs -0 grep foo
3267
3267
3268 See :hg:`help patterns` and :hg:`help filesets` for more information
3268 See :hg:`help patterns` and :hg:`help filesets` for more information
3269 on specifying file patterns.
3269 on specifying file patterns.
3270
3270
3271 Returns 0 if a match is found, 1 otherwise.
3271 Returns 0 if a match is found, 1 otherwise.
3272
3272
3273 """
3273 """
3274 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3274 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3275
3275
3276 end = '\n'
3276 end = '\n'
3277 if opts.get('print0'):
3277 if opts.get('print0'):
3278 end = '\0'
3278 end = '\0'
3279 fm = ui.formatter('files', opts)
3279 fm = ui.formatter('files', opts)
3280 fmt = '%s' + end
3280 fmt = '%s' + end
3281
3281
3282 m = scmutil.match(ctx, pats, opts)
3282 m = scmutil.match(ctx, pats, opts)
3283 ret = cmdutil.files(ui, ctx, m, fm, fmt)
3283 ret = cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
3284
3284
3285 fm.end()
3285 fm.end()
3286
3286
3287 return ret
3287 return ret
3288
3288
3289 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3289 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3290 def forget(ui, repo, *pats, **opts):
3290 def forget(ui, repo, *pats, **opts):
3291 """forget the specified files on the next commit
3291 """forget the specified files on the next commit
3292
3292
3293 Mark the specified files so they will no longer be tracked
3293 Mark the specified files so they will no longer be tracked
3294 after the next commit.
3294 after the next commit.
3295
3295
3296 This only removes files from the current branch, not from the
3296 This only removes files from the current branch, not from the
3297 entire project history, and it does not delete them from the
3297 entire project history, and it does not delete them from the
3298 working directory.
3298 working directory.
3299
3299
3300 To undo a forget before the next commit, see :hg:`add`.
3300 To undo a forget before the next commit, see :hg:`add`.
3301
3301
3302 .. container:: verbose
3302 .. container:: verbose
3303
3303
3304 Examples:
3304 Examples:
3305
3305
3306 - forget newly-added binary files::
3306 - forget newly-added binary files::
3307
3307
3308 hg forget "set:added() and binary()"
3308 hg forget "set:added() and binary()"
3309
3309
3310 - forget files that would be excluded by .hgignore::
3310 - forget files that would be excluded by .hgignore::
3311
3311
3312 hg forget "set:hgignore()"
3312 hg forget "set:hgignore()"
3313
3313
3314 Returns 0 on success.
3314 Returns 0 on success.
3315 """
3315 """
3316
3316
3317 if not pats:
3317 if not pats:
3318 raise util.Abort(_('no files specified'))
3318 raise util.Abort(_('no files specified'))
3319
3319
3320 m = scmutil.match(repo[None], pats, opts)
3320 m = scmutil.match(repo[None], pats, opts)
3321 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3321 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3322 return rejected and 1 or 0
3322 return rejected and 1 or 0
3323
3323
3324 @command(
3324 @command(
3325 'graft',
3325 'graft',
3326 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3326 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3327 ('c', 'continue', False, _('resume interrupted graft')),
3327 ('c', 'continue', False, _('resume interrupted graft')),
3328 ('e', 'edit', False, _('invoke editor on commit messages')),
3328 ('e', 'edit', False, _('invoke editor on commit messages')),
3329 ('', 'log', None, _('append graft info to log message')),
3329 ('', 'log', None, _('append graft info to log message')),
3330 ('f', 'force', False, _('force graft')),
3330 ('f', 'force', False, _('force graft')),
3331 ('D', 'currentdate', False,
3331 ('D', 'currentdate', False,
3332 _('record the current date as commit date')),
3332 _('record the current date as commit date')),
3333 ('U', 'currentuser', False,
3333 ('U', 'currentuser', False,
3334 _('record the current user as committer'), _('DATE'))]
3334 _('record the current user as committer'), _('DATE'))]
3335 + commitopts2 + mergetoolopts + dryrunopts,
3335 + commitopts2 + mergetoolopts + dryrunopts,
3336 _('[OPTION]... [-r] REV...'))
3336 _('[OPTION]... [-r] REV...'))
3337 def graft(ui, repo, *revs, **opts):
3337 def graft(ui, repo, *revs, **opts):
3338 '''copy changes from other branches onto the current branch
3338 '''copy changes from other branches onto the current branch
3339
3339
3340 This command uses Mercurial's merge logic to copy individual
3340 This command uses Mercurial's merge logic to copy individual
3341 changes from other branches without merging branches in the
3341 changes from other branches without merging branches in the
3342 history graph. This is sometimes known as 'backporting' or
3342 history graph. This is sometimes known as 'backporting' or
3343 'cherry-picking'. By default, graft will copy user, date, and
3343 'cherry-picking'. By default, graft will copy user, date, and
3344 description from the source changesets.
3344 description from the source changesets.
3345
3345
3346 Changesets that are ancestors of the current revision, that have
3346 Changesets that are ancestors of the current revision, that have
3347 already been grafted, or that are merges will be skipped.
3347 already been grafted, or that are merges will be skipped.
3348
3348
3349 If --log is specified, log messages will have a comment appended
3349 If --log is specified, log messages will have a comment appended
3350 of the form::
3350 of the form::
3351
3351
3352 (grafted from CHANGESETHASH)
3352 (grafted from CHANGESETHASH)
3353
3353
3354 If --force is specified, revisions will be grafted even if they
3354 If --force is specified, revisions will be grafted even if they
3355 are already ancestors of or have been grafted to the destination.
3355 are already ancestors of or have been grafted to the destination.
3356 This is useful when the revisions have since been backed out.
3356 This is useful when the revisions have since been backed out.
3357
3357
3358 If a graft merge results in conflicts, the graft process is
3358 If a graft merge results in conflicts, the graft process is
3359 interrupted so that the current merge can be manually resolved.
3359 interrupted so that the current merge can be manually resolved.
3360 Once all conflicts are addressed, the graft process can be
3360 Once all conflicts are addressed, the graft process can be
3361 continued with the -c/--continue option.
3361 continued with the -c/--continue option.
3362
3362
3363 .. note::
3363 .. note::
3364
3364
3365 The -c/--continue option does not reapply earlier options, except
3365 The -c/--continue option does not reapply earlier options, except
3366 for --force.
3366 for --force.
3367
3367
3368 .. container:: verbose
3368 .. container:: verbose
3369
3369
3370 Examples:
3370 Examples:
3371
3371
3372 - copy a single change to the stable branch and edit its description::
3372 - copy a single change to the stable branch and edit its description::
3373
3373
3374 hg update stable
3374 hg update stable
3375 hg graft --edit 9393
3375 hg graft --edit 9393
3376
3376
3377 - graft a range of changesets with one exception, updating dates::
3377 - graft a range of changesets with one exception, updating dates::
3378
3378
3379 hg graft -D "2085::2093 and not 2091"
3379 hg graft -D "2085::2093 and not 2091"
3380
3380
3381 - continue a graft after resolving conflicts::
3381 - continue a graft after resolving conflicts::
3382
3382
3383 hg graft -c
3383 hg graft -c
3384
3384
3385 - show the source of a grafted changeset::
3385 - show the source of a grafted changeset::
3386
3386
3387 hg log --debug -r .
3387 hg log --debug -r .
3388
3388
3389 See :hg:`help revisions` and :hg:`help revsets` for more about
3389 See :hg:`help revisions` and :hg:`help revsets` for more about
3390 specifying revisions.
3390 specifying revisions.
3391
3391
3392 Returns 0 on successful completion.
3392 Returns 0 on successful completion.
3393 '''
3393 '''
3394
3394
3395 revs = list(revs)
3395 revs = list(revs)
3396 revs.extend(opts['rev'])
3396 revs.extend(opts['rev'])
3397
3397
3398 if not opts.get('user') and opts.get('currentuser'):
3398 if not opts.get('user') and opts.get('currentuser'):
3399 opts['user'] = ui.username()
3399 opts['user'] = ui.username()
3400 if not opts.get('date') and opts.get('currentdate'):
3400 if not opts.get('date') and opts.get('currentdate'):
3401 opts['date'] = "%d %d" % util.makedate()
3401 opts['date'] = "%d %d" % util.makedate()
3402
3402
3403 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3403 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3404
3404
3405 cont = False
3405 cont = False
3406 if opts['continue']:
3406 if opts['continue']:
3407 cont = True
3407 cont = True
3408 if revs:
3408 if revs:
3409 raise util.Abort(_("can't specify --continue and revisions"))
3409 raise util.Abort(_("can't specify --continue and revisions"))
3410 # read in unfinished revisions
3410 # read in unfinished revisions
3411 try:
3411 try:
3412 nodes = repo.vfs.read('graftstate').splitlines()
3412 nodes = repo.vfs.read('graftstate').splitlines()
3413 revs = [repo[node].rev() for node in nodes]
3413 revs = [repo[node].rev() for node in nodes]
3414 except IOError, inst:
3414 except IOError, inst:
3415 if inst.errno != errno.ENOENT:
3415 if inst.errno != errno.ENOENT:
3416 raise
3416 raise
3417 raise util.Abort(_("no graft state found, can't continue"))
3417 raise util.Abort(_("no graft state found, can't continue"))
3418 else:
3418 else:
3419 cmdutil.checkunfinished(repo)
3419 cmdutil.checkunfinished(repo)
3420 cmdutil.bailifchanged(repo)
3420 cmdutil.bailifchanged(repo)
3421 if not revs:
3421 if not revs:
3422 raise util.Abort(_('no revisions specified'))
3422 raise util.Abort(_('no revisions specified'))
3423 revs = scmutil.revrange(repo, revs)
3423 revs = scmutil.revrange(repo, revs)
3424
3424
3425 skipped = set()
3425 skipped = set()
3426 # check for merges
3426 # check for merges
3427 for rev in repo.revs('%ld and merge()', revs):
3427 for rev in repo.revs('%ld and merge()', revs):
3428 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3428 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3429 skipped.add(rev)
3429 skipped.add(rev)
3430 revs = [r for r in revs if r not in skipped]
3430 revs = [r for r in revs if r not in skipped]
3431 if not revs:
3431 if not revs:
3432 return -1
3432 return -1
3433
3433
3434 # Don't check in the --continue case, in effect retaining --force across
3434 # Don't check in the --continue case, in effect retaining --force across
3435 # --continues. That's because without --force, any revisions we decided to
3435 # --continues. That's because without --force, any revisions we decided to
3436 # skip would have been filtered out here, so they wouldn't have made their
3436 # skip would have been filtered out here, so they wouldn't have made their
3437 # way to the graftstate. With --force, any revisions we would have otherwise
3437 # way to the graftstate. With --force, any revisions we would have otherwise
3438 # skipped would not have been filtered out, and if they hadn't been applied
3438 # skipped would not have been filtered out, and if they hadn't been applied
3439 # already, they'd have been in the graftstate.
3439 # already, they'd have been in the graftstate.
3440 if not (cont or opts.get('force')):
3440 if not (cont or opts.get('force')):
3441 # check for ancestors of dest branch
3441 # check for ancestors of dest branch
3442 crev = repo['.'].rev()
3442 crev = repo['.'].rev()
3443 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3443 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3444 # Cannot use x.remove(y) on smart set, this has to be a list.
3444 # Cannot use x.remove(y) on smart set, this has to be a list.
3445 # XXX make this lazy in the future
3445 # XXX make this lazy in the future
3446 revs = list(revs)
3446 revs = list(revs)
3447 # don't mutate while iterating, create a copy
3447 # don't mutate while iterating, create a copy
3448 for rev in list(revs):
3448 for rev in list(revs):
3449 if rev in ancestors:
3449 if rev in ancestors:
3450 ui.warn(_('skipping ancestor revision %d:%s\n') %
3450 ui.warn(_('skipping ancestor revision %d:%s\n') %
3451 (rev, repo[rev]))
3451 (rev, repo[rev]))
3452 # XXX remove on list is slow
3452 # XXX remove on list is slow
3453 revs.remove(rev)
3453 revs.remove(rev)
3454 if not revs:
3454 if not revs:
3455 return -1
3455 return -1
3456
3456
3457 # analyze revs for earlier grafts
3457 # analyze revs for earlier grafts
3458 ids = {}
3458 ids = {}
3459 for ctx in repo.set("%ld", revs):
3459 for ctx in repo.set("%ld", revs):
3460 ids[ctx.hex()] = ctx.rev()
3460 ids[ctx.hex()] = ctx.rev()
3461 n = ctx.extra().get('source')
3461 n = ctx.extra().get('source')
3462 if n:
3462 if n:
3463 ids[n] = ctx.rev()
3463 ids[n] = ctx.rev()
3464
3464
3465 # check ancestors for earlier grafts
3465 # check ancestors for earlier grafts
3466 ui.debug('scanning for duplicate grafts\n')
3466 ui.debug('scanning for duplicate grafts\n')
3467
3467
3468 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3468 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3469 ctx = repo[rev]
3469 ctx = repo[rev]
3470 n = ctx.extra().get('source')
3470 n = ctx.extra().get('source')
3471 if n in ids:
3471 if n in ids:
3472 try:
3472 try:
3473 r = repo[n].rev()
3473 r = repo[n].rev()
3474 except error.RepoLookupError:
3474 except error.RepoLookupError:
3475 r = None
3475 r = None
3476 if r in revs:
3476 if r in revs:
3477 ui.warn(_('skipping revision %d:%s '
3477 ui.warn(_('skipping revision %d:%s '
3478 '(already grafted to %d:%s)\n')
3478 '(already grafted to %d:%s)\n')
3479 % (r, repo[r], rev, ctx))
3479 % (r, repo[r], rev, ctx))
3480 revs.remove(r)
3480 revs.remove(r)
3481 elif ids[n] in revs:
3481 elif ids[n] in revs:
3482 if r is None:
3482 if r is None:
3483 ui.warn(_('skipping already grafted revision %d:%s '
3483 ui.warn(_('skipping already grafted revision %d:%s '
3484 '(%d:%s also has unknown origin %s)\n')
3484 '(%d:%s also has unknown origin %s)\n')
3485 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3485 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3486 else:
3486 else:
3487 ui.warn(_('skipping already grafted revision %d:%s '
3487 ui.warn(_('skipping already grafted revision %d:%s '
3488 '(%d:%s also has origin %d:%s)\n')
3488 '(%d:%s also has origin %d:%s)\n')
3489 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3489 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3490 revs.remove(ids[n])
3490 revs.remove(ids[n])
3491 elif ctx.hex() in ids:
3491 elif ctx.hex() in ids:
3492 r = ids[ctx.hex()]
3492 r = ids[ctx.hex()]
3493 ui.warn(_('skipping already grafted revision %d:%s '
3493 ui.warn(_('skipping already grafted revision %d:%s '
3494 '(was grafted from %d:%s)\n') %
3494 '(was grafted from %d:%s)\n') %
3495 (r, repo[r], rev, ctx))
3495 (r, repo[r], rev, ctx))
3496 revs.remove(r)
3496 revs.remove(r)
3497 if not revs:
3497 if not revs:
3498 return -1
3498 return -1
3499
3499
3500 wlock = repo.wlock()
3500 wlock = repo.wlock()
3501 try:
3501 try:
3502 for pos, ctx in enumerate(repo.set("%ld", revs)):
3502 for pos, ctx in enumerate(repo.set("%ld", revs)):
3503 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3503 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3504 ctx.description().split('\n', 1)[0])
3504 ctx.description().split('\n', 1)[0])
3505 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3505 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3506 if names:
3506 if names:
3507 desc += ' (%s)' % ' '.join(names)
3507 desc += ' (%s)' % ' '.join(names)
3508 ui.status(_('grafting %s\n') % desc)
3508 ui.status(_('grafting %s\n') % desc)
3509 if opts.get('dry_run'):
3509 if opts.get('dry_run'):
3510 continue
3510 continue
3511
3511
3512 source = ctx.extra().get('source')
3512 source = ctx.extra().get('source')
3513 if not source:
3513 if not source:
3514 source = ctx.hex()
3514 source = ctx.hex()
3515 extra = {'source': source}
3515 extra = {'source': source}
3516 user = ctx.user()
3516 user = ctx.user()
3517 if opts.get('user'):
3517 if opts.get('user'):
3518 user = opts['user']
3518 user = opts['user']
3519 date = ctx.date()
3519 date = ctx.date()
3520 if opts.get('date'):
3520 if opts.get('date'):
3521 date = opts['date']
3521 date = opts['date']
3522 message = ctx.description()
3522 message = ctx.description()
3523 if opts.get('log'):
3523 if opts.get('log'):
3524 message += '\n(grafted from %s)' % ctx.hex()
3524 message += '\n(grafted from %s)' % ctx.hex()
3525
3525
3526 # we don't merge the first commit when continuing
3526 # we don't merge the first commit when continuing
3527 if not cont:
3527 if not cont:
3528 # perform the graft merge with p1(rev) as 'ancestor'
3528 # perform the graft merge with p1(rev) as 'ancestor'
3529 try:
3529 try:
3530 # ui.forcemerge is an internal variable, do not document
3530 # ui.forcemerge is an internal variable, do not document
3531 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3531 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3532 'graft')
3532 'graft')
3533 stats = mergemod.graft(repo, ctx, ctx.p1(),
3533 stats = mergemod.graft(repo, ctx, ctx.p1(),
3534 ['local', 'graft'])
3534 ['local', 'graft'])
3535 finally:
3535 finally:
3536 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3536 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3537 # report any conflicts
3537 # report any conflicts
3538 if stats and stats[3] > 0:
3538 if stats and stats[3] > 0:
3539 # write out state for --continue
3539 # write out state for --continue
3540 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3540 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3541 repo.vfs.write('graftstate', ''.join(nodelines))
3541 repo.vfs.write('graftstate', ''.join(nodelines))
3542 raise util.Abort(
3542 raise util.Abort(
3543 _("unresolved conflicts, can't continue"),
3543 _("unresolved conflicts, can't continue"),
3544 hint=_('use hg resolve and hg graft --continue'))
3544 hint=_('use hg resolve and hg graft --continue'))
3545 else:
3545 else:
3546 cont = False
3546 cont = False
3547
3547
3548 # commit
3548 # commit
3549 node = repo.commit(text=message, user=user,
3549 node = repo.commit(text=message, user=user,
3550 date=date, extra=extra, editor=editor)
3550 date=date, extra=extra, editor=editor)
3551 if node is None:
3551 if node is None:
3552 ui.warn(
3552 ui.warn(
3553 _('note: graft of %d:%s created no changes to commit\n') %
3553 _('note: graft of %d:%s created no changes to commit\n') %
3554 (ctx.rev(), ctx))
3554 (ctx.rev(), ctx))
3555 finally:
3555 finally:
3556 wlock.release()
3556 wlock.release()
3557
3557
3558 # remove state when we complete successfully
3558 # remove state when we complete successfully
3559 if not opts.get('dry_run'):
3559 if not opts.get('dry_run'):
3560 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3560 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3561
3561
3562 return 0
3562 return 0
3563
3563
3564 @command('grep',
3564 @command('grep',
3565 [('0', 'print0', None, _('end fields with NUL')),
3565 [('0', 'print0', None, _('end fields with NUL')),
3566 ('', 'all', None, _('print all revisions that match')),
3566 ('', 'all', None, _('print all revisions that match')),
3567 ('a', 'text', None, _('treat all files as text')),
3567 ('a', 'text', None, _('treat all files as text')),
3568 ('f', 'follow', None,
3568 ('f', 'follow', None,
3569 _('follow changeset history,'
3569 _('follow changeset history,'
3570 ' or file history across copies and renames')),
3570 ' or file history across copies and renames')),
3571 ('i', 'ignore-case', None, _('ignore case when matching')),
3571 ('i', 'ignore-case', None, _('ignore case when matching')),
3572 ('l', 'files-with-matches', None,
3572 ('l', 'files-with-matches', None,
3573 _('print only filenames and revisions that match')),
3573 _('print only filenames and revisions that match')),
3574 ('n', 'line-number', None, _('print matching line numbers')),
3574 ('n', 'line-number', None, _('print matching line numbers')),
3575 ('r', 'rev', [],
3575 ('r', 'rev', [],
3576 _('only search files changed within revision range'), _('REV')),
3576 _('only search files changed within revision range'), _('REV')),
3577 ('u', 'user', None, _('list the author (long with -v)')),
3577 ('u', 'user', None, _('list the author (long with -v)')),
3578 ('d', 'date', None, _('list the date (short with -q)')),
3578 ('d', 'date', None, _('list the date (short with -q)')),
3579 ] + walkopts,
3579 ] + walkopts,
3580 _('[OPTION]... PATTERN [FILE]...'),
3580 _('[OPTION]... PATTERN [FILE]...'),
3581 inferrepo=True)
3581 inferrepo=True)
3582 def grep(ui, repo, pattern, *pats, **opts):
3582 def grep(ui, repo, pattern, *pats, **opts):
3583 """search for a pattern in specified files and revisions
3583 """search for a pattern in specified files and revisions
3584
3584
3585 Search revisions of files for a regular expression.
3585 Search revisions of files for a regular expression.
3586
3586
3587 This command behaves differently than Unix grep. It only accepts
3587 This command behaves differently than Unix grep. It only accepts
3588 Python/Perl regexps. It searches repository history, not the
3588 Python/Perl regexps. It searches repository history, not the
3589 working directory. It always prints the revision number in which a
3589 working directory. It always prints the revision number in which a
3590 match appears.
3590 match appears.
3591
3591
3592 By default, grep only prints output for the first revision of a
3592 By default, grep only prints output for the first revision of a
3593 file in which it finds a match. To get it to print every revision
3593 file in which it finds a match. To get it to print every revision
3594 that contains a change in match status ("-" for a match that
3594 that contains a change in match status ("-" for a match that
3595 becomes a non-match, or "+" for a non-match that becomes a match),
3595 becomes a non-match, or "+" for a non-match that becomes a match),
3596 use the --all flag.
3596 use the --all flag.
3597
3597
3598 Returns 0 if a match is found, 1 otherwise.
3598 Returns 0 if a match is found, 1 otherwise.
3599 """
3599 """
3600 reflags = re.M
3600 reflags = re.M
3601 if opts.get('ignore_case'):
3601 if opts.get('ignore_case'):
3602 reflags |= re.I
3602 reflags |= re.I
3603 try:
3603 try:
3604 regexp = util.re.compile(pattern, reflags)
3604 regexp = util.re.compile(pattern, reflags)
3605 except re.error, inst:
3605 except re.error, inst:
3606 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3606 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3607 return 1
3607 return 1
3608 sep, eol = ':', '\n'
3608 sep, eol = ':', '\n'
3609 if opts.get('print0'):
3609 if opts.get('print0'):
3610 sep = eol = '\0'
3610 sep = eol = '\0'
3611
3611
3612 getfile = util.lrucachefunc(repo.file)
3612 getfile = util.lrucachefunc(repo.file)
3613
3613
3614 def matchlines(body):
3614 def matchlines(body):
3615 begin = 0
3615 begin = 0
3616 linenum = 0
3616 linenum = 0
3617 while begin < len(body):
3617 while begin < len(body):
3618 match = regexp.search(body, begin)
3618 match = regexp.search(body, begin)
3619 if not match:
3619 if not match:
3620 break
3620 break
3621 mstart, mend = match.span()
3621 mstart, mend = match.span()
3622 linenum += body.count('\n', begin, mstart) + 1
3622 linenum += body.count('\n', begin, mstart) + 1
3623 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3623 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3624 begin = body.find('\n', mend) + 1 or len(body) + 1
3624 begin = body.find('\n', mend) + 1 or len(body) + 1
3625 lend = begin - 1
3625 lend = begin - 1
3626 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3626 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3627
3627
3628 class linestate(object):
3628 class linestate(object):
3629 def __init__(self, line, linenum, colstart, colend):
3629 def __init__(self, line, linenum, colstart, colend):
3630 self.line = line
3630 self.line = line
3631 self.linenum = linenum
3631 self.linenum = linenum
3632 self.colstart = colstart
3632 self.colstart = colstart
3633 self.colend = colend
3633 self.colend = colend
3634
3634
3635 def __hash__(self):
3635 def __hash__(self):
3636 return hash((self.linenum, self.line))
3636 return hash((self.linenum, self.line))
3637
3637
3638 def __eq__(self, other):
3638 def __eq__(self, other):
3639 return self.line == other.line
3639 return self.line == other.line
3640
3640
3641 def __iter__(self):
3641 def __iter__(self):
3642 yield (self.line[:self.colstart], '')
3642 yield (self.line[:self.colstart], '')
3643 yield (self.line[self.colstart:self.colend], 'grep.match')
3643 yield (self.line[self.colstart:self.colend], 'grep.match')
3644 rest = self.line[self.colend:]
3644 rest = self.line[self.colend:]
3645 while rest != '':
3645 while rest != '':
3646 match = regexp.search(rest)
3646 match = regexp.search(rest)
3647 if not match:
3647 if not match:
3648 yield (rest, '')
3648 yield (rest, '')
3649 break
3649 break
3650 mstart, mend = match.span()
3650 mstart, mend = match.span()
3651 yield (rest[:mstart], '')
3651 yield (rest[:mstart], '')
3652 yield (rest[mstart:mend], 'grep.match')
3652 yield (rest[mstart:mend], 'grep.match')
3653 rest = rest[mend:]
3653 rest = rest[mend:]
3654
3654
3655 matches = {}
3655 matches = {}
3656 copies = {}
3656 copies = {}
3657 def grepbody(fn, rev, body):
3657 def grepbody(fn, rev, body):
3658 matches[rev].setdefault(fn, [])
3658 matches[rev].setdefault(fn, [])
3659 m = matches[rev][fn]
3659 m = matches[rev][fn]
3660 for lnum, cstart, cend, line in matchlines(body):
3660 for lnum, cstart, cend, line in matchlines(body):
3661 s = linestate(line, lnum, cstart, cend)
3661 s = linestate(line, lnum, cstart, cend)
3662 m.append(s)
3662 m.append(s)
3663
3663
3664 def difflinestates(a, b):
3664 def difflinestates(a, b):
3665 sm = difflib.SequenceMatcher(None, a, b)
3665 sm = difflib.SequenceMatcher(None, a, b)
3666 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3666 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3667 if tag == 'insert':
3667 if tag == 'insert':
3668 for i in xrange(blo, bhi):
3668 for i in xrange(blo, bhi):
3669 yield ('+', b[i])
3669 yield ('+', b[i])
3670 elif tag == 'delete':
3670 elif tag == 'delete':
3671 for i in xrange(alo, ahi):
3671 for i in xrange(alo, ahi):
3672 yield ('-', a[i])
3672 yield ('-', a[i])
3673 elif tag == 'replace':
3673 elif tag == 'replace':
3674 for i in xrange(alo, ahi):
3674 for i in xrange(alo, ahi):
3675 yield ('-', a[i])
3675 yield ('-', a[i])
3676 for i in xrange(blo, bhi):
3676 for i in xrange(blo, bhi):
3677 yield ('+', b[i])
3677 yield ('+', b[i])
3678
3678
3679 def display(fn, ctx, pstates, states):
3679 def display(fn, ctx, pstates, states):
3680 rev = ctx.rev()
3680 rev = ctx.rev()
3681 if ui.quiet:
3681 if ui.quiet:
3682 datefunc = util.shortdate
3682 datefunc = util.shortdate
3683 else:
3683 else:
3684 datefunc = util.datestr
3684 datefunc = util.datestr
3685 found = False
3685 found = False
3686 @util.cachefunc
3686 @util.cachefunc
3687 def binary():
3687 def binary():
3688 flog = getfile(fn)
3688 flog = getfile(fn)
3689 return util.binary(flog.read(ctx.filenode(fn)))
3689 return util.binary(flog.read(ctx.filenode(fn)))
3690
3690
3691 if opts.get('all'):
3691 if opts.get('all'):
3692 iter = difflinestates(pstates, states)
3692 iter = difflinestates(pstates, states)
3693 else:
3693 else:
3694 iter = [('', l) for l in states]
3694 iter = [('', l) for l in states]
3695 for change, l in iter:
3695 for change, l in iter:
3696 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3696 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3697
3697
3698 if opts.get('line_number'):
3698 if opts.get('line_number'):
3699 cols.append((str(l.linenum), 'grep.linenumber'))
3699 cols.append((str(l.linenum), 'grep.linenumber'))
3700 if opts.get('all'):
3700 if opts.get('all'):
3701 cols.append((change, 'grep.change'))
3701 cols.append((change, 'grep.change'))
3702 if opts.get('user'):
3702 if opts.get('user'):
3703 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3703 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3704 if opts.get('date'):
3704 if opts.get('date'):
3705 cols.append((datefunc(ctx.date()), 'grep.date'))
3705 cols.append((datefunc(ctx.date()), 'grep.date'))
3706 for col, label in cols[:-1]:
3706 for col, label in cols[:-1]:
3707 ui.write(col, label=label)
3707 ui.write(col, label=label)
3708 ui.write(sep, label='grep.sep')
3708 ui.write(sep, label='grep.sep')
3709 ui.write(cols[-1][0], label=cols[-1][1])
3709 ui.write(cols[-1][0], label=cols[-1][1])
3710 if not opts.get('files_with_matches'):
3710 if not opts.get('files_with_matches'):
3711 ui.write(sep, label='grep.sep')
3711 ui.write(sep, label='grep.sep')
3712 if not opts.get('text') and binary():
3712 if not opts.get('text') and binary():
3713 ui.write(" Binary file matches")
3713 ui.write(" Binary file matches")
3714 else:
3714 else:
3715 for s, label in l:
3715 for s, label in l:
3716 ui.write(s, label=label)
3716 ui.write(s, label=label)
3717 ui.write(eol)
3717 ui.write(eol)
3718 found = True
3718 found = True
3719 if opts.get('files_with_matches'):
3719 if opts.get('files_with_matches'):
3720 break
3720 break
3721 return found
3721 return found
3722
3722
3723 skip = {}
3723 skip = {}
3724 revfiles = {}
3724 revfiles = {}
3725 matchfn = scmutil.match(repo[None], pats, opts)
3725 matchfn = scmutil.match(repo[None], pats, opts)
3726 found = False
3726 found = False
3727 follow = opts.get('follow')
3727 follow = opts.get('follow')
3728
3728
3729 def prep(ctx, fns):
3729 def prep(ctx, fns):
3730 rev = ctx.rev()
3730 rev = ctx.rev()
3731 pctx = ctx.p1()
3731 pctx = ctx.p1()
3732 parent = pctx.rev()
3732 parent = pctx.rev()
3733 matches.setdefault(rev, {})
3733 matches.setdefault(rev, {})
3734 matches.setdefault(parent, {})
3734 matches.setdefault(parent, {})
3735 files = revfiles.setdefault(rev, [])
3735 files = revfiles.setdefault(rev, [])
3736 for fn in fns:
3736 for fn in fns:
3737 flog = getfile(fn)
3737 flog = getfile(fn)
3738 try:
3738 try:
3739 fnode = ctx.filenode(fn)
3739 fnode = ctx.filenode(fn)
3740 except error.LookupError:
3740 except error.LookupError:
3741 continue
3741 continue
3742
3742
3743 copied = flog.renamed(fnode)
3743 copied = flog.renamed(fnode)
3744 copy = follow and copied and copied[0]
3744 copy = follow and copied and copied[0]
3745 if copy:
3745 if copy:
3746 copies.setdefault(rev, {})[fn] = copy
3746 copies.setdefault(rev, {})[fn] = copy
3747 if fn in skip:
3747 if fn in skip:
3748 if copy:
3748 if copy:
3749 skip[copy] = True
3749 skip[copy] = True
3750 continue
3750 continue
3751 files.append(fn)
3751 files.append(fn)
3752
3752
3753 if fn not in matches[rev]:
3753 if fn not in matches[rev]:
3754 grepbody(fn, rev, flog.read(fnode))
3754 grepbody(fn, rev, flog.read(fnode))
3755
3755
3756 pfn = copy or fn
3756 pfn = copy or fn
3757 if pfn not in matches[parent]:
3757 if pfn not in matches[parent]:
3758 try:
3758 try:
3759 fnode = pctx.filenode(pfn)
3759 fnode = pctx.filenode(pfn)
3760 grepbody(pfn, parent, flog.read(fnode))
3760 grepbody(pfn, parent, flog.read(fnode))
3761 except error.LookupError:
3761 except error.LookupError:
3762 pass
3762 pass
3763
3763
3764 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3764 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3765 rev = ctx.rev()
3765 rev = ctx.rev()
3766 parent = ctx.p1().rev()
3766 parent = ctx.p1().rev()
3767 for fn in sorted(revfiles.get(rev, [])):
3767 for fn in sorted(revfiles.get(rev, [])):
3768 states = matches[rev][fn]
3768 states = matches[rev][fn]
3769 copy = copies.get(rev, {}).get(fn)
3769 copy = copies.get(rev, {}).get(fn)
3770 if fn in skip:
3770 if fn in skip:
3771 if copy:
3771 if copy:
3772 skip[copy] = True
3772 skip[copy] = True
3773 continue
3773 continue
3774 pstates = matches.get(parent, {}).get(copy or fn, [])
3774 pstates = matches.get(parent, {}).get(copy or fn, [])
3775 if pstates or states:
3775 if pstates or states:
3776 r = display(fn, ctx, pstates, states)
3776 r = display(fn, ctx, pstates, states)
3777 found = found or r
3777 found = found or r
3778 if r and not opts.get('all'):
3778 if r and not opts.get('all'):
3779 skip[fn] = True
3779 skip[fn] = True
3780 if copy:
3780 if copy:
3781 skip[copy] = True
3781 skip[copy] = True
3782 del matches[rev]
3782 del matches[rev]
3783 del revfiles[rev]
3783 del revfiles[rev]
3784
3784
3785 return not found
3785 return not found
3786
3786
3787 @command('heads',
3787 @command('heads',
3788 [('r', 'rev', '',
3788 [('r', 'rev', '',
3789 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3789 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3790 ('t', 'topo', False, _('show topological heads only')),
3790 ('t', 'topo', False, _('show topological heads only')),
3791 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3791 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3792 ('c', 'closed', False, _('show normal and closed branch heads')),
3792 ('c', 'closed', False, _('show normal and closed branch heads')),
3793 ] + templateopts,
3793 ] + templateopts,
3794 _('[-ct] [-r STARTREV] [REV]...'))
3794 _('[-ct] [-r STARTREV] [REV]...'))
3795 def heads(ui, repo, *branchrevs, **opts):
3795 def heads(ui, repo, *branchrevs, **opts):
3796 """show branch heads
3796 """show branch heads
3797
3797
3798 With no arguments, show all open branch heads in the repository.
3798 With no arguments, show all open branch heads in the repository.
3799 Branch heads are changesets that have no descendants on the
3799 Branch heads are changesets that have no descendants on the
3800 same branch. They are where development generally takes place and
3800 same branch. They are where development generally takes place and
3801 are the usual targets for update and merge operations.
3801 are the usual targets for update and merge operations.
3802
3802
3803 If one or more REVs are given, only open branch heads on the
3803 If one or more REVs are given, only open branch heads on the
3804 branches associated with the specified changesets are shown. This
3804 branches associated with the specified changesets are shown. This
3805 means that you can use :hg:`heads .` to see the heads on the
3805 means that you can use :hg:`heads .` to see the heads on the
3806 currently checked-out branch.
3806 currently checked-out branch.
3807
3807
3808 If -c/--closed is specified, also show branch heads marked closed
3808 If -c/--closed is specified, also show branch heads marked closed
3809 (see :hg:`commit --close-branch`).
3809 (see :hg:`commit --close-branch`).
3810
3810
3811 If STARTREV is specified, only those heads that are descendants of
3811 If STARTREV is specified, only those heads that are descendants of
3812 STARTREV will be displayed.
3812 STARTREV will be displayed.
3813
3813
3814 If -t/--topo is specified, named branch mechanics will be ignored and only
3814 If -t/--topo is specified, named branch mechanics will be ignored and only
3815 topological heads (changesets with no children) will be shown.
3815 topological heads (changesets with no children) will be shown.
3816
3816
3817 Returns 0 if matching heads are found, 1 if not.
3817 Returns 0 if matching heads are found, 1 if not.
3818 """
3818 """
3819
3819
3820 start = None
3820 start = None
3821 if 'rev' in opts:
3821 if 'rev' in opts:
3822 start = scmutil.revsingle(repo, opts['rev'], None).node()
3822 start = scmutil.revsingle(repo, opts['rev'], None).node()
3823
3823
3824 if opts.get('topo'):
3824 if opts.get('topo'):
3825 heads = [repo[h] for h in repo.heads(start)]
3825 heads = [repo[h] for h in repo.heads(start)]
3826 else:
3826 else:
3827 heads = []
3827 heads = []
3828 for branch in repo.branchmap():
3828 for branch in repo.branchmap():
3829 heads += repo.branchheads(branch, start, opts.get('closed'))
3829 heads += repo.branchheads(branch, start, opts.get('closed'))
3830 heads = [repo[h] for h in heads]
3830 heads = [repo[h] for h in heads]
3831
3831
3832 if branchrevs:
3832 if branchrevs:
3833 branches = set(repo[br].branch() for br in branchrevs)
3833 branches = set(repo[br].branch() for br in branchrevs)
3834 heads = [h for h in heads if h.branch() in branches]
3834 heads = [h for h in heads if h.branch() in branches]
3835
3835
3836 if opts.get('active') and branchrevs:
3836 if opts.get('active') and branchrevs:
3837 dagheads = repo.heads(start)
3837 dagheads = repo.heads(start)
3838 heads = [h for h in heads if h.node() in dagheads]
3838 heads = [h for h in heads if h.node() in dagheads]
3839
3839
3840 if branchrevs:
3840 if branchrevs:
3841 haveheads = set(h.branch() for h in heads)
3841 haveheads = set(h.branch() for h in heads)
3842 if branches - haveheads:
3842 if branches - haveheads:
3843 headless = ', '.join(b for b in branches - haveheads)
3843 headless = ', '.join(b for b in branches - haveheads)
3844 msg = _('no open branch heads found on branches %s')
3844 msg = _('no open branch heads found on branches %s')
3845 if opts.get('rev'):
3845 if opts.get('rev'):
3846 msg += _(' (started at %s)') % opts['rev']
3846 msg += _(' (started at %s)') % opts['rev']
3847 ui.warn((msg + '\n') % headless)
3847 ui.warn((msg + '\n') % headless)
3848
3848
3849 if not heads:
3849 if not heads:
3850 return 1
3850 return 1
3851
3851
3852 heads = sorted(heads, key=lambda x: -x.rev())
3852 heads = sorted(heads, key=lambda x: -x.rev())
3853 displayer = cmdutil.show_changeset(ui, repo, opts)
3853 displayer = cmdutil.show_changeset(ui, repo, opts)
3854 for ctx in heads:
3854 for ctx in heads:
3855 displayer.show(ctx)
3855 displayer.show(ctx)
3856 displayer.close()
3856 displayer.close()
3857
3857
3858 @command('help',
3858 @command('help',
3859 [('e', 'extension', None, _('show only help for extensions')),
3859 [('e', 'extension', None, _('show only help for extensions')),
3860 ('c', 'command', None, _('show only help for commands')),
3860 ('c', 'command', None, _('show only help for commands')),
3861 ('k', 'keyword', '', _('show topics matching keyword')),
3861 ('k', 'keyword', '', _('show topics matching keyword')),
3862 ],
3862 ],
3863 _('[-ec] [TOPIC]'),
3863 _('[-ec] [TOPIC]'),
3864 norepo=True)
3864 norepo=True)
3865 def help_(ui, name=None, **opts):
3865 def help_(ui, name=None, **opts):
3866 """show help for a given topic or a help overview
3866 """show help for a given topic or a help overview
3867
3867
3868 With no arguments, print a list of commands with short help messages.
3868 With no arguments, print a list of commands with short help messages.
3869
3869
3870 Given a topic, extension, or command name, print help for that
3870 Given a topic, extension, or command name, print help for that
3871 topic.
3871 topic.
3872
3872
3873 Returns 0 if successful.
3873 Returns 0 if successful.
3874 """
3874 """
3875
3875
3876 textwidth = min(ui.termwidth(), 80) - 2
3876 textwidth = min(ui.termwidth(), 80) - 2
3877
3877
3878 keep = []
3878 keep = []
3879 if ui.verbose:
3879 if ui.verbose:
3880 keep.append('verbose')
3880 keep.append('verbose')
3881 if sys.platform.startswith('win'):
3881 if sys.platform.startswith('win'):
3882 keep.append('windows')
3882 keep.append('windows')
3883 elif sys.platform == 'OpenVMS':
3883 elif sys.platform == 'OpenVMS':
3884 keep.append('vms')
3884 keep.append('vms')
3885 elif sys.platform == 'plan9':
3885 elif sys.platform == 'plan9':
3886 keep.append('plan9')
3886 keep.append('plan9')
3887 else:
3887 else:
3888 keep.append('unix')
3888 keep.append('unix')
3889 keep.append(sys.platform.lower())
3889 keep.append(sys.platform.lower())
3890
3890
3891 section = None
3891 section = None
3892 if name and '.' in name:
3892 if name and '.' in name:
3893 name, section = name.split('.', 1)
3893 name, section = name.split('.', 1)
3894
3894
3895 text = help.help_(ui, name, **opts)
3895 text = help.help_(ui, name, **opts)
3896
3896
3897 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3897 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3898 section=section)
3898 section=section)
3899 if section and not formatted:
3899 if section and not formatted:
3900 raise util.Abort(_("help section not found"))
3900 raise util.Abort(_("help section not found"))
3901
3901
3902 if 'verbose' in pruned:
3902 if 'verbose' in pruned:
3903 keep.append('omitted')
3903 keep.append('omitted')
3904 else:
3904 else:
3905 keep.append('notomitted')
3905 keep.append('notomitted')
3906 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3906 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3907 section=section)
3907 section=section)
3908 ui.write(formatted)
3908 ui.write(formatted)
3909
3909
3910
3910
3911 @command('identify|id',
3911 @command('identify|id',
3912 [('r', 'rev', '',
3912 [('r', 'rev', '',
3913 _('identify the specified revision'), _('REV')),
3913 _('identify the specified revision'), _('REV')),
3914 ('n', 'num', None, _('show local revision number')),
3914 ('n', 'num', None, _('show local revision number')),
3915 ('i', 'id', None, _('show global revision id')),
3915 ('i', 'id', None, _('show global revision id')),
3916 ('b', 'branch', None, _('show branch')),
3916 ('b', 'branch', None, _('show branch')),
3917 ('t', 'tags', None, _('show tags')),
3917 ('t', 'tags', None, _('show tags')),
3918 ('B', 'bookmarks', None, _('show bookmarks')),
3918 ('B', 'bookmarks', None, _('show bookmarks')),
3919 ] + remoteopts,
3919 ] + remoteopts,
3920 _('[-nibtB] [-r REV] [SOURCE]'),
3920 _('[-nibtB] [-r REV] [SOURCE]'),
3921 optionalrepo=True)
3921 optionalrepo=True)
3922 def identify(ui, repo, source=None, rev=None,
3922 def identify(ui, repo, source=None, rev=None,
3923 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3923 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3924 """identify the working directory or specified revision
3924 """identify the working directory or specified revision
3925
3925
3926 Print a summary identifying the repository state at REV using one or
3926 Print a summary identifying the repository state at REV using one or
3927 two parent hash identifiers, followed by a "+" if the working
3927 two parent hash identifiers, followed by a "+" if the working
3928 directory has uncommitted changes, the branch name (if not default),
3928 directory has uncommitted changes, the branch name (if not default),
3929 a list of tags, and a list of bookmarks.
3929 a list of tags, and a list of bookmarks.
3930
3930
3931 When REV is not given, print a summary of the current state of the
3931 When REV is not given, print a summary of the current state of the
3932 repository.
3932 repository.
3933
3933
3934 Specifying a path to a repository root or Mercurial bundle will
3934 Specifying a path to a repository root or Mercurial bundle will
3935 cause lookup to operate on that repository/bundle.
3935 cause lookup to operate on that repository/bundle.
3936
3936
3937 .. container:: verbose
3937 .. container:: verbose
3938
3938
3939 Examples:
3939 Examples:
3940
3940
3941 - generate a build identifier for the working directory::
3941 - generate a build identifier for the working directory::
3942
3942
3943 hg id --id > build-id.dat
3943 hg id --id > build-id.dat
3944
3944
3945 - find the revision corresponding to a tag::
3945 - find the revision corresponding to a tag::
3946
3946
3947 hg id -n -r 1.3
3947 hg id -n -r 1.3
3948
3948
3949 - check the most recent revision of a remote repository::
3949 - check the most recent revision of a remote repository::
3950
3950
3951 hg id -r tip http://selenic.com/hg/
3951 hg id -r tip http://selenic.com/hg/
3952
3952
3953 Returns 0 if successful.
3953 Returns 0 if successful.
3954 """
3954 """
3955
3955
3956 if not repo and not source:
3956 if not repo and not source:
3957 raise util.Abort(_("there is no Mercurial repository here "
3957 raise util.Abort(_("there is no Mercurial repository here "
3958 "(.hg not found)"))
3958 "(.hg not found)"))
3959
3959
3960 if ui.debugflag:
3960 if ui.debugflag:
3961 hexfunc = hex
3961 hexfunc = hex
3962 else:
3962 else:
3963 hexfunc = short
3963 hexfunc = short
3964 default = not (num or id or branch or tags or bookmarks)
3964 default = not (num or id or branch or tags or bookmarks)
3965 output = []
3965 output = []
3966 revs = []
3966 revs = []
3967
3967
3968 if source:
3968 if source:
3969 source, branches = hg.parseurl(ui.expandpath(source))
3969 source, branches = hg.parseurl(ui.expandpath(source))
3970 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3970 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3971 repo = peer.local()
3971 repo = peer.local()
3972 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3972 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3973
3973
3974 if not repo:
3974 if not repo:
3975 if num or branch or tags:
3975 if num or branch or tags:
3976 raise util.Abort(
3976 raise util.Abort(
3977 _("can't query remote revision number, branch, or tags"))
3977 _("can't query remote revision number, branch, or tags"))
3978 if not rev and revs:
3978 if not rev and revs:
3979 rev = revs[0]
3979 rev = revs[0]
3980 if not rev:
3980 if not rev:
3981 rev = "tip"
3981 rev = "tip"
3982
3982
3983 remoterev = peer.lookup(rev)
3983 remoterev = peer.lookup(rev)
3984 if default or id:
3984 if default or id:
3985 output = [hexfunc(remoterev)]
3985 output = [hexfunc(remoterev)]
3986
3986
3987 def getbms():
3987 def getbms():
3988 bms = []
3988 bms = []
3989
3989
3990 if 'bookmarks' in peer.listkeys('namespaces'):
3990 if 'bookmarks' in peer.listkeys('namespaces'):
3991 hexremoterev = hex(remoterev)
3991 hexremoterev = hex(remoterev)
3992 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3992 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3993 if bmr == hexremoterev]
3993 if bmr == hexremoterev]
3994
3994
3995 return sorted(bms)
3995 return sorted(bms)
3996
3996
3997 if bookmarks:
3997 if bookmarks:
3998 output.extend(getbms())
3998 output.extend(getbms())
3999 elif default and not ui.quiet:
3999 elif default and not ui.quiet:
4000 # multiple bookmarks for a single parent separated by '/'
4000 # multiple bookmarks for a single parent separated by '/'
4001 bm = '/'.join(getbms())
4001 bm = '/'.join(getbms())
4002 if bm:
4002 if bm:
4003 output.append(bm)
4003 output.append(bm)
4004 else:
4004 else:
4005 if not rev:
4005 if not rev:
4006 ctx = repo[None]
4006 ctx = repo[None]
4007 parents = ctx.parents()
4007 parents = ctx.parents()
4008 changed = ""
4008 changed = ""
4009 if default or id or num:
4009 if default or id or num:
4010 if (util.any(repo.status())
4010 if (util.any(repo.status())
4011 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
4011 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
4012 changed = '+'
4012 changed = '+'
4013 if default or id:
4013 if default or id:
4014 output = ["%s%s" %
4014 output = ["%s%s" %
4015 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4015 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4016 if num:
4016 if num:
4017 output.append("%s%s" %
4017 output.append("%s%s" %
4018 ('+'.join([str(p.rev()) for p in parents]), changed))
4018 ('+'.join([str(p.rev()) for p in parents]), changed))
4019 else:
4019 else:
4020 ctx = scmutil.revsingle(repo, rev)
4020 ctx = scmutil.revsingle(repo, rev)
4021 if default or id:
4021 if default or id:
4022 output = [hexfunc(ctx.node())]
4022 output = [hexfunc(ctx.node())]
4023 if num:
4023 if num:
4024 output.append(str(ctx.rev()))
4024 output.append(str(ctx.rev()))
4025
4025
4026 if default and not ui.quiet:
4026 if default and not ui.quiet:
4027 b = ctx.branch()
4027 b = ctx.branch()
4028 if b != 'default':
4028 if b != 'default':
4029 output.append("(%s)" % b)
4029 output.append("(%s)" % b)
4030
4030
4031 # multiple tags for a single parent separated by '/'
4031 # multiple tags for a single parent separated by '/'
4032 t = '/'.join(ctx.tags())
4032 t = '/'.join(ctx.tags())
4033 if t:
4033 if t:
4034 output.append(t)
4034 output.append(t)
4035
4035
4036 # multiple bookmarks for a single parent separated by '/'
4036 # multiple bookmarks for a single parent separated by '/'
4037 bm = '/'.join(ctx.bookmarks())
4037 bm = '/'.join(ctx.bookmarks())
4038 if bm:
4038 if bm:
4039 output.append(bm)
4039 output.append(bm)
4040 else:
4040 else:
4041 if branch:
4041 if branch:
4042 output.append(ctx.branch())
4042 output.append(ctx.branch())
4043
4043
4044 if tags:
4044 if tags:
4045 output.extend(ctx.tags())
4045 output.extend(ctx.tags())
4046
4046
4047 if bookmarks:
4047 if bookmarks:
4048 output.extend(ctx.bookmarks())
4048 output.extend(ctx.bookmarks())
4049
4049
4050 ui.write("%s\n" % ' '.join(output))
4050 ui.write("%s\n" % ' '.join(output))
4051
4051
4052 @command('import|patch',
4052 @command('import|patch',
4053 [('p', 'strip', 1,
4053 [('p', 'strip', 1,
4054 _('directory strip option for patch. This has the same '
4054 _('directory strip option for patch. This has the same '
4055 'meaning as the corresponding patch option'), _('NUM')),
4055 'meaning as the corresponding patch option'), _('NUM')),
4056 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4056 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4057 ('e', 'edit', False, _('invoke editor on commit messages')),
4057 ('e', 'edit', False, _('invoke editor on commit messages')),
4058 ('f', 'force', None,
4058 ('f', 'force', None,
4059 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4059 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4060 ('', 'no-commit', None,
4060 ('', 'no-commit', None,
4061 _("don't commit, just update the working directory")),
4061 _("don't commit, just update the working directory")),
4062 ('', 'bypass', None,
4062 ('', 'bypass', None,
4063 _("apply patch without touching the working directory")),
4063 _("apply patch without touching the working directory")),
4064 ('', 'partial', None,
4064 ('', 'partial', None,
4065 _('commit even if some hunks fail')),
4065 _('commit even if some hunks fail')),
4066 ('', 'exact', None,
4066 ('', 'exact', None,
4067 _('apply patch to the nodes from which it was generated')),
4067 _('apply patch to the nodes from which it was generated')),
4068 ('', 'prefix', '',
4068 ('', 'prefix', '',
4069 _('apply patch to subdirectory'), _('DIR')),
4069 _('apply patch to subdirectory'), _('DIR')),
4070 ('', 'import-branch', None,
4070 ('', 'import-branch', None,
4071 _('use any branch information in patch (implied by --exact)'))] +
4071 _('use any branch information in patch (implied by --exact)'))] +
4072 commitopts + commitopts2 + similarityopts,
4072 commitopts + commitopts2 + similarityopts,
4073 _('[OPTION]... PATCH...'))
4073 _('[OPTION]... PATCH...'))
4074 def import_(ui, repo, patch1=None, *patches, **opts):
4074 def import_(ui, repo, patch1=None, *patches, **opts):
4075 """import an ordered set of patches
4075 """import an ordered set of patches
4076
4076
4077 Import a list of patches and commit them individually (unless
4077 Import a list of patches and commit them individually (unless
4078 --no-commit is specified).
4078 --no-commit is specified).
4079
4079
4080 Because import first applies changes to the working directory,
4080 Because import first applies changes to the working directory,
4081 import will abort if there are outstanding changes.
4081 import will abort if there are outstanding changes.
4082
4082
4083 You can import a patch straight from a mail message. Even patches
4083 You can import a patch straight from a mail message. Even patches
4084 as attachments work (to use the body part, it must have type
4084 as attachments work (to use the body part, it must have type
4085 text/plain or text/x-patch). From and Subject headers of email
4085 text/plain or text/x-patch). From and Subject headers of email
4086 message are used as default committer and commit message. All
4086 message are used as default committer and commit message. All
4087 text/plain body parts before first diff are added to commit
4087 text/plain body parts before first diff are added to commit
4088 message.
4088 message.
4089
4089
4090 If the imported patch was generated by :hg:`export`, user and
4090 If the imported patch was generated by :hg:`export`, user and
4091 description from patch override values from message headers and
4091 description from patch override values from message headers and
4092 body. Values given on command line with -m/--message and -u/--user
4092 body. Values given on command line with -m/--message and -u/--user
4093 override these.
4093 override these.
4094
4094
4095 If --exact is specified, import will set the working directory to
4095 If --exact is specified, import will set the working directory to
4096 the parent of each patch before applying it, and will abort if the
4096 the parent of each patch before applying it, and will abort if the
4097 resulting changeset has a different ID than the one recorded in
4097 resulting changeset has a different ID than the one recorded in
4098 the patch. This may happen due to character set problems or other
4098 the patch. This may happen due to character set problems or other
4099 deficiencies in the text patch format.
4099 deficiencies in the text patch format.
4100
4100
4101 Use --bypass to apply and commit patches directly to the
4101 Use --bypass to apply and commit patches directly to the
4102 repository, not touching the working directory. Without --exact,
4102 repository, not touching the working directory. Without --exact,
4103 patches will be applied on top of the working directory parent
4103 patches will be applied on top of the working directory parent
4104 revision.
4104 revision.
4105
4105
4106 With -s/--similarity, hg will attempt to discover renames and
4106 With -s/--similarity, hg will attempt to discover renames and
4107 copies in the patch in the same way as :hg:`addremove`.
4107 copies in the patch in the same way as :hg:`addremove`.
4108
4108
4109 Use --partial to ensure a changeset will be created from the patch
4109 Use --partial to ensure a changeset will be created from the patch
4110 even if some hunks fail to apply. Hunks that fail to apply will be
4110 even if some hunks fail to apply. Hunks that fail to apply will be
4111 written to a <target-file>.rej file. Conflicts can then be resolved
4111 written to a <target-file>.rej file. Conflicts can then be resolved
4112 by hand before :hg:`commit --amend` is run to update the created
4112 by hand before :hg:`commit --amend` is run to update the created
4113 changeset. This flag exists to let people import patches that
4113 changeset. This flag exists to let people import patches that
4114 partially apply without losing the associated metadata (author,
4114 partially apply without losing the associated metadata (author,
4115 date, description, ...). Note that when none of the hunk applies
4115 date, description, ...). Note that when none of the hunk applies
4116 cleanly, :hg:`import --partial` will create an empty changeset,
4116 cleanly, :hg:`import --partial` will create an empty changeset,
4117 importing only the patch metadata.
4117 importing only the patch metadata.
4118
4118
4119 To read a patch from standard input, use "-" as the patch name. If
4119 To read a patch from standard input, use "-" as the patch name. If
4120 a URL is specified, the patch will be downloaded from it.
4120 a URL is specified, the patch will be downloaded from it.
4121 See :hg:`help dates` for a list of formats valid for -d/--date.
4121 See :hg:`help dates` for a list of formats valid for -d/--date.
4122
4122
4123 .. container:: verbose
4123 .. container:: verbose
4124
4124
4125 Examples:
4125 Examples:
4126
4126
4127 - import a traditional patch from a website and detect renames::
4127 - import a traditional patch from a website and detect renames::
4128
4128
4129 hg import -s 80 http://example.com/bugfix.patch
4129 hg import -s 80 http://example.com/bugfix.patch
4130
4130
4131 - import a changeset from an hgweb server::
4131 - import a changeset from an hgweb server::
4132
4132
4133 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
4133 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
4134
4134
4135 - import all the patches in an Unix-style mbox::
4135 - import all the patches in an Unix-style mbox::
4136
4136
4137 hg import incoming-patches.mbox
4137 hg import incoming-patches.mbox
4138
4138
4139 - attempt to exactly restore an exported changeset (not always
4139 - attempt to exactly restore an exported changeset (not always
4140 possible)::
4140 possible)::
4141
4141
4142 hg import --exact proposed-fix.patch
4142 hg import --exact proposed-fix.patch
4143
4143
4144 Returns 0 on success, 1 on partial success (see --partial).
4144 Returns 0 on success, 1 on partial success (see --partial).
4145 """
4145 """
4146
4146
4147 if not patch1:
4147 if not patch1:
4148 raise util.Abort(_('need at least one patch to import'))
4148 raise util.Abort(_('need at least one patch to import'))
4149
4149
4150 patches = (patch1,) + patches
4150 patches = (patch1,) + patches
4151
4151
4152 date = opts.get('date')
4152 date = opts.get('date')
4153 if date:
4153 if date:
4154 opts['date'] = util.parsedate(date)
4154 opts['date'] = util.parsedate(date)
4155
4155
4156 update = not opts.get('bypass')
4156 update = not opts.get('bypass')
4157 if not update and opts.get('no_commit'):
4157 if not update and opts.get('no_commit'):
4158 raise util.Abort(_('cannot use --no-commit with --bypass'))
4158 raise util.Abort(_('cannot use --no-commit with --bypass'))
4159 try:
4159 try:
4160 sim = float(opts.get('similarity') or 0)
4160 sim = float(opts.get('similarity') or 0)
4161 except ValueError:
4161 except ValueError:
4162 raise util.Abort(_('similarity must be a number'))
4162 raise util.Abort(_('similarity must be a number'))
4163 if sim < 0 or sim > 100:
4163 if sim < 0 or sim > 100:
4164 raise util.Abort(_('similarity must be between 0 and 100'))
4164 raise util.Abort(_('similarity must be between 0 and 100'))
4165 if sim and not update:
4165 if sim and not update:
4166 raise util.Abort(_('cannot use --similarity with --bypass'))
4166 raise util.Abort(_('cannot use --similarity with --bypass'))
4167 if opts.get('exact') and opts.get('edit'):
4167 if opts.get('exact') and opts.get('edit'):
4168 raise util.Abort(_('cannot use --exact with --edit'))
4168 raise util.Abort(_('cannot use --exact with --edit'))
4169 if opts.get('exact') and opts.get('prefix'):
4169 if opts.get('exact') and opts.get('prefix'):
4170 raise util.Abort(_('cannot use --exact with --prefix'))
4170 raise util.Abort(_('cannot use --exact with --prefix'))
4171
4171
4172 if update:
4172 if update:
4173 cmdutil.checkunfinished(repo)
4173 cmdutil.checkunfinished(repo)
4174 if (opts.get('exact') or not opts.get('force')) and update:
4174 if (opts.get('exact') or not opts.get('force')) and update:
4175 cmdutil.bailifchanged(repo)
4175 cmdutil.bailifchanged(repo)
4176
4176
4177 base = opts["base"]
4177 base = opts["base"]
4178 wlock = lock = tr = None
4178 wlock = lock = tr = None
4179 msgs = []
4179 msgs = []
4180 ret = 0
4180 ret = 0
4181
4181
4182
4182
4183 try:
4183 try:
4184 try:
4184 try:
4185 wlock = repo.wlock()
4185 wlock = repo.wlock()
4186 repo.dirstate.beginparentchange()
4186 repo.dirstate.beginparentchange()
4187 if not opts.get('no_commit'):
4187 if not opts.get('no_commit'):
4188 lock = repo.lock()
4188 lock = repo.lock()
4189 tr = repo.transaction('import')
4189 tr = repo.transaction('import')
4190 parents = repo.parents()
4190 parents = repo.parents()
4191 for patchurl in patches:
4191 for patchurl in patches:
4192 if patchurl == '-':
4192 if patchurl == '-':
4193 ui.status(_('applying patch from stdin\n'))
4193 ui.status(_('applying patch from stdin\n'))
4194 patchfile = ui.fin
4194 patchfile = ui.fin
4195 patchurl = 'stdin' # for error message
4195 patchurl = 'stdin' # for error message
4196 else:
4196 else:
4197 patchurl = os.path.join(base, patchurl)
4197 patchurl = os.path.join(base, patchurl)
4198 ui.status(_('applying %s\n') % patchurl)
4198 ui.status(_('applying %s\n') % patchurl)
4199 patchfile = hg.openpath(ui, patchurl)
4199 patchfile = hg.openpath(ui, patchurl)
4200
4200
4201 haspatch = False
4201 haspatch = False
4202 for hunk in patch.split(patchfile):
4202 for hunk in patch.split(patchfile):
4203 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4203 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4204 parents, opts,
4204 parents, opts,
4205 msgs, hg.clean)
4205 msgs, hg.clean)
4206 if msg:
4206 if msg:
4207 haspatch = True
4207 haspatch = True
4208 ui.note(msg + '\n')
4208 ui.note(msg + '\n')
4209 if update or opts.get('exact'):
4209 if update or opts.get('exact'):
4210 parents = repo.parents()
4210 parents = repo.parents()
4211 else:
4211 else:
4212 parents = [repo[node]]
4212 parents = [repo[node]]
4213 if rej:
4213 if rej:
4214 ui.write_err(_("patch applied partially\n"))
4214 ui.write_err(_("patch applied partially\n"))
4215 ui.write_err(_("(fix the .rej files and run "
4215 ui.write_err(_("(fix the .rej files and run "
4216 "`hg commit --amend`)\n"))
4216 "`hg commit --amend`)\n"))
4217 ret = 1
4217 ret = 1
4218 break
4218 break
4219
4219
4220 if not haspatch:
4220 if not haspatch:
4221 raise util.Abort(_('%s: no diffs found') % patchurl)
4221 raise util.Abort(_('%s: no diffs found') % patchurl)
4222
4222
4223 if tr:
4223 if tr:
4224 tr.close()
4224 tr.close()
4225 if msgs:
4225 if msgs:
4226 repo.savecommitmessage('\n* * *\n'.join(msgs))
4226 repo.savecommitmessage('\n* * *\n'.join(msgs))
4227 repo.dirstate.endparentchange()
4227 repo.dirstate.endparentchange()
4228 return ret
4228 return ret
4229 except: # re-raises
4229 except: # re-raises
4230 # wlock.release() indirectly calls dirstate.write(): since
4230 # wlock.release() indirectly calls dirstate.write(): since
4231 # we're crashing, we do not want to change the working dir
4231 # we're crashing, we do not want to change the working dir
4232 # parent after all, so make sure it writes nothing
4232 # parent after all, so make sure it writes nothing
4233 repo.dirstate.invalidate()
4233 repo.dirstate.invalidate()
4234 raise
4234 raise
4235 finally:
4235 finally:
4236 if tr:
4236 if tr:
4237 tr.release()
4237 tr.release()
4238 release(lock, wlock)
4238 release(lock, wlock)
4239
4239
4240 @command('incoming|in',
4240 @command('incoming|in',
4241 [('f', 'force', None,
4241 [('f', 'force', None,
4242 _('run even if remote repository is unrelated')),
4242 _('run even if remote repository is unrelated')),
4243 ('n', 'newest-first', None, _('show newest record first')),
4243 ('n', 'newest-first', None, _('show newest record first')),
4244 ('', 'bundle', '',
4244 ('', 'bundle', '',
4245 _('file to store the bundles into'), _('FILE')),
4245 _('file to store the bundles into'), _('FILE')),
4246 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4246 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4247 ('B', 'bookmarks', False, _("compare bookmarks")),
4247 ('B', 'bookmarks', False, _("compare bookmarks")),
4248 ('b', 'branch', [],
4248 ('b', 'branch', [],
4249 _('a specific branch you would like to pull'), _('BRANCH')),
4249 _('a specific branch you would like to pull'), _('BRANCH')),
4250 ] + logopts + remoteopts + subrepoopts,
4250 ] + logopts + remoteopts + subrepoopts,
4251 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4251 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4252 def incoming(ui, repo, source="default", **opts):
4252 def incoming(ui, repo, source="default", **opts):
4253 """show new changesets found in source
4253 """show new changesets found in source
4254
4254
4255 Show new changesets found in the specified path/URL or the default
4255 Show new changesets found in the specified path/URL or the default
4256 pull location. These are the changesets that would have been pulled
4256 pull location. These are the changesets that would have been pulled
4257 if a pull at the time you issued this command.
4257 if a pull at the time you issued this command.
4258
4258
4259 See pull for valid source format details.
4259 See pull for valid source format details.
4260
4260
4261 .. container:: verbose
4261 .. container:: verbose
4262
4262
4263 For remote repository, using --bundle avoids downloading the
4263 For remote repository, using --bundle avoids downloading the
4264 changesets twice if the incoming is followed by a pull.
4264 changesets twice if the incoming is followed by a pull.
4265
4265
4266 Examples:
4266 Examples:
4267
4267
4268 - show incoming changes with patches and full description::
4268 - show incoming changes with patches and full description::
4269
4269
4270 hg incoming -vp
4270 hg incoming -vp
4271
4271
4272 - show incoming changes excluding merges, store a bundle::
4272 - show incoming changes excluding merges, store a bundle::
4273
4273
4274 hg in -vpM --bundle incoming.hg
4274 hg in -vpM --bundle incoming.hg
4275 hg pull incoming.hg
4275 hg pull incoming.hg
4276
4276
4277 - briefly list changes inside a bundle::
4277 - briefly list changes inside a bundle::
4278
4278
4279 hg in changes.hg -T "{desc|firstline}\\n"
4279 hg in changes.hg -T "{desc|firstline}\\n"
4280
4280
4281 Returns 0 if there are incoming changes, 1 otherwise.
4281 Returns 0 if there are incoming changes, 1 otherwise.
4282 """
4282 """
4283 if opts.get('graph'):
4283 if opts.get('graph'):
4284 cmdutil.checkunsupportedgraphflags([], opts)
4284 cmdutil.checkunsupportedgraphflags([], opts)
4285 def display(other, chlist, displayer):
4285 def display(other, chlist, displayer):
4286 revdag = cmdutil.graphrevs(other, chlist, opts)
4286 revdag = cmdutil.graphrevs(other, chlist, opts)
4287 showparents = [ctx.node() for ctx in repo[None].parents()]
4287 showparents = [ctx.node() for ctx in repo[None].parents()]
4288 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4288 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4289 graphmod.asciiedges)
4289 graphmod.asciiedges)
4290
4290
4291 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4291 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4292 return 0
4292 return 0
4293
4293
4294 if opts.get('bundle') and opts.get('subrepos'):
4294 if opts.get('bundle') and opts.get('subrepos'):
4295 raise util.Abort(_('cannot combine --bundle and --subrepos'))
4295 raise util.Abort(_('cannot combine --bundle and --subrepos'))
4296
4296
4297 if opts.get('bookmarks'):
4297 if opts.get('bookmarks'):
4298 source, branches = hg.parseurl(ui.expandpath(source),
4298 source, branches = hg.parseurl(ui.expandpath(source),
4299 opts.get('branch'))
4299 opts.get('branch'))
4300 other = hg.peer(repo, opts, source)
4300 other = hg.peer(repo, opts, source)
4301 if 'bookmarks' not in other.listkeys('namespaces'):
4301 if 'bookmarks' not in other.listkeys('namespaces'):
4302 ui.warn(_("remote doesn't support bookmarks\n"))
4302 ui.warn(_("remote doesn't support bookmarks\n"))
4303 return 0
4303 return 0
4304 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4304 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4305 return bookmarks.incoming(ui, repo, other)
4305 return bookmarks.incoming(ui, repo, other)
4306
4306
4307 repo._subtoppath = ui.expandpath(source)
4307 repo._subtoppath = ui.expandpath(source)
4308 try:
4308 try:
4309 return hg.incoming(ui, repo, source, opts)
4309 return hg.incoming(ui, repo, source, opts)
4310 finally:
4310 finally:
4311 del repo._subtoppath
4311 del repo._subtoppath
4312
4312
4313
4313
4314 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4314 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4315 norepo=True)
4315 norepo=True)
4316 def init(ui, dest=".", **opts):
4316 def init(ui, dest=".", **opts):
4317 """create a new repository in the given directory
4317 """create a new repository in the given directory
4318
4318
4319 Initialize a new repository in the given directory. If the given
4319 Initialize a new repository in the given directory. If the given
4320 directory does not exist, it will be created.
4320 directory does not exist, it will be created.
4321
4321
4322 If no directory is given, the current directory is used.
4322 If no directory is given, the current directory is used.
4323
4323
4324 It is possible to specify an ``ssh://`` URL as the destination.
4324 It is possible to specify an ``ssh://`` URL as the destination.
4325 See :hg:`help urls` for more information.
4325 See :hg:`help urls` for more information.
4326
4326
4327 Returns 0 on success.
4327 Returns 0 on success.
4328 """
4328 """
4329 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4329 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4330
4330
4331 @command('locate',
4331 @command('locate',
4332 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4332 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4333 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4333 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4334 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4334 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4335 ] + walkopts,
4335 ] + walkopts,
4336 _('[OPTION]... [PATTERN]...'))
4336 _('[OPTION]... [PATTERN]...'))
4337 def locate(ui, repo, *pats, **opts):
4337 def locate(ui, repo, *pats, **opts):
4338 """locate files matching specific patterns (DEPRECATED)
4338 """locate files matching specific patterns (DEPRECATED)
4339
4339
4340 Print files under Mercurial control in the working directory whose
4340 Print files under Mercurial control in the working directory whose
4341 names match the given patterns.
4341 names match the given patterns.
4342
4342
4343 By default, this command searches all directories in the working
4343 By default, this command searches all directories in the working
4344 directory. To search just the current directory and its
4344 directory. To search just the current directory and its
4345 subdirectories, use "--include .".
4345 subdirectories, use "--include .".
4346
4346
4347 If no patterns are given to match, this command prints the names
4347 If no patterns are given to match, this command prints the names
4348 of all files under Mercurial control in the working directory.
4348 of all files under Mercurial control in the working directory.
4349
4349
4350 If you want to feed the output of this command into the "xargs"
4350 If you want to feed the output of this command into the "xargs"
4351 command, use the -0 option to both this command and "xargs". This
4351 command, use the -0 option to both this command and "xargs". This
4352 will avoid the problem of "xargs" treating single filenames that
4352 will avoid the problem of "xargs" treating single filenames that
4353 contain whitespace as multiple filenames.
4353 contain whitespace as multiple filenames.
4354
4354
4355 See :hg:`help files` for a more versatile command.
4355 See :hg:`help files` for a more versatile command.
4356
4356
4357 Returns 0 if a match is found, 1 otherwise.
4357 Returns 0 if a match is found, 1 otherwise.
4358 """
4358 """
4359 if opts.get('print0'):
4359 if opts.get('print0'):
4360 end = '\0'
4360 end = '\0'
4361 else:
4361 else:
4362 end = '\n'
4362 end = '\n'
4363 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4363 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4364
4364
4365 ret = 1
4365 ret = 1
4366 ctx = repo[rev]
4366 ctx = repo[rev]
4367 m = scmutil.match(ctx, pats, opts, default='relglob')
4367 m = scmutil.match(ctx, pats, opts, default='relglob')
4368 m.bad = lambda x, y: False
4368 m.bad = lambda x, y: False
4369
4369
4370 for abs in ctx.matches(m):
4370 for abs in ctx.matches(m):
4371 if opts.get('fullpath'):
4371 if opts.get('fullpath'):
4372 ui.write(repo.wjoin(abs), end)
4372 ui.write(repo.wjoin(abs), end)
4373 else:
4373 else:
4374 ui.write(((pats and m.rel(abs)) or abs), end)
4374 ui.write(((pats and m.rel(abs)) or abs), end)
4375 ret = 0
4375 ret = 0
4376
4376
4377 return ret
4377 return ret
4378
4378
4379 @command('^log|history',
4379 @command('^log|history',
4380 [('f', 'follow', None,
4380 [('f', 'follow', None,
4381 _('follow changeset history, or file history across copies and renames')),
4381 _('follow changeset history, or file history across copies and renames')),
4382 ('', 'follow-first', None,
4382 ('', 'follow-first', None,
4383 _('only follow the first parent of merge changesets (DEPRECATED)')),
4383 _('only follow the first parent of merge changesets (DEPRECATED)')),
4384 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4384 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4385 ('C', 'copies', None, _('show copied files')),
4385 ('C', 'copies', None, _('show copied files')),
4386 ('k', 'keyword', [],
4386 ('k', 'keyword', [],
4387 _('do case-insensitive search for a given text'), _('TEXT')),
4387 _('do case-insensitive search for a given text'), _('TEXT')),
4388 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4388 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4389 ('', 'removed', None, _('include revisions where files were removed')),
4389 ('', 'removed', None, _('include revisions where files were removed')),
4390 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4390 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4391 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4391 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4392 ('', 'only-branch', [],
4392 ('', 'only-branch', [],
4393 _('show only changesets within the given named branch (DEPRECATED)'),
4393 _('show only changesets within the given named branch (DEPRECATED)'),
4394 _('BRANCH')),
4394 _('BRANCH')),
4395 ('b', 'branch', [],
4395 ('b', 'branch', [],
4396 _('show changesets within the given named branch'), _('BRANCH')),
4396 _('show changesets within the given named branch'), _('BRANCH')),
4397 ('P', 'prune', [],
4397 ('P', 'prune', [],
4398 _('do not display revision or any of its ancestors'), _('REV')),
4398 _('do not display revision or any of its ancestors'), _('REV')),
4399 ] + logopts + walkopts,
4399 ] + logopts + walkopts,
4400 _('[OPTION]... [FILE]'),
4400 _('[OPTION]... [FILE]'),
4401 inferrepo=True)
4401 inferrepo=True)
4402 def log(ui, repo, *pats, **opts):
4402 def log(ui, repo, *pats, **opts):
4403 """show revision history of entire repository or files
4403 """show revision history of entire repository or files
4404
4404
4405 Print the revision history of the specified files or the entire
4405 Print the revision history of the specified files or the entire
4406 project.
4406 project.
4407
4407
4408 If no revision range is specified, the default is ``tip:0`` unless
4408 If no revision range is specified, the default is ``tip:0`` unless
4409 --follow is set, in which case the working directory parent is
4409 --follow is set, in which case the working directory parent is
4410 used as the starting revision.
4410 used as the starting revision.
4411
4411
4412 File history is shown without following rename or copy history of
4412 File history is shown without following rename or copy history of
4413 files. Use -f/--follow with a filename to follow history across
4413 files. Use -f/--follow with a filename to follow history across
4414 renames and copies. --follow without a filename will only show
4414 renames and copies. --follow without a filename will only show
4415 ancestors or descendants of the starting revision.
4415 ancestors or descendants of the starting revision.
4416
4416
4417 By default this command prints revision number and changeset id,
4417 By default this command prints revision number and changeset id,
4418 tags, non-trivial parents, user, date and time, and a summary for
4418 tags, non-trivial parents, user, date and time, and a summary for
4419 each commit. When the -v/--verbose switch is used, the list of
4419 each commit. When the -v/--verbose switch is used, the list of
4420 changed files and full commit message are shown.
4420 changed files and full commit message are shown.
4421
4421
4422 With --graph the revisions are shown as an ASCII art DAG with the most
4422 With --graph the revisions are shown as an ASCII art DAG with the most
4423 recent changeset at the top.
4423 recent changeset at the top.
4424 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4424 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4425 and '+' represents a fork where the changeset from the lines below is a
4425 and '+' represents a fork where the changeset from the lines below is a
4426 parent of the 'o' merge on the same line.
4426 parent of the 'o' merge on the same line.
4427
4427
4428 .. note::
4428 .. note::
4429
4429
4430 log -p/--patch may generate unexpected diff output for merge
4430 log -p/--patch may generate unexpected diff output for merge
4431 changesets, as it will only compare the merge changeset against
4431 changesets, as it will only compare the merge changeset against
4432 its first parent. Also, only files different from BOTH parents
4432 its first parent. Also, only files different from BOTH parents
4433 will appear in files:.
4433 will appear in files:.
4434
4434
4435 .. note::
4435 .. note::
4436
4436
4437 for performance reasons, log FILE may omit duplicate changes
4437 for performance reasons, log FILE may omit duplicate changes
4438 made on branches and will not show removals or mode changes. To
4438 made on branches and will not show removals or mode changes. To
4439 see all such changes, use the --removed switch.
4439 see all such changes, use the --removed switch.
4440
4440
4441 .. container:: verbose
4441 .. container:: verbose
4442
4442
4443 Some examples:
4443 Some examples:
4444
4444
4445 - changesets with full descriptions and file lists::
4445 - changesets with full descriptions and file lists::
4446
4446
4447 hg log -v
4447 hg log -v
4448
4448
4449 - changesets ancestral to the working directory::
4449 - changesets ancestral to the working directory::
4450
4450
4451 hg log -f
4451 hg log -f
4452
4452
4453 - last 10 commits on the current branch::
4453 - last 10 commits on the current branch::
4454
4454
4455 hg log -l 10 -b .
4455 hg log -l 10 -b .
4456
4456
4457 - changesets showing all modifications of a file, including removals::
4457 - changesets showing all modifications of a file, including removals::
4458
4458
4459 hg log --removed file.c
4459 hg log --removed file.c
4460
4460
4461 - all changesets that touch a directory, with diffs, excluding merges::
4461 - all changesets that touch a directory, with diffs, excluding merges::
4462
4462
4463 hg log -Mp lib/
4463 hg log -Mp lib/
4464
4464
4465 - all revision numbers that match a keyword::
4465 - all revision numbers that match a keyword::
4466
4466
4467 hg log -k bug --template "{rev}\\n"
4467 hg log -k bug --template "{rev}\\n"
4468
4468
4469 - list available log templates::
4469 - list available log templates::
4470
4470
4471 hg log -T list
4471 hg log -T list
4472
4472
4473 - check if a given changeset is included in a tagged release::
4473 - check if a given changeset is included in a tagged release::
4474
4474
4475 hg log -r "a21ccf and ancestor(1.9)"
4475 hg log -r "a21ccf and ancestor(1.9)"
4476
4476
4477 - find all changesets by some user in a date range::
4477 - find all changesets by some user in a date range::
4478
4478
4479 hg log -k alice -d "may 2008 to jul 2008"
4479 hg log -k alice -d "may 2008 to jul 2008"
4480
4480
4481 - summary of all changesets after the last tag::
4481 - summary of all changesets after the last tag::
4482
4482
4483 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4483 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4484
4484
4485 See :hg:`help dates` for a list of formats valid for -d/--date.
4485 See :hg:`help dates` for a list of formats valid for -d/--date.
4486
4486
4487 See :hg:`help revisions` and :hg:`help revsets` for more about
4487 See :hg:`help revisions` and :hg:`help revsets` for more about
4488 specifying revisions.
4488 specifying revisions.
4489
4489
4490 See :hg:`help templates` for more about pre-packaged styles and
4490 See :hg:`help templates` for more about pre-packaged styles and
4491 specifying custom templates.
4491 specifying custom templates.
4492
4492
4493 Returns 0 on success.
4493 Returns 0 on success.
4494
4494
4495 """
4495 """
4496 if opts.get('follow') and opts.get('rev'):
4496 if opts.get('follow') and opts.get('rev'):
4497 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
4497 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
4498 del opts['follow']
4498 del opts['follow']
4499
4499
4500 if opts.get('graph'):
4500 if opts.get('graph'):
4501 return cmdutil.graphlog(ui, repo, *pats, **opts)
4501 return cmdutil.graphlog(ui, repo, *pats, **opts)
4502
4502
4503 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4503 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4504 limit = cmdutil.loglimit(opts)
4504 limit = cmdutil.loglimit(opts)
4505 count = 0
4505 count = 0
4506
4506
4507 getrenamed = None
4507 getrenamed = None
4508 if opts.get('copies'):
4508 if opts.get('copies'):
4509 endrev = None
4509 endrev = None
4510 if opts.get('rev'):
4510 if opts.get('rev'):
4511 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4511 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4512 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4512 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4513
4513
4514 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4514 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4515 for rev in revs:
4515 for rev in revs:
4516 if count == limit:
4516 if count == limit:
4517 break
4517 break
4518 ctx = repo[rev]
4518 ctx = repo[rev]
4519 copies = None
4519 copies = None
4520 if getrenamed is not None and rev:
4520 if getrenamed is not None and rev:
4521 copies = []
4521 copies = []
4522 for fn in ctx.files():
4522 for fn in ctx.files():
4523 rename = getrenamed(fn, rev)
4523 rename = getrenamed(fn, rev)
4524 if rename:
4524 if rename:
4525 copies.append((fn, rename[0]))
4525 copies.append((fn, rename[0]))
4526 if filematcher:
4526 if filematcher:
4527 revmatchfn = filematcher(ctx.rev())
4527 revmatchfn = filematcher(ctx.rev())
4528 else:
4528 else:
4529 revmatchfn = None
4529 revmatchfn = None
4530 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4530 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4531 if displayer.flush(rev):
4531 if displayer.flush(rev):
4532 count += 1
4532 count += 1
4533
4533
4534 displayer.close()
4534 displayer.close()
4535
4535
4536 @command('manifest',
4536 @command('manifest',
4537 [('r', 'rev', '', _('revision to display'), _('REV')),
4537 [('r', 'rev', '', _('revision to display'), _('REV')),
4538 ('', 'all', False, _("list files from all revisions"))]
4538 ('', 'all', False, _("list files from all revisions"))]
4539 + formatteropts,
4539 + formatteropts,
4540 _('[-r REV]'))
4540 _('[-r REV]'))
4541 def manifest(ui, repo, node=None, rev=None, **opts):
4541 def manifest(ui, repo, node=None, rev=None, **opts):
4542 """output the current or given revision of the project manifest
4542 """output the current or given revision of the project manifest
4543
4543
4544 Print a list of version controlled files for the given revision.
4544 Print a list of version controlled files for the given revision.
4545 If no revision is given, the first parent of the working directory
4545 If no revision is given, the first parent of the working directory
4546 is used, or the null revision if no revision is checked out.
4546 is used, or the null revision if no revision is checked out.
4547
4547
4548 With -v, print file permissions, symlink and executable bits.
4548 With -v, print file permissions, symlink and executable bits.
4549 With --debug, print file revision hashes.
4549 With --debug, print file revision hashes.
4550
4550
4551 If option --all is specified, the list of all files from all revisions
4551 If option --all is specified, the list of all files from all revisions
4552 is printed. This includes deleted and renamed files.
4552 is printed. This includes deleted and renamed files.
4553
4553
4554 Returns 0 on success.
4554 Returns 0 on success.
4555 """
4555 """
4556
4556
4557 fm = ui.formatter('manifest', opts)
4557 fm = ui.formatter('manifest', opts)
4558
4558
4559 if opts.get('all'):
4559 if opts.get('all'):
4560 if rev or node:
4560 if rev or node:
4561 raise util.Abort(_("can't specify a revision with --all"))
4561 raise util.Abort(_("can't specify a revision with --all"))
4562
4562
4563 res = []
4563 res = []
4564 prefix = "data/"
4564 prefix = "data/"
4565 suffix = ".i"
4565 suffix = ".i"
4566 plen = len(prefix)
4566 plen = len(prefix)
4567 slen = len(suffix)
4567 slen = len(suffix)
4568 lock = repo.lock()
4568 lock = repo.lock()
4569 try:
4569 try:
4570 for fn, b, size in repo.store.datafiles():
4570 for fn, b, size in repo.store.datafiles():
4571 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4571 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4572 res.append(fn[plen:-slen])
4572 res.append(fn[plen:-slen])
4573 finally:
4573 finally:
4574 lock.release()
4574 lock.release()
4575 for f in res:
4575 for f in res:
4576 fm.startitem()
4576 fm.startitem()
4577 fm.write("path", '%s\n', f)
4577 fm.write("path", '%s\n', f)
4578 fm.end()
4578 fm.end()
4579 return
4579 return
4580
4580
4581 if rev and node:
4581 if rev and node:
4582 raise util.Abort(_("please specify just one revision"))
4582 raise util.Abort(_("please specify just one revision"))
4583
4583
4584 if not node:
4584 if not node:
4585 node = rev
4585 node = rev
4586
4586
4587 char = {'l': '@', 'x': '*', '': ''}
4587 char = {'l': '@', 'x': '*', '': ''}
4588 mode = {'l': '644', 'x': '755', '': '644'}
4588 mode = {'l': '644', 'x': '755', '': '644'}
4589 ctx = scmutil.revsingle(repo, node)
4589 ctx = scmutil.revsingle(repo, node)
4590 mf = ctx.manifest()
4590 mf = ctx.manifest()
4591 for f in ctx:
4591 for f in ctx:
4592 fm.startitem()
4592 fm.startitem()
4593 fl = ctx[f].flags()
4593 fl = ctx[f].flags()
4594 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4594 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4595 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4595 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4596 fm.write('path', '%s\n', f)
4596 fm.write('path', '%s\n', f)
4597 fm.end()
4597 fm.end()
4598
4598
4599 @command('^merge',
4599 @command('^merge',
4600 [('f', 'force', None,
4600 [('f', 'force', None,
4601 _('force a merge including outstanding changes (DEPRECATED)')),
4601 _('force a merge including outstanding changes (DEPRECATED)')),
4602 ('r', 'rev', '', _('revision to merge'), _('REV')),
4602 ('r', 'rev', '', _('revision to merge'), _('REV')),
4603 ('P', 'preview', None,
4603 ('P', 'preview', None,
4604 _('review revisions to merge (no merge is performed)'))
4604 _('review revisions to merge (no merge is performed)'))
4605 ] + mergetoolopts,
4605 ] + mergetoolopts,
4606 _('[-P] [-f] [[-r] REV]'))
4606 _('[-P] [-f] [[-r] REV]'))
4607 def merge(ui, repo, node=None, **opts):
4607 def merge(ui, repo, node=None, **opts):
4608 """merge another revision into working directory
4608 """merge another revision into working directory
4609
4609
4610 The current working directory is updated with all changes made in
4610 The current working directory is updated with all changes made in
4611 the requested revision since the last common predecessor revision.
4611 the requested revision since the last common predecessor revision.
4612
4612
4613 Files that changed between either parent are marked as changed for
4613 Files that changed between either parent are marked as changed for
4614 the next commit and a commit must be performed before any further
4614 the next commit and a commit must be performed before any further
4615 updates to the repository are allowed. The next commit will have
4615 updates to the repository are allowed. The next commit will have
4616 two parents.
4616 two parents.
4617
4617
4618 ``--tool`` can be used to specify the merge tool used for file
4618 ``--tool`` can be used to specify the merge tool used for file
4619 merges. It overrides the HGMERGE environment variable and your
4619 merges. It overrides the HGMERGE environment variable and your
4620 configuration files. See :hg:`help merge-tools` for options.
4620 configuration files. See :hg:`help merge-tools` for options.
4621
4621
4622 If no revision is specified, the working directory's parent is a
4622 If no revision is specified, the working directory's parent is a
4623 head revision, and the current branch contains exactly one other
4623 head revision, and the current branch contains exactly one other
4624 head, the other head is merged with by default. Otherwise, an
4624 head, the other head is merged with by default. Otherwise, an
4625 explicit revision with which to merge with must be provided.
4625 explicit revision with which to merge with must be provided.
4626
4626
4627 :hg:`resolve` must be used to resolve unresolved files.
4627 :hg:`resolve` must be used to resolve unresolved files.
4628
4628
4629 To undo an uncommitted merge, use :hg:`update --clean .` which
4629 To undo an uncommitted merge, use :hg:`update --clean .` which
4630 will check out a clean copy of the original merge parent, losing
4630 will check out a clean copy of the original merge parent, losing
4631 all changes.
4631 all changes.
4632
4632
4633 Returns 0 on success, 1 if there are unresolved files.
4633 Returns 0 on success, 1 if there are unresolved files.
4634 """
4634 """
4635
4635
4636 if opts.get('rev') and node:
4636 if opts.get('rev') and node:
4637 raise util.Abort(_("please specify just one revision"))
4637 raise util.Abort(_("please specify just one revision"))
4638 if not node:
4638 if not node:
4639 node = opts.get('rev')
4639 node = opts.get('rev')
4640
4640
4641 if node:
4641 if node:
4642 node = scmutil.revsingle(repo, node).node()
4642 node = scmutil.revsingle(repo, node).node()
4643
4643
4644 if not node and repo._bookmarkcurrent:
4644 if not node and repo._bookmarkcurrent:
4645 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4645 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4646 curhead = repo[repo._bookmarkcurrent].node()
4646 curhead = repo[repo._bookmarkcurrent].node()
4647 if len(bmheads) == 2:
4647 if len(bmheads) == 2:
4648 if curhead == bmheads[0]:
4648 if curhead == bmheads[0]:
4649 node = bmheads[1]
4649 node = bmheads[1]
4650 else:
4650 else:
4651 node = bmheads[0]
4651 node = bmheads[0]
4652 elif len(bmheads) > 2:
4652 elif len(bmheads) > 2:
4653 raise util.Abort(_("multiple matching bookmarks to merge - "
4653 raise util.Abort(_("multiple matching bookmarks to merge - "
4654 "please merge with an explicit rev or bookmark"),
4654 "please merge with an explicit rev or bookmark"),
4655 hint=_("run 'hg heads' to see all heads"))
4655 hint=_("run 'hg heads' to see all heads"))
4656 elif len(bmheads) <= 1:
4656 elif len(bmheads) <= 1:
4657 raise util.Abort(_("no matching bookmark to merge - "
4657 raise util.Abort(_("no matching bookmark to merge - "
4658 "please merge with an explicit rev or bookmark"),
4658 "please merge with an explicit rev or bookmark"),
4659 hint=_("run 'hg heads' to see all heads"))
4659 hint=_("run 'hg heads' to see all heads"))
4660
4660
4661 if not node and not repo._bookmarkcurrent:
4661 if not node and not repo._bookmarkcurrent:
4662 branch = repo[None].branch()
4662 branch = repo[None].branch()
4663 bheads = repo.branchheads(branch)
4663 bheads = repo.branchheads(branch)
4664 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4664 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4665
4665
4666 if len(nbhs) > 2:
4666 if len(nbhs) > 2:
4667 raise util.Abort(_("branch '%s' has %d heads - "
4667 raise util.Abort(_("branch '%s' has %d heads - "
4668 "please merge with an explicit rev")
4668 "please merge with an explicit rev")
4669 % (branch, len(bheads)),
4669 % (branch, len(bheads)),
4670 hint=_("run 'hg heads .' to see heads"))
4670 hint=_("run 'hg heads .' to see heads"))
4671
4671
4672 parent = repo.dirstate.p1()
4672 parent = repo.dirstate.p1()
4673 if len(nbhs) <= 1:
4673 if len(nbhs) <= 1:
4674 if len(bheads) > 1:
4674 if len(bheads) > 1:
4675 raise util.Abort(_("heads are bookmarked - "
4675 raise util.Abort(_("heads are bookmarked - "
4676 "please merge with an explicit rev"),
4676 "please merge with an explicit rev"),
4677 hint=_("run 'hg heads' to see all heads"))
4677 hint=_("run 'hg heads' to see all heads"))
4678 if len(repo.heads()) > 1:
4678 if len(repo.heads()) > 1:
4679 raise util.Abort(_("branch '%s' has one head - "
4679 raise util.Abort(_("branch '%s' has one head - "
4680 "please merge with an explicit rev")
4680 "please merge with an explicit rev")
4681 % branch,
4681 % branch,
4682 hint=_("run 'hg heads' to see all heads"))
4682 hint=_("run 'hg heads' to see all heads"))
4683 msg, hint = _('nothing to merge'), None
4683 msg, hint = _('nothing to merge'), None
4684 if parent != repo.lookup(branch):
4684 if parent != repo.lookup(branch):
4685 hint = _("use 'hg update' instead")
4685 hint = _("use 'hg update' instead")
4686 raise util.Abort(msg, hint=hint)
4686 raise util.Abort(msg, hint=hint)
4687
4687
4688 if parent not in bheads:
4688 if parent not in bheads:
4689 raise util.Abort(_('working directory not at a head revision'),
4689 raise util.Abort(_('working directory not at a head revision'),
4690 hint=_("use 'hg update' or merge with an "
4690 hint=_("use 'hg update' or merge with an "
4691 "explicit revision"))
4691 "explicit revision"))
4692 if parent == nbhs[0]:
4692 if parent == nbhs[0]:
4693 node = nbhs[-1]
4693 node = nbhs[-1]
4694 else:
4694 else:
4695 node = nbhs[0]
4695 node = nbhs[0]
4696
4696
4697 if opts.get('preview'):
4697 if opts.get('preview'):
4698 # find nodes that are ancestors of p2 but not of p1
4698 # find nodes that are ancestors of p2 but not of p1
4699 p1 = repo.lookup('.')
4699 p1 = repo.lookup('.')
4700 p2 = repo.lookup(node)
4700 p2 = repo.lookup(node)
4701 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4701 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4702
4702
4703 displayer = cmdutil.show_changeset(ui, repo, opts)
4703 displayer = cmdutil.show_changeset(ui, repo, opts)
4704 for node in nodes:
4704 for node in nodes:
4705 displayer.show(repo[node])
4705 displayer.show(repo[node])
4706 displayer.close()
4706 displayer.close()
4707 return 0
4707 return 0
4708
4708
4709 try:
4709 try:
4710 # ui.forcemerge is an internal variable, do not document
4710 # ui.forcemerge is an internal variable, do not document
4711 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4711 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4712 return hg.merge(repo, node, force=opts.get('force'))
4712 return hg.merge(repo, node, force=opts.get('force'))
4713 finally:
4713 finally:
4714 ui.setconfig('ui', 'forcemerge', '', 'merge')
4714 ui.setconfig('ui', 'forcemerge', '', 'merge')
4715
4715
4716 @command('outgoing|out',
4716 @command('outgoing|out',
4717 [('f', 'force', None, _('run even when the destination is unrelated')),
4717 [('f', 'force', None, _('run even when the destination is unrelated')),
4718 ('r', 'rev', [],
4718 ('r', 'rev', [],
4719 _('a changeset intended to be included in the destination'), _('REV')),
4719 _('a changeset intended to be included in the destination'), _('REV')),
4720 ('n', 'newest-first', None, _('show newest record first')),
4720 ('n', 'newest-first', None, _('show newest record first')),
4721 ('B', 'bookmarks', False, _('compare bookmarks')),
4721 ('B', 'bookmarks', False, _('compare bookmarks')),
4722 ('b', 'branch', [], _('a specific branch you would like to push'),
4722 ('b', 'branch', [], _('a specific branch you would like to push'),
4723 _('BRANCH')),
4723 _('BRANCH')),
4724 ] + logopts + remoteopts + subrepoopts,
4724 ] + logopts + remoteopts + subrepoopts,
4725 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4725 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4726 def outgoing(ui, repo, dest=None, **opts):
4726 def outgoing(ui, repo, dest=None, **opts):
4727 """show changesets not found in the destination
4727 """show changesets not found in the destination
4728
4728
4729 Show changesets not found in the specified destination repository
4729 Show changesets not found in the specified destination repository
4730 or the default push location. These are the changesets that would
4730 or the default push location. These are the changesets that would
4731 be pushed if a push was requested.
4731 be pushed if a push was requested.
4732
4732
4733 See pull for details of valid destination formats.
4733 See pull for details of valid destination formats.
4734
4734
4735 Returns 0 if there are outgoing changes, 1 otherwise.
4735 Returns 0 if there are outgoing changes, 1 otherwise.
4736 """
4736 """
4737 if opts.get('graph'):
4737 if opts.get('graph'):
4738 cmdutil.checkunsupportedgraphflags([], opts)
4738 cmdutil.checkunsupportedgraphflags([], opts)
4739 o, other = hg._outgoing(ui, repo, dest, opts)
4739 o, other = hg._outgoing(ui, repo, dest, opts)
4740 if not o:
4740 if not o:
4741 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4741 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4742 return
4742 return
4743
4743
4744 revdag = cmdutil.graphrevs(repo, o, opts)
4744 revdag = cmdutil.graphrevs(repo, o, opts)
4745 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4745 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4746 showparents = [ctx.node() for ctx in repo[None].parents()]
4746 showparents = [ctx.node() for ctx in repo[None].parents()]
4747 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4747 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4748 graphmod.asciiedges)
4748 graphmod.asciiedges)
4749 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4749 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4750 return 0
4750 return 0
4751
4751
4752 if opts.get('bookmarks'):
4752 if opts.get('bookmarks'):
4753 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4753 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4754 dest, branches = hg.parseurl(dest, opts.get('branch'))
4754 dest, branches = hg.parseurl(dest, opts.get('branch'))
4755 other = hg.peer(repo, opts, dest)
4755 other = hg.peer(repo, opts, dest)
4756 if 'bookmarks' not in other.listkeys('namespaces'):
4756 if 'bookmarks' not in other.listkeys('namespaces'):
4757 ui.warn(_("remote doesn't support bookmarks\n"))
4757 ui.warn(_("remote doesn't support bookmarks\n"))
4758 return 0
4758 return 0
4759 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4759 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4760 return bookmarks.outgoing(ui, repo, other)
4760 return bookmarks.outgoing(ui, repo, other)
4761
4761
4762 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4762 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4763 try:
4763 try:
4764 return hg.outgoing(ui, repo, dest, opts)
4764 return hg.outgoing(ui, repo, dest, opts)
4765 finally:
4765 finally:
4766 del repo._subtoppath
4766 del repo._subtoppath
4767
4767
4768 @command('parents',
4768 @command('parents',
4769 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4769 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4770 ] + templateopts,
4770 ] + templateopts,
4771 _('[-r REV] [FILE]'),
4771 _('[-r REV] [FILE]'),
4772 inferrepo=True)
4772 inferrepo=True)
4773 def parents(ui, repo, file_=None, **opts):
4773 def parents(ui, repo, file_=None, **opts):
4774 """show the parents of the working directory or revision (DEPRECATED)
4774 """show the parents of the working directory or revision (DEPRECATED)
4775
4775
4776 Print the working directory's parent revisions. If a revision is
4776 Print the working directory's parent revisions. If a revision is
4777 given via -r/--rev, the parent of that revision will be printed.
4777 given via -r/--rev, the parent of that revision will be printed.
4778 If a file argument is given, the revision in which the file was
4778 If a file argument is given, the revision in which the file was
4779 last changed (before the working directory revision or the
4779 last changed (before the working directory revision or the
4780 argument to --rev if given) is printed.
4780 argument to --rev if given) is printed.
4781
4781
4782 See :hg:`summary` and :hg:`help revsets` for related information.
4782 See :hg:`summary` and :hg:`help revsets` for related information.
4783
4783
4784 Returns 0 on success.
4784 Returns 0 on success.
4785 """
4785 """
4786
4786
4787 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4787 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4788
4788
4789 if file_:
4789 if file_:
4790 m = scmutil.match(ctx, (file_,), opts)
4790 m = scmutil.match(ctx, (file_,), opts)
4791 if m.anypats() or len(m.files()) != 1:
4791 if m.anypats() or len(m.files()) != 1:
4792 raise util.Abort(_('can only specify an explicit filename'))
4792 raise util.Abort(_('can only specify an explicit filename'))
4793 file_ = m.files()[0]
4793 file_ = m.files()[0]
4794 filenodes = []
4794 filenodes = []
4795 for cp in ctx.parents():
4795 for cp in ctx.parents():
4796 if not cp:
4796 if not cp:
4797 continue
4797 continue
4798 try:
4798 try:
4799 filenodes.append(cp.filenode(file_))
4799 filenodes.append(cp.filenode(file_))
4800 except error.LookupError:
4800 except error.LookupError:
4801 pass
4801 pass
4802 if not filenodes:
4802 if not filenodes:
4803 raise util.Abort(_("'%s' not found in manifest!") % file_)
4803 raise util.Abort(_("'%s' not found in manifest!") % file_)
4804 p = []
4804 p = []
4805 for fn in filenodes:
4805 for fn in filenodes:
4806 fctx = repo.filectx(file_, fileid=fn)
4806 fctx = repo.filectx(file_, fileid=fn)
4807 p.append(fctx.node())
4807 p.append(fctx.node())
4808 else:
4808 else:
4809 p = [cp.node() for cp in ctx.parents()]
4809 p = [cp.node() for cp in ctx.parents()]
4810
4810
4811 displayer = cmdutil.show_changeset(ui, repo, opts)
4811 displayer = cmdutil.show_changeset(ui, repo, opts)
4812 for n in p:
4812 for n in p:
4813 if n != nullid:
4813 if n != nullid:
4814 displayer.show(repo[n])
4814 displayer.show(repo[n])
4815 displayer.close()
4815 displayer.close()
4816
4816
4817 @command('paths', [], _('[NAME]'), optionalrepo=True)
4817 @command('paths', [], _('[NAME]'), optionalrepo=True)
4818 def paths(ui, repo, search=None):
4818 def paths(ui, repo, search=None):
4819 """show aliases for remote repositories
4819 """show aliases for remote repositories
4820
4820
4821 Show definition of symbolic path name NAME. If no name is given,
4821 Show definition of symbolic path name NAME. If no name is given,
4822 show definition of all available names.
4822 show definition of all available names.
4823
4823
4824 Option -q/--quiet suppresses all output when searching for NAME
4824 Option -q/--quiet suppresses all output when searching for NAME
4825 and shows only the path names when listing all definitions.
4825 and shows only the path names when listing all definitions.
4826
4826
4827 Path names are defined in the [paths] section of your
4827 Path names are defined in the [paths] section of your
4828 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4828 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4829 repository, ``.hg/hgrc`` is used, too.
4829 repository, ``.hg/hgrc`` is used, too.
4830
4830
4831 The path names ``default`` and ``default-push`` have a special
4831 The path names ``default`` and ``default-push`` have a special
4832 meaning. When performing a push or pull operation, they are used
4832 meaning. When performing a push or pull operation, they are used
4833 as fallbacks if no location is specified on the command-line.
4833 as fallbacks if no location is specified on the command-line.
4834 When ``default-push`` is set, it will be used for push and
4834 When ``default-push`` is set, it will be used for push and
4835 ``default`` will be used for pull; otherwise ``default`` is used
4835 ``default`` will be used for pull; otherwise ``default`` is used
4836 as the fallback for both. When cloning a repository, the clone
4836 as the fallback for both. When cloning a repository, the clone
4837 source is written as ``default`` in ``.hg/hgrc``. Note that
4837 source is written as ``default`` in ``.hg/hgrc``. Note that
4838 ``default`` and ``default-push`` apply to all inbound (e.g.
4838 ``default`` and ``default-push`` apply to all inbound (e.g.
4839 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4839 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4840 :hg:`bundle`) operations.
4840 :hg:`bundle`) operations.
4841
4841
4842 See :hg:`help urls` for more information.
4842 See :hg:`help urls` for more information.
4843
4843
4844 Returns 0 on success.
4844 Returns 0 on success.
4845 """
4845 """
4846 if search:
4846 if search:
4847 for name, path in sorted(ui.paths.iteritems()):
4847 for name, path in sorted(ui.paths.iteritems()):
4848 if name == search:
4848 if name == search:
4849 ui.status("%s\n" % util.hidepassword(path.loc))
4849 ui.status("%s\n" % util.hidepassword(path.loc))
4850 return
4850 return
4851 if not ui.quiet:
4851 if not ui.quiet:
4852 ui.warn(_("not found!\n"))
4852 ui.warn(_("not found!\n"))
4853 return 1
4853 return 1
4854 else:
4854 else:
4855 for name, path in sorted(ui.paths.iteritems()):
4855 for name, path in sorted(ui.paths.iteritems()):
4856 if ui.quiet:
4856 if ui.quiet:
4857 ui.write("%s\n" % name)
4857 ui.write("%s\n" % name)
4858 else:
4858 else:
4859 ui.write("%s = %s\n" % (name,
4859 ui.write("%s = %s\n" % (name,
4860 util.hidepassword(path.loc)))
4860 util.hidepassword(path.loc)))
4861
4861
4862 @command('phase',
4862 @command('phase',
4863 [('p', 'public', False, _('set changeset phase to public')),
4863 [('p', 'public', False, _('set changeset phase to public')),
4864 ('d', 'draft', False, _('set changeset phase to draft')),
4864 ('d', 'draft', False, _('set changeset phase to draft')),
4865 ('s', 'secret', False, _('set changeset phase to secret')),
4865 ('s', 'secret', False, _('set changeset phase to secret')),
4866 ('f', 'force', False, _('allow to move boundary backward')),
4866 ('f', 'force', False, _('allow to move boundary backward')),
4867 ('r', 'rev', [], _('target revision'), _('REV')),
4867 ('r', 'rev', [], _('target revision'), _('REV')),
4868 ],
4868 ],
4869 _('[-p|-d|-s] [-f] [-r] REV...'))
4869 _('[-p|-d|-s] [-f] [-r] REV...'))
4870 def phase(ui, repo, *revs, **opts):
4870 def phase(ui, repo, *revs, **opts):
4871 """set or show the current phase name
4871 """set or show the current phase name
4872
4872
4873 With no argument, show the phase name of specified revisions.
4873 With no argument, show the phase name of specified revisions.
4874
4874
4875 With one of -p/--public, -d/--draft or -s/--secret, change the
4875 With one of -p/--public, -d/--draft or -s/--secret, change the
4876 phase value of the specified revisions.
4876 phase value of the specified revisions.
4877
4877
4878 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4878 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4879 lower phase to an higher phase. Phases are ordered as follows::
4879 lower phase to an higher phase. Phases are ordered as follows::
4880
4880
4881 public < draft < secret
4881 public < draft < secret
4882
4882
4883 Returns 0 on success, 1 if no phases were changed or some could not
4883 Returns 0 on success, 1 if no phases were changed or some could not
4884 be changed.
4884 be changed.
4885 """
4885 """
4886 # search for a unique phase argument
4886 # search for a unique phase argument
4887 targetphase = None
4887 targetphase = None
4888 for idx, name in enumerate(phases.phasenames):
4888 for idx, name in enumerate(phases.phasenames):
4889 if opts[name]:
4889 if opts[name]:
4890 if targetphase is not None:
4890 if targetphase is not None:
4891 raise util.Abort(_('only one phase can be specified'))
4891 raise util.Abort(_('only one phase can be specified'))
4892 targetphase = idx
4892 targetphase = idx
4893
4893
4894 # look for specified revision
4894 # look for specified revision
4895 revs = list(revs)
4895 revs = list(revs)
4896 revs.extend(opts['rev'])
4896 revs.extend(opts['rev'])
4897 if not revs:
4897 if not revs:
4898 raise util.Abort(_('no revisions specified'))
4898 raise util.Abort(_('no revisions specified'))
4899
4899
4900 revs = scmutil.revrange(repo, revs)
4900 revs = scmutil.revrange(repo, revs)
4901
4901
4902 lock = None
4902 lock = None
4903 ret = 0
4903 ret = 0
4904 if targetphase is None:
4904 if targetphase is None:
4905 # display
4905 # display
4906 for r in revs:
4906 for r in revs:
4907 ctx = repo[r]
4907 ctx = repo[r]
4908 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4908 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4909 else:
4909 else:
4910 tr = None
4910 tr = None
4911 lock = repo.lock()
4911 lock = repo.lock()
4912 try:
4912 try:
4913 tr = repo.transaction("phase")
4913 tr = repo.transaction("phase")
4914 # set phase
4914 # set phase
4915 if not revs:
4915 if not revs:
4916 raise util.Abort(_('empty revision set'))
4916 raise util.Abort(_('empty revision set'))
4917 nodes = [repo[r].node() for r in revs]
4917 nodes = [repo[r].node() for r in revs]
4918 # moving revision from public to draft may hide them
4918 # moving revision from public to draft may hide them
4919 # We have to check result on an unfiltered repository
4919 # We have to check result on an unfiltered repository
4920 unfi = repo.unfiltered()
4920 unfi = repo.unfiltered()
4921 getphase = unfi._phasecache.phase
4921 getphase = unfi._phasecache.phase
4922 olddata = [getphase(unfi, r) for r in unfi]
4922 olddata = [getphase(unfi, r) for r in unfi]
4923 phases.advanceboundary(repo, tr, targetphase, nodes)
4923 phases.advanceboundary(repo, tr, targetphase, nodes)
4924 if opts['force']:
4924 if opts['force']:
4925 phases.retractboundary(repo, tr, targetphase, nodes)
4925 phases.retractboundary(repo, tr, targetphase, nodes)
4926 tr.close()
4926 tr.close()
4927 finally:
4927 finally:
4928 if tr is not None:
4928 if tr is not None:
4929 tr.release()
4929 tr.release()
4930 lock.release()
4930 lock.release()
4931 getphase = unfi._phasecache.phase
4931 getphase = unfi._phasecache.phase
4932 newdata = [getphase(unfi, r) for r in unfi]
4932 newdata = [getphase(unfi, r) for r in unfi]
4933 changes = sum(newdata[r] != olddata[r] for r in unfi)
4933 changes = sum(newdata[r] != olddata[r] for r in unfi)
4934 cl = unfi.changelog
4934 cl = unfi.changelog
4935 rejected = [n for n in nodes
4935 rejected = [n for n in nodes
4936 if newdata[cl.rev(n)] < targetphase]
4936 if newdata[cl.rev(n)] < targetphase]
4937 if rejected:
4937 if rejected:
4938 ui.warn(_('cannot move %i changesets to a higher '
4938 ui.warn(_('cannot move %i changesets to a higher '
4939 'phase, use --force\n') % len(rejected))
4939 'phase, use --force\n') % len(rejected))
4940 ret = 1
4940 ret = 1
4941 if changes:
4941 if changes:
4942 msg = _('phase changed for %i changesets\n') % changes
4942 msg = _('phase changed for %i changesets\n') % changes
4943 if ret:
4943 if ret:
4944 ui.status(msg)
4944 ui.status(msg)
4945 else:
4945 else:
4946 ui.note(msg)
4946 ui.note(msg)
4947 else:
4947 else:
4948 ui.warn(_('no phases changed\n'))
4948 ui.warn(_('no phases changed\n'))
4949 ret = 1
4949 ret = 1
4950 return ret
4950 return ret
4951
4951
4952 def postincoming(ui, repo, modheads, optupdate, checkout):
4952 def postincoming(ui, repo, modheads, optupdate, checkout):
4953 if modheads == 0:
4953 if modheads == 0:
4954 return
4954 return
4955 if optupdate:
4955 if optupdate:
4956 checkout, movemarkfrom = bookmarks.calculateupdate(ui, repo, checkout)
4956 checkout, movemarkfrom = bookmarks.calculateupdate(ui, repo, checkout)
4957 try:
4957 try:
4958 ret = hg.update(repo, checkout)
4958 ret = hg.update(repo, checkout)
4959 except util.Abort, inst:
4959 except util.Abort, inst:
4960 ui.warn(_("not updating: %s\n") % str(inst))
4960 ui.warn(_("not updating: %s\n") % str(inst))
4961 if inst.hint:
4961 if inst.hint:
4962 ui.warn(_("(%s)\n") % inst.hint)
4962 ui.warn(_("(%s)\n") % inst.hint)
4963 return 0
4963 return 0
4964 if not ret and not checkout:
4964 if not ret and not checkout:
4965 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4965 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4966 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4966 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4967 return ret
4967 return ret
4968 if modheads > 1:
4968 if modheads > 1:
4969 currentbranchheads = len(repo.branchheads())
4969 currentbranchheads = len(repo.branchheads())
4970 if currentbranchheads == modheads:
4970 if currentbranchheads == modheads:
4971 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4971 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4972 elif currentbranchheads > 1:
4972 elif currentbranchheads > 1:
4973 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4973 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4974 "merge)\n"))
4974 "merge)\n"))
4975 else:
4975 else:
4976 ui.status(_("(run 'hg heads' to see heads)\n"))
4976 ui.status(_("(run 'hg heads' to see heads)\n"))
4977 else:
4977 else:
4978 ui.status(_("(run 'hg update' to get a working copy)\n"))
4978 ui.status(_("(run 'hg update' to get a working copy)\n"))
4979
4979
4980 @command('^pull',
4980 @command('^pull',
4981 [('u', 'update', None,
4981 [('u', 'update', None,
4982 _('update to new branch head if changesets were pulled')),
4982 _('update to new branch head if changesets were pulled')),
4983 ('f', 'force', None, _('run even when remote repository is unrelated')),
4983 ('f', 'force', None, _('run even when remote repository is unrelated')),
4984 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4984 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4985 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4985 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4986 ('b', 'branch', [], _('a specific branch you would like to pull'),
4986 ('b', 'branch', [], _('a specific branch you would like to pull'),
4987 _('BRANCH')),
4987 _('BRANCH')),
4988 ] + remoteopts,
4988 ] + remoteopts,
4989 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4989 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4990 def pull(ui, repo, source="default", **opts):
4990 def pull(ui, repo, source="default", **opts):
4991 """pull changes from the specified source
4991 """pull changes from the specified source
4992
4992
4993 Pull changes from a remote repository to a local one.
4993 Pull changes from a remote repository to a local one.
4994
4994
4995 This finds all changes from the repository at the specified path
4995 This finds all changes from the repository at the specified path
4996 or URL and adds them to a local repository (the current one unless
4996 or URL and adds them to a local repository (the current one unless
4997 -R is specified). By default, this does not update the copy of the
4997 -R is specified). By default, this does not update the copy of the
4998 project in the working directory.
4998 project in the working directory.
4999
4999
5000 Use :hg:`incoming` if you want to see what would have been added
5000 Use :hg:`incoming` if you want to see what would have been added
5001 by a pull at the time you issued this command. If you then decide
5001 by a pull at the time you issued this command. If you then decide
5002 to add those changes to the repository, you should use :hg:`pull
5002 to add those changes to the repository, you should use :hg:`pull
5003 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5003 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5004
5004
5005 If SOURCE is omitted, the 'default' path will be used.
5005 If SOURCE is omitted, the 'default' path will be used.
5006 See :hg:`help urls` for more information.
5006 See :hg:`help urls` for more information.
5007
5007
5008 Returns 0 on success, 1 if an update had unresolved files.
5008 Returns 0 on success, 1 if an update had unresolved files.
5009 """
5009 """
5010 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5010 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5011 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5011 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5012 other = hg.peer(repo, opts, source)
5012 other = hg.peer(repo, opts, source)
5013 try:
5013 try:
5014 revs, checkout = hg.addbranchrevs(repo, other, branches,
5014 revs, checkout = hg.addbranchrevs(repo, other, branches,
5015 opts.get('rev'))
5015 opts.get('rev'))
5016
5016
5017 remotebookmarks = other.listkeys('bookmarks')
5017 remotebookmarks = other.listkeys('bookmarks')
5018
5018
5019 if opts.get('bookmark'):
5019 if opts.get('bookmark'):
5020 if not revs:
5020 if not revs:
5021 revs = []
5021 revs = []
5022 for b in opts['bookmark']:
5022 for b in opts['bookmark']:
5023 if b not in remotebookmarks:
5023 if b not in remotebookmarks:
5024 raise util.Abort(_('remote bookmark %s not found!') % b)
5024 raise util.Abort(_('remote bookmark %s not found!') % b)
5025 revs.append(remotebookmarks[b])
5025 revs.append(remotebookmarks[b])
5026
5026
5027 if revs:
5027 if revs:
5028 try:
5028 try:
5029 revs = [other.lookup(rev) for rev in revs]
5029 revs = [other.lookup(rev) for rev in revs]
5030 except error.CapabilityError:
5030 except error.CapabilityError:
5031 err = _("other repository doesn't support revision lookup, "
5031 err = _("other repository doesn't support revision lookup, "
5032 "so a rev cannot be specified.")
5032 "so a rev cannot be specified.")
5033 raise util.Abort(err)
5033 raise util.Abort(err)
5034
5034
5035 modheads = exchange.pull(repo, other, heads=revs,
5035 modheads = exchange.pull(repo, other, heads=revs,
5036 force=opts.get('force'),
5036 force=opts.get('force'),
5037 bookmarks=opts.get('bookmark', ())).cgresult
5037 bookmarks=opts.get('bookmark', ())).cgresult
5038 if checkout:
5038 if checkout:
5039 checkout = str(repo.changelog.rev(other.lookup(checkout)))
5039 checkout = str(repo.changelog.rev(other.lookup(checkout)))
5040 repo._subtoppath = source
5040 repo._subtoppath = source
5041 try:
5041 try:
5042 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
5042 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
5043
5043
5044 finally:
5044 finally:
5045 del repo._subtoppath
5045 del repo._subtoppath
5046
5046
5047 finally:
5047 finally:
5048 other.close()
5048 other.close()
5049 return ret
5049 return ret
5050
5050
5051 @command('^push',
5051 @command('^push',
5052 [('f', 'force', None, _('force push')),
5052 [('f', 'force', None, _('force push')),
5053 ('r', 'rev', [],
5053 ('r', 'rev', [],
5054 _('a changeset intended to be included in the destination'),
5054 _('a changeset intended to be included in the destination'),
5055 _('REV')),
5055 _('REV')),
5056 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5056 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5057 ('b', 'branch', [],
5057 ('b', 'branch', [],
5058 _('a specific branch you would like to push'), _('BRANCH')),
5058 _('a specific branch you would like to push'), _('BRANCH')),
5059 ('', 'new-branch', False, _('allow pushing a new branch')),
5059 ('', 'new-branch', False, _('allow pushing a new branch')),
5060 ] + remoteopts,
5060 ] + remoteopts,
5061 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5061 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5062 def push(ui, repo, dest=None, **opts):
5062 def push(ui, repo, dest=None, **opts):
5063 """push changes to the specified destination
5063 """push changes to the specified destination
5064
5064
5065 Push changesets from the local repository to the specified
5065 Push changesets from the local repository to the specified
5066 destination.
5066 destination.
5067
5067
5068 This operation is symmetrical to pull: it is identical to a pull
5068 This operation is symmetrical to pull: it is identical to a pull
5069 in the destination repository from the current one.
5069 in the destination repository from the current one.
5070
5070
5071 By default, push will not allow creation of new heads at the
5071 By default, push will not allow creation of new heads at the
5072 destination, since multiple heads would make it unclear which head
5072 destination, since multiple heads would make it unclear which head
5073 to use. In this situation, it is recommended to pull and merge
5073 to use. In this situation, it is recommended to pull and merge
5074 before pushing.
5074 before pushing.
5075
5075
5076 Use --new-branch if you want to allow push to create a new named
5076 Use --new-branch if you want to allow push to create a new named
5077 branch that is not present at the destination. This allows you to
5077 branch that is not present at the destination. This allows you to
5078 only create a new branch without forcing other changes.
5078 only create a new branch without forcing other changes.
5079
5079
5080 .. note::
5080 .. note::
5081
5081
5082 Extra care should be taken with the -f/--force option,
5082 Extra care should be taken with the -f/--force option,
5083 which will push all new heads on all branches, an action which will
5083 which will push all new heads on all branches, an action which will
5084 almost always cause confusion for collaborators.
5084 almost always cause confusion for collaborators.
5085
5085
5086 If -r/--rev is used, the specified revision and all its ancestors
5086 If -r/--rev is used, the specified revision and all its ancestors
5087 will be pushed to the remote repository.
5087 will be pushed to the remote repository.
5088
5088
5089 If -B/--bookmark is used, the specified bookmarked revision, its
5089 If -B/--bookmark is used, the specified bookmarked revision, its
5090 ancestors, and the bookmark will be pushed to the remote
5090 ancestors, and the bookmark will be pushed to the remote
5091 repository.
5091 repository.
5092
5092
5093 Please see :hg:`help urls` for important details about ``ssh://``
5093 Please see :hg:`help urls` for important details about ``ssh://``
5094 URLs. If DESTINATION is omitted, a default path will be used.
5094 URLs. If DESTINATION is omitted, a default path will be used.
5095
5095
5096 Returns 0 if push was successful, 1 if nothing to push.
5096 Returns 0 if push was successful, 1 if nothing to push.
5097 """
5097 """
5098
5098
5099 if opts.get('bookmark'):
5099 if opts.get('bookmark'):
5100 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5100 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5101 for b in opts['bookmark']:
5101 for b in opts['bookmark']:
5102 # translate -B options to -r so changesets get pushed
5102 # translate -B options to -r so changesets get pushed
5103 if b in repo._bookmarks:
5103 if b in repo._bookmarks:
5104 opts.setdefault('rev', []).append(b)
5104 opts.setdefault('rev', []).append(b)
5105 else:
5105 else:
5106 # if we try to push a deleted bookmark, translate it to null
5106 # if we try to push a deleted bookmark, translate it to null
5107 # this lets simultaneous -r, -b options continue working
5107 # this lets simultaneous -r, -b options continue working
5108 opts.setdefault('rev', []).append("null")
5108 opts.setdefault('rev', []).append("null")
5109
5109
5110 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5110 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5111 dest, branches = hg.parseurl(dest, opts.get('branch'))
5111 dest, branches = hg.parseurl(dest, opts.get('branch'))
5112 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5112 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5113 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5113 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5114 try:
5114 try:
5115 other = hg.peer(repo, opts, dest)
5115 other = hg.peer(repo, opts, dest)
5116 except error.RepoError:
5116 except error.RepoError:
5117 if dest == "default-push":
5117 if dest == "default-push":
5118 raise util.Abort(_("default repository not configured!"),
5118 raise util.Abort(_("default repository not configured!"),
5119 hint=_('see the "path" section in "hg help config"'))
5119 hint=_('see the "path" section in "hg help config"'))
5120 else:
5120 else:
5121 raise
5121 raise
5122
5122
5123 if revs:
5123 if revs:
5124 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5124 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5125
5125
5126 repo._subtoppath = dest
5126 repo._subtoppath = dest
5127 try:
5127 try:
5128 # push subrepos depth-first for coherent ordering
5128 # push subrepos depth-first for coherent ordering
5129 c = repo['']
5129 c = repo['']
5130 subs = c.substate # only repos that are committed
5130 subs = c.substate # only repos that are committed
5131 for s in sorted(subs):
5131 for s in sorted(subs):
5132 result = c.sub(s).push(opts)
5132 result = c.sub(s).push(opts)
5133 if result == 0:
5133 if result == 0:
5134 return not result
5134 return not result
5135 finally:
5135 finally:
5136 del repo._subtoppath
5136 del repo._subtoppath
5137 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5137 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5138 newbranch=opts.get('new_branch'),
5138 newbranch=opts.get('new_branch'),
5139 bookmarks=opts.get('bookmark', ()))
5139 bookmarks=opts.get('bookmark', ()))
5140
5140
5141 result = not pushop.cgresult
5141 result = not pushop.cgresult
5142
5142
5143 if pushop.bkresult is not None:
5143 if pushop.bkresult is not None:
5144 if pushop.bkresult == 2:
5144 if pushop.bkresult == 2:
5145 result = 2
5145 result = 2
5146 elif not result and pushop.bkresult:
5146 elif not result and pushop.bkresult:
5147 result = 2
5147 result = 2
5148
5148
5149 return result
5149 return result
5150
5150
5151 @command('recover', [])
5151 @command('recover', [])
5152 def recover(ui, repo):
5152 def recover(ui, repo):
5153 """roll back an interrupted transaction
5153 """roll back an interrupted transaction
5154
5154
5155 Recover from an interrupted commit or pull.
5155 Recover from an interrupted commit or pull.
5156
5156
5157 This command tries to fix the repository status after an
5157 This command tries to fix the repository status after an
5158 interrupted operation. It should only be necessary when Mercurial
5158 interrupted operation. It should only be necessary when Mercurial
5159 suggests it.
5159 suggests it.
5160
5160
5161 Returns 0 if successful, 1 if nothing to recover or verify fails.
5161 Returns 0 if successful, 1 if nothing to recover or verify fails.
5162 """
5162 """
5163 if repo.recover():
5163 if repo.recover():
5164 return hg.verify(repo)
5164 return hg.verify(repo)
5165 return 1
5165 return 1
5166
5166
5167 @command('^remove|rm',
5167 @command('^remove|rm',
5168 [('A', 'after', None, _('record delete for missing files')),
5168 [('A', 'after', None, _('record delete for missing files')),
5169 ('f', 'force', None,
5169 ('f', 'force', None,
5170 _('remove (and delete) file even if added or modified')),
5170 _('remove (and delete) file even if added or modified')),
5171 ] + subrepoopts + walkopts,
5171 ] + subrepoopts + walkopts,
5172 _('[OPTION]... FILE...'),
5172 _('[OPTION]... FILE...'),
5173 inferrepo=True)
5173 inferrepo=True)
5174 def remove(ui, repo, *pats, **opts):
5174 def remove(ui, repo, *pats, **opts):
5175 """remove the specified files on the next commit
5175 """remove the specified files on the next commit
5176
5176
5177 Schedule the indicated files for removal from the current branch.
5177 Schedule the indicated files for removal from the current branch.
5178
5178
5179 This command schedules the files to be removed at the next commit.
5179 This command schedules the files to be removed at the next commit.
5180 To undo a remove before that, see :hg:`revert`. To undo added
5180 To undo a remove before that, see :hg:`revert`. To undo added
5181 files, see :hg:`forget`.
5181 files, see :hg:`forget`.
5182
5182
5183 .. container:: verbose
5183 .. container:: verbose
5184
5184
5185 -A/--after can be used to remove only files that have already
5185 -A/--after can be used to remove only files that have already
5186 been deleted, -f/--force can be used to force deletion, and -Af
5186 been deleted, -f/--force can be used to force deletion, and -Af
5187 can be used to remove files from the next revision without
5187 can be used to remove files from the next revision without
5188 deleting them from the working directory.
5188 deleting them from the working directory.
5189
5189
5190 The following table details the behavior of remove for different
5190 The following table details the behavior of remove for different
5191 file states (columns) and option combinations (rows). The file
5191 file states (columns) and option combinations (rows). The file
5192 states are Added [A], Clean [C], Modified [M] and Missing [!]
5192 states are Added [A], Clean [C], Modified [M] and Missing [!]
5193 (as reported by :hg:`status`). The actions are Warn, Remove
5193 (as reported by :hg:`status`). The actions are Warn, Remove
5194 (from branch) and Delete (from disk):
5194 (from branch) and Delete (from disk):
5195
5195
5196 ========= == == == ==
5196 ========= == == == ==
5197 opt/state A C M !
5197 opt/state A C M !
5198 ========= == == == ==
5198 ========= == == == ==
5199 none W RD W R
5199 none W RD W R
5200 -f R RD RD R
5200 -f R RD RD R
5201 -A W W W R
5201 -A W W W R
5202 -Af R R R R
5202 -Af R R R R
5203 ========= == == == ==
5203 ========= == == == ==
5204
5204
5205 Note that remove never deletes files in Added [A] state from the
5205 Note that remove never deletes files in Added [A] state from the
5206 working directory, not even if option --force is specified.
5206 working directory, not even if option --force is specified.
5207
5207
5208 Returns 0 on success, 1 if any warnings encountered.
5208 Returns 0 on success, 1 if any warnings encountered.
5209 """
5209 """
5210
5210
5211 after, force = opts.get('after'), opts.get('force')
5211 after, force = opts.get('after'), opts.get('force')
5212 if not pats and not after:
5212 if not pats and not after:
5213 raise util.Abort(_('no files specified'))
5213 raise util.Abort(_('no files specified'))
5214
5214
5215 m = scmutil.match(repo[None], pats, opts)
5215 m = scmutil.match(repo[None], pats, opts)
5216 subrepos = opts.get('subrepos')
5216 subrepos = opts.get('subrepos')
5217 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5217 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5218
5218
5219 @command('rename|move|mv',
5219 @command('rename|move|mv',
5220 [('A', 'after', None, _('record a rename that has already occurred')),
5220 [('A', 'after', None, _('record a rename that has already occurred')),
5221 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5221 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5222 ] + walkopts + dryrunopts,
5222 ] + walkopts + dryrunopts,
5223 _('[OPTION]... SOURCE... DEST'))
5223 _('[OPTION]... SOURCE... DEST'))
5224 def rename(ui, repo, *pats, **opts):
5224 def rename(ui, repo, *pats, **opts):
5225 """rename files; equivalent of copy + remove
5225 """rename files; equivalent of copy + remove
5226
5226
5227 Mark dest as copies of sources; mark sources for deletion. If dest
5227 Mark dest as copies of sources; mark sources for deletion. If dest
5228 is a directory, copies are put in that directory. If dest is a
5228 is a directory, copies are put in that directory. If dest is a
5229 file, there can only be one source.
5229 file, there can only be one source.
5230
5230
5231 By default, this command copies the contents of files as they
5231 By default, this command copies the contents of files as they
5232 exist in the working directory. If invoked with -A/--after, the
5232 exist in the working directory. If invoked with -A/--after, the
5233 operation is recorded, but no copying is performed.
5233 operation is recorded, but no copying is performed.
5234
5234
5235 This command takes effect at the next commit. To undo a rename
5235 This command takes effect at the next commit. To undo a rename
5236 before that, see :hg:`revert`.
5236 before that, see :hg:`revert`.
5237
5237
5238 Returns 0 on success, 1 if errors are encountered.
5238 Returns 0 on success, 1 if errors are encountered.
5239 """
5239 """
5240 wlock = repo.wlock(False)
5240 wlock = repo.wlock(False)
5241 try:
5241 try:
5242 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5242 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5243 finally:
5243 finally:
5244 wlock.release()
5244 wlock.release()
5245
5245
5246 @command('resolve',
5246 @command('resolve',
5247 [('a', 'all', None, _('select all unresolved files')),
5247 [('a', 'all', None, _('select all unresolved files')),
5248 ('l', 'list', None, _('list state of files needing merge')),
5248 ('l', 'list', None, _('list state of files needing merge')),
5249 ('m', 'mark', None, _('mark files as resolved')),
5249 ('m', 'mark', None, _('mark files as resolved')),
5250 ('u', 'unmark', None, _('mark files as unresolved')),
5250 ('u', 'unmark', None, _('mark files as unresolved')),
5251 ('n', 'no-status', None, _('hide status prefix'))]
5251 ('n', 'no-status', None, _('hide status prefix'))]
5252 + mergetoolopts + walkopts + formatteropts,
5252 + mergetoolopts + walkopts + formatteropts,
5253 _('[OPTION]... [FILE]...'),
5253 _('[OPTION]... [FILE]...'),
5254 inferrepo=True)
5254 inferrepo=True)
5255 def resolve(ui, repo, *pats, **opts):
5255 def resolve(ui, repo, *pats, **opts):
5256 """redo merges or set/view the merge status of files
5256 """redo merges or set/view the merge status of files
5257
5257
5258 Merges with unresolved conflicts are often the result of
5258 Merges with unresolved conflicts are often the result of
5259 non-interactive merging using the ``internal:merge`` configuration
5259 non-interactive merging using the ``internal:merge`` configuration
5260 setting, or a command-line merge tool like ``diff3``. The resolve
5260 setting, or a command-line merge tool like ``diff3``. The resolve
5261 command is used to manage the files involved in a merge, after
5261 command is used to manage the files involved in a merge, after
5262 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5262 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5263 working directory must have two parents). See :hg:`help
5263 working directory must have two parents). See :hg:`help
5264 merge-tools` for information on configuring merge tools.
5264 merge-tools` for information on configuring merge tools.
5265
5265
5266 The resolve command can be used in the following ways:
5266 The resolve command can be used in the following ways:
5267
5267
5268 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5268 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5269 files, discarding any previous merge attempts. Re-merging is not
5269 files, discarding any previous merge attempts. Re-merging is not
5270 performed for files already marked as resolved. Use ``--all/-a``
5270 performed for files already marked as resolved. Use ``--all/-a``
5271 to select all unresolved files. ``--tool`` can be used to specify
5271 to select all unresolved files. ``--tool`` can be used to specify
5272 the merge tool used for the given files. It overrides the HGMERGE
5272 the merge tool used for the given files. It overrides the HGMERGE
5273 environment variable and your configuration files. Previous file
5273 environment variable and your configuration files. Previous file
5274 contents are saved with a ``.orig`` suffix.
5274 contents are saved with a ``.orig`` suffix.
5275
5275
5276 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5276 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5277 (e.g. after having manually fixed-up the files). The default is
5277 (e.g. after having manually fixed-up the files). The default is
5278 to mark all unresolved files.
5278 to mark all unresolved files.
5279
5279
5280 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5280 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5281 default is to mark all resolved files.
5281 default is to mark all resolved files.
5282
5282
5283 - :hg:`resolve -l`: list files which had or still have conflicts.
5283 - :hg:`resolve -l`: list files which had or still have conflicts.
5284 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5284 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5285
5285
5286 Note that Mercurial will not let you commit files with unresolved
5286 Note that Mercurial will not let you commit files with unresolved
5287 merge conflicts. You must use :hg:`resolve -m ...` before you can
5287 merge conflicts. You must use :hg:`resolve -m ...` before you can
5288 commit after a conflicting merge.
5288 commit after a conflicting merge.
5289
5289
5290 Returns 0 on success, 1 if any files fail a resolve attempt.
5290 Returns 0 on success, 1 if any files fail a resolve attempt.
5291 """
5291 """
5292
5292
5293 all, mark, unmark, show, nostatus = \
5293 all, mark, unmark, show, nostatus = \
5294 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
5294 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
5295
5295
5296 if (show and (mark or unmark)) or (mark and unmark):
5296 if (show and (mark or unmark)) or (mark and unmark):
5297 raise util.Abort(_("too many options specified"))
5297 raise util.Abort(_("too many options specified"))
5298 if pats and all:
5298 if pats and all:
5299 raise util.Abort(_("can't specify --all and patterns"))
5299 raise util.Abort(_("can't specify --all and patterns"))
5300 if not (all or pats or show or mark or unmark):
5300 if not (all or pats or show or mark or unmark):
5301 raise util.Abort(_('no files or directories specified'),
5301 raise util.Abort(_('no files or directories specified'),
5302 hint=('use --all to remerge all files'))
5302 hint=('use --all to remerge all files'))
5303
5303
5304 if show:
5304 if show:
5305 fm = ui.formatter('resolve', opts)
5305 fm = ui.formatter('resolve', opts)
5306 ms = mergemod.mergestate(repo)
5306 ms = mergemod.mergestate(repo)
5307 m = scmutil.match(repo[None], pats, opts)
5307 m = scmutil.match(repo[None], pats, opts)
5308 for f in ms:
5308 for f in ms:
5309 if not m(f):
5309 if not m(f):
5310 continue
5310 continue
5311 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved'}[ms[f]]
5311 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved'}[ms[f]]
5312 fm.startitem()
5312 fm.startitem()
5313 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
5313 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
5314 fm.write('path', '%s\n', f, label=l)
5314 fm.write('path', '%s\n', f, label=l)
5315 fm.end()
5315 fm.end()
5316 return 0
5316 return 0
5317
5317
5318 wlock = repo.wlock()
5318 wlock = repo.wlock()
5319 try:
5319 try:
5320 ms = mergemod.mergestate(repo)
5320 ms = mergemod.mergestate(repo)
5321
5321
5322 if not (ms.active() or repo.dirstate.p2() != nullid):
5322 if not (ms.active() or repo.dirstate.p2() != nullid):
5323 raise util.Abort(
5323 raise util.Abort(
5324 _('resolve command not applicable when not merging'))
5324 _('resolve command not applicable when not merging'))
5325
5325
5326 m = scmutil.match(repo[None], pats, opts)
5326 m = scmutil.match(repo[None], pats, opts)
5327 ret = 0
5327 ret = 0
5328 didwork = False
5328 didwork = False
5329
5329
5330 for f in ms:
5330 for f in ms:
5331 if not m(f):
5331 if not m(f):
5332 continue
5332 continue
5333
5333
5334 didwork = True
5334 didwork = True
5335
5335
5336 if mark:
5336 if mark:
5337 ms.mark(f, "r")
5337 ms.mark(f, "r")
5338 elif unmark:
5338 elif unmark:
5339 ms.mark(f, "u")
5339 ms.mark(f, "u")
5340 else:
5340 else:
5341 wctx = repo[None]
5341 wctx = repo[None]
5342
5342
5343 # backup pre-resolve (merge uses .orig for its own purposes)
5343 # backup pre-resolve (merge uses .orig for its own purposes)
5344 a = repo.wjoin(f)
5344 a = repo.wjoin(f)
5345 util.copyfile(a, a + ".resolve")
5345 util.copyfile(a, a + ".resolve")
5346
5346
5347 try:
5347 try:
5348 # resolve file
5348 # resolve file
5349 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5349 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5350 'resolve')
5350 'resolve')
5351 if ms.resolve(f, wctx):
5351 if ms.resolve(f, wctx):
5352 ret = 1
5352 ret = 1
5353 finally:
5353 finally:
5354 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5354 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5355 ms.commit()
5355 ms.commit()
5356
5356
5357 # replace filemerge's .orig file with our resolve file
5357 # replace filemerge's .orig file with our resolve file
5358 util.rename(a + ".resolve", a + ".orig")
5358 util.rename(a + ".resolve", a + ".orig")
5359
5359
5360 ms.commit()
5360 ms.commit()
5361
5361
5362 if not didwork and pats:
5362 if not didwork and pats:
5363 ui.warn(_("arguments do not match paths that need resolving\n"))
5363 ui.warn(_("arguments do not match paths that need resolving\n"))
5364
5364
5365 finally:
5365 finally:
5366 wlock.release()
5366 wlock.release()
5367
5367
5368 # Nudge users into finishing an unfinished operation
5368 # Nudge users into finishing an unfinished operation
5369 if not list(ms.unresolved()):
5369 if not list(ms.unresolved()):
5370 ui.status(_('(no more unresolved files)\n'))
5370 ui.status(_('(no more unresolved files)\n'))
5371
5371
5372 return ret
5372 return ret
5373
5373
5374 @command('revert',
5374 @command('revert',
5375 [('a', 'all', None, _('revert all changes when no arguments given')),
5375 [('a', 'all', None, _('revert all changes when no arguments given')),
5376 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5376 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5377 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5377 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5378 ('C', 'no-backup', None, _('do not save backup copies of files')),
5378 ('C', 'no-backup', None, _('do not save backup copies of files')),
5379 ('i', 'interactive', None, _('interactively select the changes')),
5379 ('i', 'interactive', None, _('interactively select the changes')),
5380 ] + walkopts + dryrunopts,
5380 ] + walkopts + dryrunopts,
5381 _('[OPTION]... [-r REV] [NAME]...'))
5381 _('[OPTION]... [-r REV] [NAME]...'))
5382 def revert(ui, repo, *pats, **opts):
5382 def revert(ui, repo, *pats, **opts):
5383 """restore files to their checkout state
5383 """restore files to their checkout state
5384
5384
5385 .. note::
5385 .. note::
5386
5386
5387 To check out earlier revisions, you should use :hg:`update REV`.
5387 To check out earlier revisions, you should use :hg:`update REV`.
5388 To cancel an uncommitted merge (and lose your changes),
5388 To cancel an uncommitted merge (and lose your changes),
5389 use :hg:`update --clean .`.
5389 use :hg:`update --clean .`.
5390
5390
5391 With no revision specified, revert the specified files or directories
5391 With no revision specified, revert the specified files or directories
5392 to the contents they had in the parent of the working directory.
5392 to the contents they had in the parent of the working directory.
5393 This restores the contents of files to an unmodified
5393 This restores the contents of files to an unmodified
5394 state and unschedules adds, removes, copies, and renames. If the
5394 state and unschedules adds, removes, copies, and renames. If the
5395 working directory has two parents, you must explicitly specify a
5395 working directory has two parents, you must explicitly specify a
5396 revision.
5396 revision.
5397
5397
5398 Using the -r/--rev or -d/--date options, revert the given files or
5398 Using the -r/--rev or -d/--date options, revert the given files or
5399 directories to their states as of a specific revision. Because
5399 directories to their states as of a specific revision. Because
5400 revert does not change the working directory parents, this will
5400 revert does not change the working directory parents, this will
5401 cause these files to appear modified. This can be helpful to "back
5401 cause these files to appear modified. This can be helpful to "back
5402 out" some or all of an earlier change. See :hg:`backout` for a
5402 out" some or all of an earlier change. See :hg:`backout` for a
5403 related method.
5403 related method.
5404
5404
5405 Modified files are saved with a .orig suffix before reverting.
5405 Modified files are saved with a .orig suffix before reverting.
5406 To disable these backups, use --no-backup.
5406 To disable these backups, use --no-backup.
5407
5407
5408 See :hg:`help dates` for a list of formats valid for -d/--date.
5408 See :hg:`help dates` for a list of formats valid for -d/--date.
5409
5409
5410 Returns 0 on success.
5410 Returns 0 on success.
5411 """
5411 """
5412
5412
5413 if opts.get("date"):
5413 if opts.get("date"):
5414 if opts.get("rev"):
5414 if opts.get("rev"):
5415 raise util.Abort(_("you can't specify a revision and a date"))
5415 raise util.Abort(_("you can't specify a revision and a date"))
5416 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5416 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5417
5417
5418 parent, p2 = repo.dirstate.parents()
5418 parent, p2 = repo.dirstate.parents()
5419 if not opts.get('rev') and p2 != nullid:
5419 if not opts.get('rev') and p2 != nullid:
5420 # revert after merge is a trap for new users (issue2915)
5420 # revert after merge is a trap for new users (issue2915)
5421 raise util.Abort(_('uncommitted merge with no revision specified'),
5421 raise util.Abort(_('uncommitted merge with no revision specified'),
5422 hint=_('use "hg update" or see "hg help revert"'))
5422 hint=_('use "hg update" or see "hg help revert"'))
5423
5423
5424 ctx = scmutil.revsingle(repo, opts.get('rev'))
5424 ctx = scmutil.revsingle(repo, opts.get('rev'))
5425
5425
5426 if not pats and not opts.get('all'):
5426 if not pats and not opts.get('all'):
5427 msg = _("no files or directories specified")
5427 msg = _("no files or directories specified")
5428 if p2 != nullid:
5428 if p2 != nullid:
5429 hint = _("uncommitted merge, use --all to discard all changes,"
5429 hint = _("uncommitted merge, use --all to discard all changes,"
5430 " or 'hg update -C .' to abort the merge")
5430 " or 'hg update -C .' to abort the merge")
5431 raise util.Abort(msg, hint=hint)
5431 raise util.Abort(msg, hint=hint)
5432 dirty = util.any(repo.status())
5432 dirty = util.any(repo.status())
5433 node = ctx.node()
5433 node = ctx.node()
5434 if node != parent:
5434 if node != parent:
5435 if dirty:
5435 if dirty:
5436 hint = _("uncommitted changes, use --all to discard all"
5436 hint = _("uncommitted changes, use --all to discard all"
5437 " changes, or 'hg update %s' to update") % ctx.rev()
5437 " changes, or 'hg update %s' to update") % ctx.rev()
5438 else:
5438 else:
5439 hint = _("use --all to revert all files,"
5439 hint = _("use --all to revert all files,"
5440 " or 'hg update %s' to update") % ctx.rev()
5440 " or 'hg update %s' to update") % ctx.rev()
5441 elif dirty:
5441 elif dirty:
5442 hint = _("uncommitted changes, use --all to discard all changes")
5442 hint = _("uncommitted changes, use --all to discard all changes")
5443 else:
5443 else:
5444 hint = _("use --all to revert all files")
5444 hint = _("use --all to revert all files")
5445 raise util.Abort(msg, hint=hint)
5445 raise util.Abort(msg, hint=hint)
5446
5446
5447 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5447 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5448
5448
5449 @command('rollback', dryrunopts +
5449 @command('rollback', dryrunopts +
5450 [('f', 'force', False, _('ignore safety measures'))])
5450 [('f', 'force', False, _('ignore safety measures'))])
5451 def rollback(ui, repo, **opts):
5451 def rollback(ui, repo, **opts):
5452 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5452 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5453
5453
5454 Please use :hg:`commit --amend` instead of rollback to correct
5454 Please use :hg:`commit --amend` instead of rollback to correct
5455 mistakes in the last commit.
5455 mistakes in the last commit.
5456
5456
5457 This command should be used with care. There is only one level of
5457 This command should be used with care. There is only one level of
5458 rollback, and there is no way to undo a rollback. It will also
5458 rollback, and there is no way to undo a rollback. It will also
5459 restore the dirstate at the time of the last transaction, losing
5459 restore the dirstate at the time of the last transaction, losing
5460 any dirstate changes since that time. This command does not alter
5460 any dirstate changes since that time. This command does not alter
5461 the working directory.
5461 the working directory.
5462
5462
5463 Transactions are used to encapsulate the effects of all commands
5463 Transactions are used to encapsulate the effects of all commands
5464 that create new changesets or propagate existing changesets into a
5464 that create new changesets or propagate existing changesets into a
5465 repository.
5465 repository.
5466
5466
5467 .. container:: verbose
5467 .. container:: verbose
5468
5468
5469 For example, the following commands are transactional, and their
5469 For example, the following commands are transactional, and their
5470 effects can be rolled back:
5470 effects can be rolled back:
5471
5471
5472 - commit
5472 - commit
5473 - import
5473 - import
5474 - pull
5474 - pull
5475 - push (with this repository as the destination)
5475 - push (with this repository as the destination)
5476 - unbundle
5476 - unbundle
5477
5477
5478 To avoid permanent data loss, rollback will refuse to rollback a
5478 To avoid permanent data loss, rollback will refuse to rollback a
5479 commit transaction if it isn't checked out. Use --force to
5479 commit transaction if it isn't checked out. Use --force to
5480 override this protection.
5480 override this protection.
5481
5481
5482 This command is not intended for use on public repositories. Once
5482 This command is not intended for use on public repositories. Once
5483 changes are visible for pull by other users, rolling a transaction
5483 changes are visible for pull by other users, rolling a transaction
5484 back locally is ineffective (someone else may already have pulled
5484 back locally is ineffective (someone else may already have pulled
5485 the changes). Furthermore, a race is possible with readers of the
5485 the changes). Furthermore, a race is possible with readers of the
5486 repository; for example an in-progress pull from the repository
5486 repository; for example an in-progress pull from the repository
5487 may fail if a rollback is performed.
5487 may fail if a rollback is performed.
5488
5488
5489 Returns 0 on success, 1 if no rollback data is available.
5489 Returns 0 on success, 1 if no rollback data is available.
5490 """
5490 """
5491 return repo.rollback(dryrun=opts.get('dry_run'),
5491 return repo.rollback(dryrun=opts.get('dry_run'),
5492 force=opts.get('force'))
5492 force=opts.get('force'))
5493
5493
5494 @command('root', [])
5494 @command('root', [])
5495 def root(ui, repo):
5495 def root(ui, repo):
5496 """print the root (top) of the current working directory
5496 """print the root (top) of the current working directory
5497
5497
5498 Print the root directory of the current repository.
5498 Print the root directory of the current repository.
5499
5499
5500 Returns 0 on success.
5500 Returns 0 on success.
5501 """
5501 """
5502 ui.write(repo.root + "\n")
5502 ui.write(repo.root + "\n")
5503
5503
5504 @command('^serve',
5504 @command('^serve',
5505 [('A', 'accesslog', '', _('name of access log file to write to'),
5505 [('A', 'accesslog', '', _('name of access log file to write to'),
5506 _('FILE')),
5506 _('FILE')),
5507 ('d', 'daemon', None, _('run server in background')),
5507 ('d', 'daemon', None, _('run server in background')),
5508 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('FILE')),
5508 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('FILE')),
5509 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5509 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5510 # use string type, then we can check if something was passed
5510 # use string type, then we can check if something was passed
5511 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5511 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5512 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5512 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5513 _('ADDR')),
5513 _('ADDR')),
5514 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5514 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5515 _('PREFIX')),
5515 _('PREFIX')),
5516 ('n', 'name', '',
5516 ('n', 'name', '',
5517 _('name to show in web pages (default: working directory)'), _('NAME')),
5517 _('name to show in web pages (default: working directory)'), _('NAME')),
5518 ('', 'web-conf', '',
5518 ('', 'web-conf', '',
5519 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5519 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5520 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5520 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5521 _('FILE')),
5521 _('FILE')),
5522 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5522 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5523 ('', 'stdio', None, _('for remote clients')),
5523 ('', 'stdio', None, _('for remote clients')),
5524 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5524 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5525 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5525 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5526 ('', 'style', '', _('template style to use'), _('STYLE')),
5526 ('', 'style', '', _('template style to use'), _('STYLE')),
5527 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5527 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5528 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5528 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5529 _('[OPTION]...'),
5529 _('[OPTION]...'),
5530 optionalrepo=True)
5530 optionalrepo=True)
5531 def serve(ui, repo, **opts):
5531 def serve(ui, repo, **opts):
5532 """start stand-alone webserver
5532 """start stand-alone webserver
5533
5533
5534 Start a local HTTP repository browser and pull server. You can use
5534 Start a local HTTP repository browser and pull server. You can use
5535 this for ad-hoc sharing and browsing of repositories. It is
5535 this for ad-hoc sharing and browsing of repositories. It is
5536 recommended to use a real web server to serve a repository for
5536 recommended to use a real web server to serve a repository for
5537 longer periods of time.
5537 longer periods of time.
5538
5538
5539 Please note that the server does not implement access control.
5539 Please note that the server does not implement access control.
5540 This means that, by default, anybody can read from the server and
5540 This means that, by default, anybody can read from the server and
5541 nobody can write to it by default. Set the ``web.allow_push``
5541 nobody can write to it by default. Set the ``web.allow_push``
5542 option to ``*`` to allow everybody to push to the server. You
5542 option to ``*`` to allow everybody to push to the server. You
5543 should use a real web server if you need to authenticate users.
5543 should use a real web server if you need to authenticate users.
5544
5544
5545 By default, the server logs accesses to stdout and errors to
5545 By default, the server logs accesses to stdout and errors to
5546 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5546 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5547 files.
5547 files.
5548
5548
5549 To have the server choose a free port number to listen on, specify
5549 To have the server choose a free port number to listen on, specify
5550 a port number of 0; in this case, the server will print the port
5550 a port number of 0; in this case, the server will print the port
5551 number it uses.
5551 number it uses.
5552
5552
5553 Returns 0 on success.
5553 Returns 0 on success.
5554 """
5554 """
5555
5555
5556 if opts["stdio"] and opts["cmdserver"]:
5556 if opts["stdio"] and opts["cmdserver"]:
5557 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5557 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5558
5558
5559 if opts["stdio"]:
5559 if opts["stdio"]:
5560 if repo is None:
5560 if repo is None:
5561 raise error.RepoError(_("there is no Mercurial repository here"
5561 raise error.RepoError(_("there is no Mercurial repository here"
5562 " (.hg not found)"))
5562 " (.hg not found)"))
5563 s = sshserver.sshserver(ui, repo)
5563 s = sshserver.sshserver(ui, repo)
5564 s.serve_forever()
5564 s.serve_forever()
5565
5565
5566 if opts["cmdserver"]:
5566 if opts["cmdserver"]:
5567 service = commandserver.createservice(ui, repo, opts)
5567 service = commandserver.createservice(ui, repo, opts)
5568 return cmdutil.service(opts, initfn=service.init, runfn=service.run)
5568 return cmdutil.service(opts, initfn=service.init, runfn=service.run)
5569
5569
5570 # this way we can check if something was given in the command-line
5570 # this way we can check if something was given in the command-line
5571 if opts.get('port'):
5571 if opts.get('port'):
5572 opts['port'] = util.getport(opts.get('port'))
5572 opts['port'] = util.getport(opts.get('port'))
5573
5573
5574 if repo:
5574 if repo:
5575 baseui = repo.baseui
5575 baseui = repo.baseui
5576 else:
5576 else:
5577 baseui = ui
5577 baseui = ui
5578 optlist = ("name templates style address port prefix ipv6"
5578 optlist = ("name templates style address port prefix ipv6"
5579 " accesslog errorlog certificate encoding")
5579 " accesslog errorlog certificate encoding")
5580 for o in optlist.split():
5580 for o in optlist.split():
5581 val = opts.get(o, '')
5581 val = opts.get(o, '')
5582 if val in (None, ''): # should check against default options instead
5582 if val in (None, ''): # should check against default options instead
5583 continue
5583 continue
5584 baseui.setconfig("web", o, val, 'serve')
5584 baseui.setconfig("web", o, val, 'serve')
5585 if repo and repo.ui != baseui:
5585 if repo and repo.ui != baseui:
5586 repo.ui.setconfig("web", o, val, 'serve')
5586 repo.ui.setconfig("web", o, val, 'serve')
5587
5587
5588 o = opts.get('web_conf') or opts.get('webdir_conf')
5588 o = opts.get('web_conf') or opts.get('webdir_conf')
5589 if not o:
5589 if not o:
5590 if not repo:
5590 if not repo:
5591 raise error.RepoError(_("there is no Mercurial repository"
5591 raise error.RepoError(_("there is no Mercurial repository"
5592 " here (.hg not found)"))
5592 " here (.hg not found)"))
5593 o = repo
5593 o = repo
5594
5594
5595 app = hgweb.hgweb(o, baseui=baseui)
5595 app = hgweb.hgweb(o, baseui=baseui)
5596 service = httpservice(ui, app, opts)
5596 service = httpservice(ui, app, opts)
5597 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5597 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5598
5598
5599 class httpservice(object):
5599 class httpservice(object):
5600 def __init__(self, ui, app, opts):
5600 def __init__(self, ui, app, opts):
5601 self.ui = ui
5601 self.ui = ui
5602 self.app = app
5602 self.app = app
5603 self.opts = opts
5603 self.opts = opts
5604
5604
5605 def init(self):
5605 def init(self):
5606 util.setsignalhandler()
5606 util.setsignalhandler()
5607 self.httpd = hgweb_server.create_server(self.ui, self.app)
5607 self.httpd = hgweb_server.create_server(self.ui, self.app)
5608
5608
5609 if self.opts['port'] and not self.ui.verbose:
5609 if self.opts['port'] and not self.ui.verbose:
5610 return
5610 return
5611
5611
5612 if self.httpd.prefix:
5612 if self.httpd.prefix:
5613 prefix = self.httpd.prefix.strip('/') + '/'
5613 prefix = self.httpd.prefix.strip('/') + '/'
5614 else:
5614 else:
5615 prefix = ''
5615 prefix = ''
5616
5616
5617 port = ':%d' % self.httpd.port
5617 port = ':%d' % self.httpd.port
5618 if port == ':80':
5618 if port == ':80':
5619 port = ''
5619 port = ''
5620
5620
5621 bindaddr = self.httpd.addr
5621 bindaddr = self.httpd.addr
5622 if bindaddr == '0.0.0.0':
5622 if bindaddr == '0.0.0.0':
5623 bindaddr = '*'
5623 bindaddr = '*'
5624 elif ':' in bindaddr: # IPv6
5624 elif ':' in bindaddr: # IPv6
5625 bindaddr = '[%s]' % bindaddr
5625 bindaddr = '[%s]' % bindaddr
5626
5626
5627 fqaddr = self.httpd.fqaddr
5627 fqaddr = self.httpd.fqaddr
5628 if ':' in fqaddr:
5628 if ':' in fqaddr:
5629 fqaddr = '[%s]' % fqaddr
5629 fqaddr = '[%s]' % fqaddr
5630 if self.opts['port']:
5630 if self.opts['port']:
5631 write = self.ui.status
5631 write = self.ui.status
5632 else:
5632 else:
5633 write = self.ui.write
5633 write = self.ui.write
5634 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5634 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5635 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5635 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5636 self.ui.flush() # avoid buffering of status message
5636 self.ui.flush() # avoid buffering of status message
5637
5637
5638 def run(self):
5638 def run(self):
5639 self.httpd.serve_forever()
5639 self.httpd.serve_forever()
5640
5640
5641
5641
5642 @command('^status|st',
5642 @command('^status|st',
5643 [('A', 'all', None, _('show status of all files')),
5643 [('A', 'all', None, _('show status of all files')),
5644 ('m', 'modified', None, _('show only modified files')),
5644 ('m', 'modified', None, _('show only modified files')),
5645 ('a', 'added', None, _('show only added files')),
5645 ('a', 'added', None, _('show only added files')),
5646 ('r', 'removed', None, _('show only removed files')),
5646 ('r', 'removed', None, _('show only removed files')),
5647 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5647 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5648 ('c', 'clean', None, _('show only files without changes')),
5648 ('c', 'clean', None, _('show only files without changes')),
5649 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5649 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5650 ('i', 'ignored', None, _('show only ignored files')),
5650 ('i', 'ignored', None, _('show only ignored files')),
5651 ('n', 'no-status', None, _('hide status prefix')),
5651 ('n', 'no-status', None, _('hide status prefix')),
5652 ('C', 'copies', None, _('show source of copied files')),
5652 ('C', 'copies', None, _('show source of copied files')),
5653 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5653 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5654 ('', 'rev', [], _('show difference from revision'), _('REV')),
5654 ('', 'rev', [], _('show difference from revision'), _('REV')),
5655 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5655 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5656 ] + walkopts + subrepoopts + formatteropts,
5656 ] + walkopts + subrepoopts + formatteropts,
5657 _('[OPTION]... [FILE]...'),
5657 _('[OPTION]... [FILE]...'),
5658 inferrepo=True)
5658 inferrepo=True)
5659 def status(ui, repo, *pats, **opts):
5659 def status(ui, repo, *pats, **opts):
5660 """show changed files in the working directory
5660 """show changed files in the working directory
5661
5661
5662 Show status of files in the repository. If names are given, only
5662 Show status of files in the repository. If names are given, only
5663 files that match are shown. Files that are clean or ignored or
5663 files that match are shown. Files that are clean or ignored or
5664 the source of a copy/move operation, are not listed unless
5664 the source of a copy/move operation, are not listed unless
5665 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5665 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5666 Unless options described with "show only ..." are given, the
5666 Unless options described with "show only ..." are given, the
5667 options -mardu are used.
5667 options -mardu are used.
5668
5668
5669 Option -q/--quiet hides untracked (unknown and ignored) files
5669 Option -q/--quiet hides untracked (unknown and ignored) files
5670 unless explicitly requested with -u/--unknown or -i/--ignored.
5670 unless explicitly requested with -u/--unknown or -i/--ignored.
5671
5671
5672 .. note::
5672 .. note::
5673
5673
5674 status may appear to disagree with diff if permissions have
5674 status may appear to disagree with diff if permissions have
5675 changed or a merge has occurred. The standard diff format does
5675 changed or a merge has occurred. The standard diff format does
5676 not report permission changes and diff only reports changes
5676 not report permission changes and diff only reports changes
5677 relative to one merge parent.
5677 relative to one merge parent.
5678
5678
5679 If one revision is given, it is used as the base revision.
5679 If one revision is given, it is used as the base revision.
5680 If two revisions are given, the differences between them are
5680 If two revisions are given, the differences between them are
5681 shown. The --change option can also be used as a shortcut to list
5681 shown. The --change option can also be used as a shortcut to list
5682 the changed files of a revision from its first parent.
5682 the changed files of a revision from its first parent.
5683
5683
5684 The codes used to show the status of files are::
5684 The codes used to show the status of files are::
5685
5685
5686 M = modified
5686 M = modified
5687 A = added
5687 A = added
5688 R = removed
5688 R = removed
5689 C = clean
5689 C = clean
5690 ! = missing (deleted by non-hg command, but still tracked)
5690 ! = missing (deleted by non-hg command, but still tracked)
5691 ? = not tracked
5691 ? = not tracked
5692 I = ignored
5692 I = ignored
5693 = origin of the previous file (with --copies)
5693 = origin of the previous file (with --copies)
5694
5694
5695 .. container:: verbose
5695 .. container:: verbose
5696
5696
5697 Examples:
5697 Examples:
5698
5698
5699 - show changes in the working directory relative to a
5699 - show changes in the working directory relative to a
5700 changeset::
5700 changeset::
5701
5701
5702 hg status --rev 9353
5702 hg status --rev 9353
5703
5703
5704 - show all changes including copies in an existing changeset::
5704 - show all changes including copies in an existing changeset::
5705
5705
5706 hg status --copies --change 9353
5706 hg status --copies --change 9353
5707
5707
5708 - get a NUL separated list of added files, suitable for xargs::
5708 - get a NUL separated list of added files, suitable for xargs::
5709
5709
5710 hg status -an0
5710 hg status -an0
5711
5711
5712 Returns 0 on success.
5712 Returns 0 on success.
5713 """
5713 """
5714
5714
5715 revs = opts.get('rev')
5715 revs = opts.get('rev')
5716 change = opts.get('change')
5716 change = opts.get('change')
5717
5717
5718 if revs and change:
5718 if revs and change:
5719 msg = _('cannot specify --rev and --change at the same time')
5719 msg = _('cannot specify --rev and --change at the same time')
5720 raise util.Abort(msg)
5720 raise util.Abort(msg)
5721 elif change:
5721 elif change:
5722 node2 = scmutil.revsingle(repo, change, None).node()
5722 node2 = scmutil.revsingle(repo, change, None).node()
5723 node1 = repo[node2].p1().node()
5723 node1 = repo[node2].p1().node()
5724 else:
5724 else:
5725 node1, node2 = scmutil.revpair(repo, revs)
5725 node1, node2 = scmutil.revpair(repo, revs)
5726
5726
5727 if pats:
5727 if pats:
5728 cwd = repo.getcwd()
5728 cwd = repo.getcwd()
5729 else:
5729 else:
5730 cwd = ''
5730 cwd = ''
5731
5731
5732 if opts.get('print0'):
5732 if opts.get('print0'):
5733 end = '\0'
5733 end = '\0'
5734 else:
5734 else:
5735 end = '\n'
5735 end = '\n'
5736 copy = {}
5736 copy = {}
5737 states = 'modified added removed deleted unknown ignored clean'.split()
5737 states = 'modified added removed deleted unknown ignored clean'.split()
5738 show = [k for k in states if opts.get(k)]
5738 show = [k for k in states if opts.get(k)]
5739 if opts.get('all'):
5739 if opts.get('all'):
5740 show += ui.quiet and (states[:4] + ['clean']) or states
5740 show += ui.quiet and (states[:4] + ['clean']) or states
5741 if not show:
5741 if not show:
5742 if ui.quiet:
5742 if ui.quiet:
5743 show = states[:4]
5743 show = states[:4]
5744 else:
5744 else:
5745 show = states[:5]
5745 show = states[:5]
5746
5746
5747 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5747 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5748 'ignored' in show, 'clean' in show, 'unknown' in show,
5748 'ignored' in show, 'clean' in show, 'unknown' in show,
5749 opts.get('subrepos'))
5749 opts.get('subrepos'))
5750 changestates = zip(states, 'MAR!?IC', stat)
5750 changestates = zip(states, 'MAR!?IC', stat)
5751
5751
5752 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5752 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5753 copy = copies.pathcopies(repo[node1], repo[node2])
5753 copy = copies.pathcopies(repo[node1], repo[node2])
5754
5754
5755 fm = ui.formatter('status', opts)
5755 fm = ui.formatter('status', opts)
5756 fmt = '%s' + end
5756 fmt = '%s' + end
5757 showchar = not opts.get('no_status')
5757 showchar = not opts.get('no_status')
5758
5758
5759 for state, char, files in changestates:
5759 for state, char, files in changestates:
5760 if state in show:
5760 if state in show:
5761 label = 'status.' + state
5761 label = 'status.' + state
5762 for f in files:
5762 for f in files:
5763 fm.startitem()
5763 fm.startitem()
5764 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5764 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5765 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5765 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5766 if f in copy:
5766 if f in copy:
5767 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5767 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5768 label='status.copied')
5768 label='status.copied')
5769 fm.end()
5769 fm.end()
5770
5770
5771 @command('^summary|sum',
5771 @command('^summary|sum',
5772 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5772 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5773 def summary(ui, repo, **opts):
5773 def summary(ui, repo, **opts):
5774 """summarize working directory state
5774 """summarize working directory state
5775
5775
5776 This generates a brief summary of the working directory state,
5776 This generates a brief summary of the working directory state,
5777 including parents, branch, commit status, and available updates.
5777 including parents, branch, commit status, and available updates.
5778
5778
5779 With the --remote option, this will check the default paths for
5779 With the --remote option, this will check the default paths for
5780 incoming and outgoing changes. This can be time-consuming.
5780 incoming and outgoing changes. This can be time-consuming.
5781
5781
5782 Returns 0 on success.
5782 Returns 0 on success.
5783 """
5783 """
5784
5784
5785 ctx = repo[None]
5785 ctx = repo[None]
5786 parents = ctx.parents()
5786 parents = ctx.parents()
5787 pnode = parents[0].node()
5787 pnode = parents[0].node()
5788 marks = []
5788 marks = []
5789
5789
5790 for p in parents:
5790 for p in parents:
5791 # label with log.changeset (instead of log.parent) since this
5791 # label with log.changeset (instead of log.parent) since this
5792 # shows a working directory parent *changeset*:
5792 # shows a working directory parent *changeset*:
5793 # i18n: column positioning for "hg summary"
5793 # i18n: column positioning for "hg summary"
5794 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5794 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5795 label='log.changeset changeset.%s' % p.phasestr())
5795 label='log.changeset changeset.%s' % p.phasestr())
5796 ui.write(' '.join(p.tags()), label='log.tag')
5796 ui.write(' '.join(p.tags()), label='log.tag')
5797 if p.bookmarks():
5797 if p.bookmarks():
5798 marks.extend(p.bookmarks())
5798 marks.extend(p.bookmarks())
5799 if p.rev() == -1:
5799 if p.rev() == -1:
5800 if not len(repo):
5800 if not len(repo):
5801 ui.write(_(' (empty repository)'))
5801 ui.write(_(' (empty repository)'))
5802 else:
5802 else:
5803 ui.write(_(' (no revision checked out)'))
5803 ui.write(_(' (no revision checked out)'))
5804 ui.write('\n')
5804 ui.write('\n')
5805 if p.description():
5805 if p.description():
5806 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5806 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5807 label='log.summary')
5807 label='log.summary')
5808
5808
5809 branch = ctx.branch()
5809 branch = ctx.branch()
5810 bheads = repo.branchheads(branch)
5810 bheads = repo.branchheads(branch)
5811 # i18n: column positioning for "hg summary"
5811 # i18n: column positioning for "hg summary"
5812 m = _('branch: %s\n') % branch
5812 m = _('branch: %s\n') % branch
5813 if branch != 'default':
5813 if branch != 'default':
5814 ui.write(m, label='log.branch')
5814 ui.write(m, label='log.branch')
5815 else:
5815 else:
5816 ui.status(m, label='log.branch')
5816 ui.status(m, label='log.branch')
5817
5817
5818 if marks:
5818 if marks:
5819 current = repo._bookmarkcurrent
5819 current = repo._bookmarkcurrent
5820 # i18n: column positioning for "hg summary"
5820 # i18n: column positioning for "hg summary"
5821 ui.write(_('bookmarks:'), label='log.bookmark')
5821 ui.write(_('bookmarks:'), label='log.bookmark')
5822 if current is not None:
5822 if current is not None:
5823 if current in marks:
5823 if current in marks:
5824 ui.write(' *' + current, label='bookmarks.current')
5824 ui.write(' *' + current, label='bookmarks.current')
5825 marks.remove(current)
5825 marks.remove(current)
5826 else:
5826 else:
5827 ui.write(' [%s]' % current, label='bookmarks.current')
5827 ui.write(' [%s]' % current, label='bookmarks.current')
5828 for m in marks:
5828 for m in marks:
5829 ui.write(' ' + m, label='log.bookmark')
5829 ui.write(' ' + m, label='log.bookmark')
5830 ui.write('\n', label='log.bookmark')
5830 ui.write('\n', label='log.bookmark')
5831
5831
5832 status = repo.status(unknown=True)
5832 status = repo.status(unknown=True)
5833
5833
5834 c = repo.dirstate.copies()
5834 c = repo.dirstate.copies()
5835 copied, renamed = [], []
5835 copied, renamed = [], []
5836 for d, s in c.iteritems():
5836 for d, s in c.iteritems():
5837 if s in status.removed:
5837 if s in status.removed:
5838 status.removed.remove(s)
5838 status.removed.remove(s)
5839 renamed.append(d)
5839 renamed.append(d)
5840 else:
5840 else:
5841 copied.append(d)
5841 copied.append(d)
5842 if d in status.added:
5842 if d in status.added:
5843 status.added.remove(d)
5843 status.added.remove(d)
5844
5844
5845 ms = mergemod.mergestate(repo)
5845 ms = mergemod.mergestate(repo)
5846 unresolved = [f for f in ms if ms[f] == 'u']
5846 unresolved = [f for f in ms if ms[f] == 'u']
5847
5847
5848 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5848 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5849
5849
5850 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5850 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5851 (ui.label(_('%d added'), 'status.added'), status.added),
5851 (ui.label(_('%d added'), 'status.added'), status.added),
5852 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5852 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5853 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5853 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5854 (ui.label(_('%d copied'), 'status.copied'), copied),
5854 (ui.label(_('%d copied'), 'status.copied'), copied),
5855 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5855 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5856 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5856 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5857 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5857 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5858 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5858 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5859 t = []
5859 t = []
5860 for l, s in labels:
5860 for l, s in labels:
5861 if s:
5861 if s:
5862 t.append(l % len(s))
5862 t.append(l % len(s))
5863
5863
5864 t = ', '.join(t)
5864 t = ', '.join(t)
5865 cleanworkdir = False
5865 cleanworkdir = False
5866
5866
5867 if repo.vfs.exists('updatestate'):
5867 if repo.vfs.exists('updatestate'):
5868 t += _(' (interrupted update)')
5868 t += _(' (interrupted update)')
5869 elif len(parents) > 1:
5869 elif len(parents) > 1:
5870 t += _(' (merge)')
5870 t += _(' (merge)')
5871 elif branch != parents[0].branch():
5871 elif branch != parents[0].branch():
5872 t += _(' (new branch)')
5872 t += _(' (new branch)')
5873 elif (parents[0].closesbranch() and
5873 elif (parents[0].closesbranch() and
5874 pnode in repo.branchheads(branch, closed=True)):
5874 pnode in repo.branchheads(branch, closed=True)):
5875 t += _(' (head closed)')
5875 t += _(' (head closed)')
5876 elif not (status.modified or status.added or status.removed or renamed or
5876 elif not (status.modified or status.added or status.removed or renamed or
5877 copied or subs):
5877 copied or subs):
5878 t += _(' (clean)')
5878 t += _(' (clean)')
5879 cleanworkdir = True
5879 cleanworkdir = True
5880 elif pnode not in bheads:
5880 elif pnode not in bheads:
5881 t += _(' (new branch head)')
5881 t += _(' (new branch head)')
5882
5882
5883 if cleanworkdir:
5883 if cleanworkdir:
5884 # i18n: column positioning for "hg summary"
5884 # i18n: column positioning for "hg summary"
5885 ui.status(_('commit: %s\n') % t.strip())
5885 ui.status(_('commit: %s\n') % t.strip())
5886 else:
5886 else:
5887 # i18n: column positioning for "hg summary"
5887 # i18n: column positioning for "hg summary"
5888 ui.write(_('commit: %s\n') % t.strip())
5888 ui.write(_('commit: %s\n') % t.strip())
5889
5889
5890 # all ancestors of branch heads - all ancestors of parent = new csets
5890 # all ancestors of branch heads - all ancestors of parent = new csets
5891 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5891 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5892 bheads))
5892 bheads))
5893
5893
5894 if new == 0:
5894 if new == 0:
5895 # i18n: column positioning for "hg summary"
5895 # i18n: column positioning for "hg summary"
5896 ui.status(_('update: (current)\n'))
5896 ui.status(_('update: (current)\n'))
5897 elif pnode not in bheads:
5897 elif pnode not in bheads:
5898 # i18n: column positioning for "hg summary"
5898 # i18n: column positioning for "hg summary"
5899 ui.write(_('update: %d new changesets (update)\n') % new)
5899 ui.write(_('update: %d new changesets (update)\n') % new)
5900 else:
5900 else:
5901 # i18n: column positioning for "hg summary"
5901 # i18n: column positioning for "hg summary"
5902 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5902 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5903 (new, len(bheads)))
5903 (new, len(bheads)))
5904
5904
5905 cmdutil.summaryhooks(ui, repo)
5905 cmdutil.summaryhooks(ui, repo)
5906
5906
5907 if opts.get('remote'):
5907 if opts.get('remote'):
5908 needsincoming, needsoutgoing = True, True
5908 needsincoming, needsoutgoing = True, True
5909 else:
5909 else:
5910 needsincoming, needsoutgoing = False, False
5910 needsincoming, needsoutgoing = False, False
5911 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5911 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5912 if i:
5912 if i:
5913 needsincoming = True
5913 needsincoming = True
5914 if o:
5914 if o:
5915 needsoutgoing = True
5915 needsoutgoing = True
5916 if not needsincoming and not needsoutgoing:
5916 if not needsincoming and not needsoutgoing:
5917 return
5917 return
5918
5918
5919 def getincoming():
5919 def getincoming():
5920 source, branches = hg.parseurl(ui.expandpath('default'))
5920 source, branches = hg.parseurl(ui.expandpath('default'))
5921 sbranch = branches[0]
5921 sbranch = branches[0]
5922 try:
5922 try:
5923 other = hg.peer(repo, {}, source)
5923 other = hg.peer(repo, {}, source)
5924 except error.RepoError:
5924 except error.RepoError:
5925 if opts.get('remote'):
5925 if opts.get('remote'):
5926 raise
5926 raise
5927 return source, sbranch, None, None, None
5927 return source, sbranch, None, None, None
5928 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5928 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5929 if revs:
5929 if revs:
5930 revs = [other.lookup(rev) for rev in revs]
5930 revs = [other.lookup(rev) for rev in revs]
5931 ui.debug('comparing with %s\n' % util.hidepassword(source))
5931 ui.debug('comparing with %s\n' % util.hidepassword(source))
5932 repo.ui.pushbuffer()
5932 repo.ui.pushbuffer()
5933 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5933 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5934 repo.ui.popbuffer()
5934 repo.ui.popbuffer()
5935 return source, sbranch, other, commoninc, commoninc[1]
5935 return source, sbranch, other, commoninc, commoninc[1]
5936
5936
5937 if needsincoming:
5937 if needsincoming:
5938 source, sbranch, sother, commoninc, incoming = getincoming()
5938 source, sbranch, sother, commoninc, incoming = getincoming()
5939 else:
5939 else:
5940 source = sbranch = sother = commoninc = incoming = None
5940 source = sbranch = sother = commoninc = incoming = None
5941
5941
5942 def getoutgoing():
5942 def getoutgoing():
5943 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5943 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5944 dbranch = branches[0]
5944 dbranch = branches[0]
5945 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5945 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5946 if source != dest:
5946 if source != dest:
5947 try:
5947 try:
5948 dother = hg.peer(repo, {}, dest)
5948 dother = hg.peer(repo, {}, dest)
5949 except error.RepoError:
5949 except error.RepoError:
5950 if opts.get('remote'):
5950 if opts.get('remote'):
5951 raise
5951 raise
5952 return dest, dbranch, None, None
5952 return dest, dbranch, None, None
5953 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5953 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5954 elif sother is None:
5954 elif sother is None:
5955 # there is no explicit destination peer, but source one is invalid
5955 # there is no explicit destination peer, but source one is invalid
5956 return dest, dbranch, None, None
5956 return dest, dbranch, None, None
5957 else:
5957 else:
5958 dother = sother
5958 dother = sother
5959 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5959 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5960 common = None
5960 common = None
5961 else:
5961 else:
5962 common = commoninc
5962 common = commoninc
5963 if revs:
5963 if revs:
5964 revs = [repo.lookup(rev) for rev in revs]
5964 revs = [repo.lookup(rev) for rev in revs]
5965 repo.ui.pushbuffer()
5965 repo.ui.pushbuffer()
5966 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5966 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5967 commoninc=common)
5967 commoninc=common)
5968 repo.ui.popbuffer()
5968 repo.ui.popbuffer()
5969 return dest, dbranch, dother, outgoing
5969 return dest, dbranch, dother, outgoing
5970
5970
5971 if needsoutgoing:
5971 if needsoutgoing:
5972 dest, dbranch, dother, outgoing = getoutgoing()
5972 dest, dbranch, dother, outgoing = getoutgoing()
5973 else:
5973 else:
5974 dest = dbranch = dother = outgoing = None
5974 dest = dbranch = dother = outgoing = None
5975
5975
5976 if opts.get('remote'):
5976 if opts.get('remote'):
5977 t = []
5977 t = []
5978 if incoming:
5978 if incoming:
5979 t.append(_('1 or more incoming'))
5979 t.append(_('1 or more incoming'))
5980 o = outgoing.missing
5980 o = outgoing.missing
5981 if o:
5981 if o:
5982 t.append(_('%d outgoing') % len(o))
5982 t.append(_('%d outgoing') % len(o))
5983 other = dother or sother
5983 other = dother or sother
5984 if 'bookmarks' in other.listkeys('namespaces'):
5984 if 'bookmarks' in other.listkeys('namespaces'):
5985 counts = bookmarks.summary(repo, other)
5985 counts = bookmarks.summary(repo, other)
5986 if counts[0] > 0:
5986 if counts[0] > 0:
5987 t.append(_('%d incoming bookmarks') % counts[0])
5987 t.append(_('%d incoming bookmarks') % counts[0])
5988 if counts[1] > 0:
5988 if counts[1] > 0:
5989 t.append(_('%d outgoing bookmarks') % counts[1])
5989 t.append(_('%d outgoing bookmarks') % counts[1])
5990
5990
5991 if t:
5991 if t:
5992 # i18n: column positioning for "hg summary"
5992 # i18n: column positioning for "hg summary"
5993 ui.write(_('remote: %s\n') % (', '.join(t)))
5993 ui.write(_('remote: %s\n') % (', '.join(t)))
5994 else:
5994 else:
5995 # i18n: column positioning for "hg summary"
5995 # i18n: column positioning for "hg summary"
5996 ui.status(_('remote: (synced)\n'))
5996 ui.status(_('remote: (synced)\n'))
5997
5997
5998 cmdutil.summaryremotehooks(ui, repo, opts,
5998 cmdutil.summaryremotehooks(ui, repo, opts,
5999 ((source, sbranch, sother, commoninc),
5999 ((source, sbranch, sother, commoninc),
6000 (dest, dbranch, dother, outgoing)))
6000 (dest, dbranch, dother, outgoing)))
6001
6001
6002 @command('tag',
6002 @command('tag',
6003 [('f', 'force', None, _('force tag')),
6003 [('f', 'force', None, _('force tag')),
6004 ('l', 'local', None, _('make the tag local')),
6004 ('l', 'local', None, _('make the tag local')),
6005 ('r', 'rev', '', _('revision to tag'), _('REV')),
6005 ('r', 'rev', '', _('revision to tag'), _('REV')),
6006 ('', 'remove', None, _('remove a tag')),
6006 ('', 'remove', None, _('remove a tag')),
6007 # -l/--local is already there, commitopts cannot be used
6007 # -l/--local is already there, commitopts cannot be used
6008 ('e', 'edit', None, _('invoke editor on commit messages')),
6008 ('e', 'edit', None, _('invoke editor on commit messages')),
6009 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6009 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6010 ] + commitopts2,
6010 ] + commitopts2,
6011 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6011 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6012 def tag(ui, repo, name1, *names, **opts):
6012 def tag(ui, repo, name1, *names, **opts):
6013 """add one or more tags for the current or given revision
6013 """add one or more tags for the current or given revision
6014
6014
6015 Name a particular revision using <name>.
6015 Name a particular revision using <name>.
6016
6016
6017 Tags are used to name particular revisions of the repository and are
6017 Tags are used to name particular revisions of the repository and are
6018 very useful to compare different revisions, to go back to significant
6018 very useful to compare different revisions, to go back to significant
6019 earlier versions or to mark branch points as releases, etc. Changing
6019 earlier versions or to mark branch points as releases, etc. Changing
6020 an existing tag is normally disallowed; use -f/--force to override.
6020 an existing tag is normally disallowed; use -f/--force to override.
6021
6021
6022 If no revision is given, the parent of the working directory is
6022 If no revision is given, the parent of the working directory is
6023 used.
6023 used.
6024
6024
6025 To facilitate version control, distribution, and merging of tags,
6025 To facilitate version control, distribution, and merging of tags,
6026 they are stored as a file named ".hgtags" which is managed similarly
6026 they are stored as a file named ".hgtags" which is managed similarly
6027 to other project files and can be hand-edited if necessary. This
6027 to other project files and can be hand-edited if necessary. This
6028 also means that tagging creates a new commit. The file
6028 also means that tagging creates a new commit. The file
6029 ".hg/localtags" is used for local tags (not shared among
6029 ".hg/localtags" is used for local tags (not shared among
6030 repositories).
6030 repositories).
6031
6031
6032 Tag commits are usually made at the head of a branch. If the parent
6032 Tag commits are usually made at the head of a branch. If the parent
6033 of the working directory is not a branch head, :hg:`tag` aborts; use
6033 of the working directory is not a branch head, :hg:`tag` aborts; use
6034 -f/--force to force the tag commit to be based on a non-head
6034 -f/--force to force the tag commit to be based on a non-head
6035 changeset.
6035 changeset.
6036
6036
6037 See :hg:`help dates` for a list of formats valid for -d/--date.
6037 See :hg:`help dates` for a list of formats valid for -d/--date.
6038
6038
6039 Since tag names have priority over branch names during revision
6039 Since tag names have priority over branch names during revision
6040 lookup, using an existing branch name as a tag name is discouraged.
6040 lookup, using an existing branch name as a tag name is discouraged.
6041
6041
6042 Returns 0 on success.
6042 Returns 0 on success.
6043 """
6043 """
6044 wlock = lock = None
6044 wlock = lock = None
6045 try:
6045 try:
6046 wlock = repo.wlock()
6046 wlock = repo.wlock()
6047 lock = repo.lock()
6047 lock = repo.lock()
6048 rev_ = "."
6048 rev_ = "."
6049 names = [t.strip() for t in (name1,) + names]
6049 names = [t.strip() for t in (name1,) + names]
6050 if len(names) != len(set(names)):
6050 if len(names) != len(set(names)):
6051 raise util.Abort(_('tag names must be unique'))
6051 raise util.Abort(_('tag names must be unique'))
6052 for n in names:
6052 for n in names:
6053 scmutil.checknewlabel(repo, n, 'tag')
6053 scmutil.checknewlabel(repo, n, 'tag')
6054 if not n:
6054 if not n:
6055 raise util.Abort(_('tag names cannot consist entirely of '
6055 raise util.Abort(_('tag names cannot consist entirely of '
6056 'whitespace'))
6056 'whitespace'))
6057 if opts.get('rev') and opts.get('remove'):
6057 if opts.get('rev') and opts.get('remove'):
6058 raise util.Abort(_("--rev and --remove are incompatible"))
6058 raise util.Abort(_("--rev and --remove are incompatible"))
6059 if opts.get('rev'):
6059 if opts.get('rev'):
6060 rev_ = opts['rev']
6060 rev_ = opts['rev']
6061 message = opts.get('message')
6061 message = opts.get('message')
6062 if opts.get('remove'):
6062 if opts.get('remove'):
6063 if opts.get('local'):
6063 if opts.get('local'):
6064 expectedtype = 'local'
6064 expectedtype = 'local'
6065 else:
6065 else:
6066 expectedtype = 'global'
6066 expectedtype = 'global'
6067
6067
6068 for n in names:
6068 for n in names:
6069 if not repo.tagtype(n):
6069 if not repo.tagtype(n):
6070 raise util.Abort(_("tag '%s' does not exist") % n)
6070 raise util.Abort(_("tag '%s' does not exist") % n)
6071 if repo.tagtype(n) != expectedtype:
6071 if repo.tagtype(n) != expectedtype:
6072 if expectedtype == 'global':
6072 if expectedtype == 'global':
6073 raise util.Abort(_("tag '%s' is not a global tag") % n)
6073 raise util.Abort(_("tag '%s' is not a global tag") % n)
6074 else:
6074 else:
6075 raise util.Abort(_("tag '%s' is not a local tag") % n)
6075 raise util.Abort(_("tag '%s' is not a local tag") % n)
6076 rev_ = nullid
6076 rev_ = nullid
6077 if not message:
6077 if not message:
6078 # we don't translate commit messages
6078 # we don't translate commit messages
6079 message = 'Removed tag %s' % ', '.join(names)
6079 message = 'Removed tag %s' % ', '.join(names)
6080 elif not opts.get('force'):
6080 elif not opts.get('force'):
6081 for n in names:
6081 for n in names:
6082 if n in repo.tags():
6082 if n in repo.tags():
6083 raise util.Abort(_("tag '%s' already exists "
6083 raise util.Abort(_("tag '%s' already exists "
6084 "(use -f to force)") % n)
6084 "(use -f to force)") % n)
6085 if not opts.get('local'):
6085 if not opts.get('local'):
6086 p1, p2 = repo.dirstate.parents()
6086 p1, p2 = repo.dirstate.parents()
6087 if p2 != nullid:
6087 if p2 != nullid:
6088 raise util.Abort(_('uncommitted merge'))
6088 raise util.Abort(_('uncommitted merge'))
6089 bheads = repo.branchheads()
6089 bheads = repo.branchheads()
6090 if not opts.get('force') and bheads and p1 not in bheads:
6090 if not opts.get('force') and bheads and p1 not in bheads:
6091 raise util.Abort(_('not at a branch head (use -f to force)'))
6091 raise util.Abort(_('not at a branch head (use -f to force)'))
6092 r = scmutil.revsingle(repo, rev_).node()
6092 r = scmutil.revsingle(repo, rev_).node()
6093
6093
6094 if not message:
6094 if not message:
6095 # we don't translate commit messages
6095 # we don't translate commit messages
6096 message = ('Added tag %s for changeset %s' %
6096 message = ('Added tag %s for changeset %s' %
6097 (', '.join(names), short(r)))
6097 (', '.join(names), short(r)))
6098
6098
6099 date = opts.get('date')
6099 date = opts.get('date')
6100 if date:
6100 if date:
6101 date = util.parsedate(date)
6101 date = util.parsedate(date)
6102
6102
6103 if opts.get('remove'):
6103 if opts.get('remove'):
6104 editform = 'tag.remove'
6104 editform = 'tag.remove'
6105 else:
6105 else:
6106 editform = 'tag.add'
6106 editform = 'tag.add'
6107 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6107 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6108
6108
6109 # don't allow tagging the null rev
6109 # don't allow tagging the null rev
6110 if (not opts.get('remove') and
6110 if (not opts.get('remove') and
6111 scmutil.revsingle(repo, rev_).rev() == nullrev):
6111 scmutil.revsingle(repo, rev_).rev() == nullrev):
6112 raise util.Abort(_("cannot tag null revision"))
6112 raise util.Abort(_("cannot tag null revision"))
6113
6113
6114 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6114 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6115 editor=editor)
6115 editor=editor)
6116 finally:
6116 finally:
6117 release(lock, wlock)
6117 release(lock, wlock)
6118
6118
6119 @command('tags', formatteropts, '')
6119 @command('tags', formatteropts, '')
6120 def tags(ui, repo, **opts):
6120 def tags(ui, repo, **opts):
6121 """list repository tags
6121 """list repository tags
6122
6122
6123 This lists both regular and local tags. When the -v/--verbose
6123 This lists both regular and local tags. When the -v/--verbose
6124 switch is used, a third column "local" is printed for local tags.
6124 switch is used, a third column "local" is printed for local tags.
6125
6125
6126 Returns 0 on success.
6126 Returns 0 on success.
6127 """
6127 """
6128
6128
6129 fm = ui.formatter('tags', opts)
6129 fm = ui.formatter('tags', opts)
6130 hexfunc = fm.hexfunc
6130 hexfunc = fm.hexfunc
6131 tagtype = ""
6131 tagtype = ""
6132
6132
6133 for t, n in reversed(repo.tagslist()):
6133 for t, n in reversed(repo.tagslist()):
6134 hn = hexfunc(n)
6134 hn = hexfunc(n)
6135 label = 'tags.normal'
6135 label = 'tags.normal'
6136 tagtype = ''
6136 tagtype = ''
6137 if repo.tagtype(t) == 'local':
6137 if repo.tagtype(t) == 'local':
6138 label = 'tags.local'
6138 label = 'tags.local'
6139 tagtype = 'local'
6139 tagtype = 'local'
6140
6140
6141 fm.startitem()
6141 fm.startitem()
6142 fm.write('tag', '%s', t, label=label)
6142 fm.write('tag', '%s', t, label=label)
6143 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6143 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6144 fm.condwrite(not ui.quiet, 'rev node', fmt,
6144 fm.condwrite(not ui.quiet, 'rev node', fmt,
6145 repo.changelog.rev(n), hn, label=label)
6145 repo.changelog.rev(n), hn, label=label)
6146 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6146 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6147 tagtype, label=label)
6147 tagtype, label=label)
6148 fm.plain('\n')
6148 fm.plain('\n')
6149 fm.end()
6149 fm.end()
6150
6150
6151 @command('tip',
6151 @command('tip',
6152 [('p', 'patch', None, _('show patch')),
6152 [('p', 'patch', None, _('show patch')),
6153 ('g', 'git', None, _('use git extended diff format')),
6153 ('g', 'git', None, _('use git extended diff format')),
6154 ] + templateopts,
6154 ] + templateopts,
6155 _('[-p] [-g]'))
6155 _('[-p] [-g]'))
6156 def tip(ui, repo, **opts):
6156 def tip(ui, repo, **opts):
6157 """show the tip revision (DEPRECATED)
6157 """show the tip revision (DEPRECATED)
6158
6158
6159 The tip revision (usually just called the tip) is the changeset
6159 The tip revision (usually just called the tip) is the changeset
6160 most recently added to the repository (and therefore the most
6160 most recently added to the repository (and therefore the most
6161 recently changed head).
6161 recently changed head).
6162
6162
6163 If you have just made a commit, that commit will be the tip. If
6163 If you have just made a commit, that commit will be the tip. If
6164 you have just pulled changes from another repository, the tip of
6164 you have just pulled changes from another repository, the tip of
6165 that repository becomes the current tip. The "tip" tag is special
6165 that repository becomes the current tip. The "tip" tag is special
6166 and cannot be renamed or assigned to a different changeset.
6166 and cannot be renamed or assigned to a different changeset.
6167
6167
6168 This command is deprecated, please use :hg:`heads` instead.
6168 This command is deprecated, please use :hg:`heads` instead.
6169
6169
6170 Returns 0 on success.
6170 Returns 0 on success.
6171 """
6171 """
6172 displayer = cmdutil.show_changeset(ui, repo, opts)
6172 displayer = cmdutil.show_changeset(ui, repo, opts)
6173 displayer.show(repo['tip'])
6173 displayer.show(repo['tip'])
6174 displayer.close()
6174 displayer.close()
6175
6175
6176 @command('unbundle',
6176 @command('unbundle',
6177 [('u', 'update', None,
6177 [('u', 'update', None,
6178 _('update to new branch head if changesets were unbundled'))],
6178 _('update to new branch head if changesets were unbundled'))],
6179 _('[-u] FILE...'))
6179 _('[-u] FILE...'))
6180 def unbundle(ui, repo, fname1, *fnames, **opts):
6180 def unbundle(ui, repo, fname1, *fnames, **opts):
6181 """apply one or more changegroup files
6181 """apply one or more changegroup files
6182
6182
6183 Apply one or more compressed changegroup files generated by the
6183 Apply one or more compressed changegroup files generated by the
6184 bundle command.
6184 bundle command.
6185
6185
6186 Returns 0 on success, 1 if an update has unresolved files.
6186 Returns 0 on success, 1 if an update has unresolved files.
6187 """
6187 """
6188 fnames = (fname1,) + fnames
6188 fnames = (fname1,) + fnames
6189
6189
6190 lock = repo.lock()
6190 lock = repo.lock()
6191 try:
6191 try:
6192 for fname in fnames:
6192 for fname in fnames:
6193 f = hg.openpath(ui, fname)
6193 f = hg.openpath(ui, fname)
6194 gen = exchange.readbundle(ui, f, fname)
6194 gen = exchange.readbundle(ui, f, fname)
6195 if isinstance(gen, bundle2.unbundle20):
6195 if isinstance(gen, bundle2.unbundle20):
6196 tr = repo.transaction('unbundle')
6196 tr = repo.transaction('unbundle')
6197 try:
6197 try:
6198 op = bundle2.processbundle(repo, gen, lambda: tr)
6198 op = bundle2.processbundle(repo, gen, lambda: tr)
6199 tr.close()
6199 tr.close()
6200 finally:
6200 finally:
6201 if tr:
6201 if tr:
6202 tr.release()
6202 tr.release()
6203 changes = [r.get('result', 0)
6203 changes = [r.get('result', 0)
6204 for r in op.records['changegroup']]
6204 for r in op.records['changegroup']]
6205 modheads = changegroup.combineresults(changes)
6205 modheads = changegroup.combineresults(changes)
6206 else:
6206 else:
6207 modheads = changegroup.addchangegroup(repo, gen, 'unbundle',
6207 modheads = changegroup.addchangegroup(repo, gen, 'unbundle',
6208 'bundle:' + fname)
6208 'bundle:' + fname)
6209 finally:
6209 finally:
6210 lock.release()
6210 lock.release()
6211
6211
6212 return postincoming(ui, repo, modheads, opts.get('update'), None)
6212 return postincoming(ui, repo, modheads, opts.get('update'), None)
6213
6213
6214 @command('^update|up|checkout|co',
6214 @command('^update|up|checkout|co',
6215 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6215 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6216 ('c', 'check', None,
6216 ('c', 'check', None,
6217 _('update across branches if no uncommitted changes')),
6217 _('update across branches if no uncommitted changes')),
6218 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6218 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6219 ('r', 'rev', '', _('revision'), _('REV'))
6219 ('r', 'rev', '', _('revision'), _('REV'))
6220 ] + mergetoolopts,
6220 ] + mergetoolopts,
6221 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6221 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6222 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6222 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6223 tool=None):
6223 tool=None):
6224 """update working directory (or switch revisions)
6224 """update working directory (or switch revisions)
6225
6225
6226 Update the repository's working directory to the specified
6226 Update the repository's working directory to the specified
6227 changeset. If no changeset is specified, update to the tip of the
6227 changeset. If no changeset is specified, update to the tip of the
6228 current named branch and move the current bookmark (see :hg:`help
6228 current named branch and move the current bookmark (see :hg:`help
6229 bookmarks`).
6229 bookmarks`).
6230
6230
6231 Update sets the working directory's parent revision to the specified
6231 Update sets the working directory's parent revision to the specified
6232 changeset (see :hg:`help parents`).
6232 changeset (see :hg:`help parents`).
6233
6233
6234 If the changeset is not a descendant or ancestor of the working
6234 If the changeset is not a descendant or ancestor of the working
6235 directory's parent, the update is aborted. With the -c/--check
6235 directory's parent, the update is aborted. With the -c/--check
6236 option, the working directory is checked for uncommitted changes; if
6236 option, the working directory is checked for uncommitted changes; if
6237 none are found, the working directory is updated to the specified
6237 none are found, the working directory is updated to the specified
6238 changeset.
6238 changeset.
6239
6239
6240 .. container:: verbose
6240 .. container:: verbose
6241
6241
6242 The following rules apply when the working directory contains
6242 The following rules apply when the working directory contains
6243 uncommitted changes:
6243 uncommitted changes:
6244
6244
6245 1. If neither -c/--check nor -C/--clean is specified, and if
6245 1. If neither -c/--check nor -C/--clean is specified, and if
6246 the requested changeset is an ancestor or descendant of
6246 the requested changeset is an ancestor or descendant of
6247 the working directory's parent, the uncommitted changes
6247 the working directory's parent, the uncommitted changes
6248 are merged into the requested changeset and the merged
6248 are merged into the requested changeset and the merged
6249 result is left uncommitted. If the requested changeset is
6249 result is left uncommitted. If the requested changeset is
6250 not an ancestor or descendant (that is, it is on another
6250 not an ancestor or descendant (that is, it is on another
6251 branch), the update is aborted and the uncommitted changes
6251 branch), the update is aborted and the uncommitted changes
6252 are preserved.
6252 are preserved.
6253
6253
6254 2. With the -c/--check option, the update is aborted and the
6254 2. With the -c/--check option, the update is aborted and the
6255 uncommitted changes are preserved.
6255 uncommitted changes are preserved.
6256
6256
6257 3. With the -C/--clean option, uncommitted changes are discarded and
6257 3. With the -C/--clean option, uncommitted changes are discarded and
6258 the working directory is updated to the requested changeset.
6258 the working directory is updated to the requested changeset.
6259
6259
6260 To cancel an uncommitted merge (and lose your changes), use
6260 To cancel an uncommitted merge (and lose your changes), use
6261 :hg:`update --clean .`.
6261 :hg:`update --clean .`.
6262
6262
6263 Use null as the changeset to remove the working directory (like
6263 Use null as the changeset to remove the working directory (like
6264 :hg:`clone -U`).
6264 :hg:`clone -U`).
6265
6265
6266 If you want to revert just one file to an older revision, use
6266 If you want to revert just one file to an older revision, use
6267 :hg:`revert [-r REV] NAME`.
6267 :hg:`revert [-r REV] NAME`.
6268
6268
6269 See :hg:`help dates` for a list of formats valid for -d/--date.
6269 See :hg:`help dates` for a list of formats valid for -d/--date.
6270
6270
6271 Returns 0 on success, 1 if there are unresolved files.
6271 Returns 0 on success, 1 if there are unresolved files.
6272 """
6272 """
6273 if rev and node:
6273 if rev and node:
6274 raise util.Abort(_("please specify just one revision"))
6274 raise util.Abort(_("please specify just one revision"))
6275
6275
6276 if rev is None or rev == '':
6276 if rev is None or rev == '':
6277 rev = node
6277 rev = node
6278
6278
6279 cmdutil.clearunfinished(repo)
6279 cmdutil.clearunfinished(repo)
6280
6280
6281 # with no argument, we also move the current bookmark, if any
6281 # with no argument, we also move the current bookmark, if any
6282 rev, movemarkfrom = bookmarks.calculateupdate(ui, repo, rev)
6282 rev, movemarkfrom = bookmarks.calculateupdate(ui, repo, rev)
6283
6283
6284 # if we defined a bookmark, we have to remember the original bookmark name
6284 # if we defined a bookmark, we have to remember the original bookmark name
6285 brev = rev
6285 brev = rev
6286 rev = scmutil.revsingle(repo, rev, rev).rev()
6286 rev = scmutil.revsingle(repo, rev, rev).rev()
6287
6287
6288 if check and clean:
6288 if check and clean:
6289 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
6289 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
6290
6290
6291 if date:
6291 if date:
6292 if rev is not None:
6292 if rev is not None:
6293 raise util.Abort(_("you can't specify a revision and a date"))
6293 raise util.Abort(_("you can't specify a revision and a date"))
6294 rev = cmdutil.finddate(ui, repo, date)
6294 rev = cmdutil.finddate(ui, repo, date)
6295
6295
6296 if check:
6296 if check:
6297 c = repo[None]
6297 c = repo[None]
6298 if c.dirty(merge=False, branch=False, missing=True):
6298 if c.dirty(merge=False, branch=False, missing=True):
6299 raise util.Abort(_("uncommitted changes"))
6299 raise util.Abort(_("uncommitted changes"))
6300 if rev is None:
6300 if rev is None:
6301 rev = repo[repo[None].branch()].rev()
6301 rev = repo[repo[None].branch()].rev()
6302
6302
6303 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6303 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6304
6304
6305 if clean:
6305 if clean:
6306 ret = hg.clean(repo, rev)
6306 ret = hg.clean(repo, rev)
6307 else:
6307 else:
6308 ret = hg.update(repo, rev)
6308 ret = hg.update(repo, rev)
6309
6309
6310 if not ret and movemarkfrom:
6310 if not ret and movemarkfrom:
6311 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
6311 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
6312 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
6312 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
6313 elif brev in repo._bookmarks:
6313 elif brev in repo._bookmarks:
6314 bookmarks.setcurrent(repo, brev)
6314 bookmarks.setcurrent(repo, brev)
6315 ui.status(_("(activating bookmark %s)\n") % brev)
6315 ui.status(_("(activating bookmark %s)\n") % brev)
6316 elif brev:
6316 elif brev:
6317 if repo._bookmarkcurrent:
6317 if repo._bookmarkcurrent:
6318 ui.status(_("(leaving bookmark %s)\n") %
6318 ui.status(_("(leaving bookmark %s)\n") %
6319 repo._bookmarkcurrent)
6319 repo._bookmarkcurrent)
6320 bookmarks.unsetcurrent(repo)
6320 bookmarks.unsetcurrent(repo)
6321
6321
6322 return ret
6322 return ret
6323
6323
6324 @command('verify', [])
6324 @command('verify', [])
6325 def verify(ui, repo):
6325 def verify(ui, repo):
6326 """verify the integrity of the repository
6326 """verify the integrity of the repository
6327
6327
6328 Verify the integrity of the current repository.
6328 Verify the integrity of the current repository.
6329
6329
6330 This will perform an extensive check of the repository's
6330 This will perform an extensive check of the repository's
6331 integrity, validating the hashes and checksums of each entry in
6331 integrity, validating the hashes and checksums of each entry in
6332 the changelog, manifest, and tracked files, as well as the
6332 the changelog, manifest, and tracked files, as well as the
6333 integrity of their crosslinks and indices.
6333 integrity of their crosslinks and indices.
6334
6334
6335 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
6335 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
6336 for more information about recovery from corruption of the
6336 for more information about recovery from corruption of the
6337 repository.
6337 repository.
6338
6338
6339 Returns 0 on success, 1 if errors are encountered.
6339 Returns 0 on success, 1 if errors are encountered.
6340 """
6340 """
6341 return hg.verify(repo)
6341 return hg.verify(repo)
6342
6342
6343 @command('version', [], norepo=True)
6343 @command('version', [], norepo=True)
6344 def version_(ui):
6344 def version_(ui):
6345 """output version and copyright information"""
6345 """output version and copyright information"""
6346 ui.write(_("Mercurial Distributed SCM (version %s)\n")
6346 ui.write(_("Mercurial Distributed SCM (version %s)\n")
6347 % util.version())
6347 % util.version())
6348 ui.status(_(
6348 ui.status(_(
6349 "(see http://mercurial.selenic.com for more information)\n"
6349 "(see http://mercurial.selenic.com for more information)\n"
6350 "\nCopyright (C) 2005-2015 Matt Mackall and others\n"
6350 "\nCopyright (C) 2005-2015 Matt Mackall and others\n"
6351 "This is free software; see the source for copying conditions. "
6351 "This is free software; see the source for copying conditions. "
6352 "There is NO\nwarranty; "
6352 "There is NO\nwarranty; "
6353 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6353 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6354 ))
6354 ))
6355
6355
6356 ui.note(_("\nEnabled extensions:\n\n"))
6356 ui.note(_("\nEnabled extensions:\n\n"))
6357 if ui.verbose:
6357 if ui.verbose:
6358 # format names and versions into columns
6358 # format names and versions into columns
6359 names = []
6359 names = []
6360 vers = []
6360 vers = []
6361 for name, module in extensions.extensions():
6361 for name, module in extensions.extensions():
6362 names.append(name)
6362 names.append(name)
6363 vers.append(extensions.moduleversion(module))
6363 vers.append(extensions.moduleversion(module))
6364 if names:
6364 if names:
6365 maxnamelen = max(len(n) for n in names)
6365 maxnamelen = max(len(n) for n in names)
6366 for i, name in enumerate(names):
6366 for i, name in enumerate(names):
6367 ui.write(" %-*s %s\n" % (maxnamelen, name, vers[i]))
6367 ui.write(" %-*s %s\n" % (maxnamelen, name, vers[i]))
@@ -1,160 +1,164 b''
1 Subrepositories let you nest external repositories or projects into a
1 Subrepositories let you nest external repositories or projects into a
2 parent Mercurial repository, and make commands operate on them as a
2 parent Mercurial repository, and make commands operate on them as a
3 group.
3 group.
4
4
5 Mercurial currently supports Mercurial, Git, and Subversion
5 Mercurial currently supports Mercurial, Git, and Subversion
6 subrepositories.
6 subrepositories.
7
7
8 Subrepositories are made of three components:
8 Subrepositories are made of three components:
9
9
10 1. Nested repository checkouts. They can appear anywhere in the
10 1. Nested repository checkouts. They can appear anywhere in the
11 parent working directory.
11 parent working directory.
12
12
13 2. Nested repository references. They are defined in ``.hgsub``, which
13 2. Nested repository references. They are defined in ``.hgsub``, which
14 should be placed in the root of working directory, and
14 should be placed in the root of working directory, and
15 tell where the subrepository checkouts come from. Mercurial
15 tell where the subrepository checkouts come from. Mercurial
16 subrepositories are referenced like::
16 subrepositories are referenced like::
17
17
18 path/to/nested = https://example.com/nested/repo/path
18 path/to/nested = https://example.com/nested/repo/path
19
19
20 Git and Subversion subrepos are also supported::
20 Git and Subversion subrepos are also supported::
21
21
22 path/to/nested = [git]git://example.com/nested/repo/path
22 path/to/nested = [git]git://example.com/nested/repo/path
23 path/to/nested = [svn]https://example.com/nested/trunk/path
23 path/to/nested = [svn]https://example.com/nested/trunk/path
24
24
25 where ``path/to/nested`` is the checkout location relatively to the
25 where ``path/to/nested`` is the checkout location relatively to the
26 parent Mercurial root, and ``https://example.com/nested/repo/path``
26 parent Mercurial root, and ``https://example.com/nested/repo/path``
27 is the source repository path. The source can also reference a
27 is the source repository path. The source can also reference a
28 filesystem path.
28 filesystem path.
29
29
30 Note that ``.hgsub`` does not exist by default in Mercurial
30 Note that ``.hgsub`` does not exist by default in Mercurial
31 repositories, you have to create and add it to the parent
31 repositories, you have to create and add it to the parent
32 repository before using subrepositories.
32 repository before using subrepositories.
33
33
34 3. Nested repository states. They are defined in ``.hgsubstate``, which
34 3. Nested repository states. They are defined in ``.hgsubstate``, which
35 is placed in the root of working directory, and
35 is placed in the root of working directory, and
36 capture whatever information is required to restore the
36 capture whatever information is required to restore the
37 subrepositories to the state they were committed in a parent
37 subrepositories to the state they were committed in a parent
38 repository changeset. Mercurial automatically record the nested
38 repository changeset. Mercurial automatically record the nested
39 repositories states when committing in the parent repository.
39 repositories states when committing in the parent repository.
40
40
41 .. note::
41 .. note::
42
42
43 The ``.hgsubstate`` file should not be edited manually.
43 The ``.hgsubstate`` file should not be edited manually.
44
44
45
45
46 Adding a Subrepository
46 Adding a Subrepository
47 ======================
47 ======================
48
48
49 If ``.hgsub`` does not exist, create it and add it to the parent
49 If ``.hgsub`` does not exist, create it and add it to the parent
50 repository. Clone or checkout the external projects where you want it
50 repository. Clone or checkout the external projects where you want it
51 to live in the parent repository. Edit ``.hgsub`` and add the
51 to live in the parent repository. Edit ``.hgsub`` and add the
52 subrepository entry as described above. At this point, the
52 subrepository entry as described above. At this point, the
53 subrepository is tracked and the next commit will record its state in
53 subrepository is tracked and the next commit will record its state in
54 ``.hgsubstate`` and bind it to the committed changeset.
54 ``.hgsubstate`` and bind it to the committed changeset.
55
55
56 Synchronizing a Subrepository
56 Synchronizing a Subrepository
57 =============================
57 =============================
58
58
59 Subrepos do not automatically track the latest changeset of their
59 Subrepos do not automatically track the latest changeset of their
60 sources. Instead, they are updated to the changeset that corresponds
60 sources. Instead, they are updated to the changeset that corresponds
61 with the changeset checked out in the top-level changeset. This is so
61 with the changeset checked out in the top-level changeset. This is so
62 developers always get a consistent set of compatible code and
62 developers always get a consistent set of compatible code and
63 libraries when they update.
63 libraries when they update.
64
64
65 Thus, updating subrepos is a manual process. Simply check out target
65 Thus, updating subrepos is a manual process. Simply check out target
66 subrepo at the desired revision, test in the top-level repo, then
66 subrepo at the desired revision, test in the top-level repo, then
67 commit in the parent repository to record the new combination.
67 commit in the parent repository to record the new combination.
68
68
69 Deleting a Subrepository
69 Deleting a Subrepository
70 ========================
70 ========================
71
71
72 To remove a subrepository from the parent repository, delete its
72 To remove a subrepository from the parent repository, delete its
73 reference from ``.hgsub``, then remove its files.
73 reference from ``.hgsub``, then remove its files.
74
74
75 Interaction with Mercurial Commands
75 Interaction with Mercurial Commands
76 ===================================
76 ===================================
77
77
78 :add: add does not recurse in subrepos unless -S/--subrepos is
78 :add: add does not recurse in subrepos unless -S/--subrepos is
79 specified. However, if you specify the full path of a file in a
79 specified. However, if you specify the full path of a file in a
80 subrepo, it will be added even without -S/--subrepos specified.
80 subrepo, it will be added even without -S/--subrepos specified.
81 Subversion subrepositories are currently silently
81 Subversion subrepositories are currently silently
82 ignored.
82 ignored.
83
83
84 :addremove: addremove does not recurse into subrepos unless
84 :addremove: addremove does not recurse into subrepos unless
85 -S/--subrepos is specified. However, if you specify the full
85 -S/--subrepos is specified. However, if you specify the full
86 path of a directory in a subrepo, addremove will be performed on
86 path of a directory in a subrepo, addremove will be performed on
87 it even without -S/--subrepos being specified. Git and
87 it even without -S/--subrepos being specified. Git and
88 Subversion subrepositories will print a warning and continue.
88 Subversion subrepositories will print a warning and continue.
89
89
90 :archive: archive does not recurse in subrepositories unless
90 :archive: archive does not recurse in subrepositories unless
91 -S/--subrepos is specified.
91 -S/--subrepos is specified.
92
92
93 :cat: cat currently only handles exact file matches in subrepos.
93 :cat: cat currently only handles exact file matches in subrepos.
94 Subversion subrepositories are currently ignored.
94 Subversion subrepositories are currently ignored.
95
95
96 :commit: commit creates a consistent snapshot of the state of the
96 :commit: commit creates a consistent snapshot of the state of the
97 entire project and its subrepositories. If any subrepositories
97 entire project and its subrepositories. If any subrepositories
98 have been modified, Mercurial will abort. Mercurial can be made
98 have been modified, Mercurial will abort. Mercurial can be made
99 to instead commit all modified subrepositories by specifying
99 to instead commit all modified subrepositories by specifying
100 -S/--subrepos, or setting "ui.commitsubrepos=True" in a
100 -S/--subrepos, or setting "ui.commitsubrepos=True" in a
101 configuration file (see :hg:`help config`). After there are no
101 configuration file (see :hg:`help config`). After there are no
102 longer any modified subrepositories, it records their state and
102 longer any modified subrepositories, it records their state and
103 finally commits it in the parent repository. The --addremove
103 finally commits it in the parent repository. The --addremove
104 option also honors the -S/--subrepos option. However, Git and
104 option also honors the -S/--subrepos option. However, Git and
105 Subversion subrepositories will print a warning and abort.
105 Subversion subrepositories will print a warning and abort.
106
106
107 :diff: diff does not recurse in subrepos unless -S/--subrepos is
107 :diff: diff does not recurse in subrepos unless -S/--subrepos is
108 specified. Changes are displayed as usual, on the subrepositories
108 specified. Changes are displayed as usual, on the subrepositories
109 elements. Git subrepositories do not support --include/--exclude.
109 elements. Git subrepositories do not support --include/--exclude.
110 Subversion subrepositories are currently silently ignored.
110 Subversion subrepositories are currently silently ignored.
111
111
112 :files: files does not recurse into subrepos unless -S/--subrepos is
113 specified. Git and Subversion subrepositories are currently
114 silently ignored.
115
112 :forget: forget currently only handles exact file matches in subrepos.
116 :forget: forget currently only handles exact file matches in subrepos.
113 Git and Subversion subrepositories are currently silently ignored.
117 Git and Subversion subrepositories are currently silently ignored.
114
118
115 :incoming: incoming does not recurse in subrepos unless -S/--subrepos
119 :incoming: incoming does not recurse in subrepos unless -S/--subrepos
116 is specified. Git and Subversion subrepositories are currently
120 is specified. Git and Subversion subrepositories are currently
117 silently ignored.
121 silently ignored.
118
122
119 :outgoing: outgoing does not recurse in subrepos unless -S/--subrepos
123 :outgoing: outgoing does not recurse in subrepos unless -S/--subrepos
120 is specified. Git and Subversion subrepositories are currently
124 is specified. Git and Subversion subrepositories are currently
121 silently ignored.
125 silently ignored.
122
126
123 :pull: pull is not recursive since it is not clear what to pull prior
127 :pull: pull is not recursive since it is not clear what to pull prior
124 to running :hg:`update`. Listing and retrieving all
128 to running :hg:`update`. Listing and retrieving all
125 subrepositories changes referenced by the parent repository pulled
129 subrepositories changes referenced by the parent repository pulled
126 changesets is expensive at best, impossible in the Subversion
130 changesets is expensive at best, impossible in the Subversion
127 case.
131 case.
128
132
129 :push: Mercurial will automatically push all subrepositories first
133 :push: Mercurial will automatically push all subrepositories first
130 when the parent repository is being pushed. This ensures new
134 when the parent repository is being pushed. This ensures new
131 subrepository changes are available when referenced by top-level
135 subrepository changes are available when referenced by top-level
132 repositories. Push is a no-op for Subversion subrepositories.
136 repositories. Push is a no-op for Subversion subrepositories.
133
137
134 :status: status does not recurse into subrepositories unless
138 :status: status does not recurse into subrepositories unless
135 -S/--subrepos is specified. Subrepository changes are displayed as
139 -S/--subrepos is specified. Subrepository changes are displayed as
136 regular Mercurial changes on the subrepository
140 regular Mercurial changes on the subrepository
137 elements. Subversion subrepositories are currently silently
141 elements. Subversion subrepositories are currently silently
138 ignored.
142 ignored.
139
143
140 :remove: remove does not recurse into subrepositories unless
144 :remove: remove does not recurse into subrepositories unless
141 -S/--subrepos is specified. However, if you specify a file or
145 -S/--subrepos is specified. However, if you specify a file or
142 directory path in a subrepo, it will be removed even without
146 directory path in a subrepo, it will be removed even without
143 -S/--subrepos. Git and Subversion subrepositories are currently
147 -S/--subrepos. Git and Subversion subrepositories are currently
144 silently ignored.
148 silently ignored.
145
149
146 :update: update restores the subrepos in the state they were
150 :update: update restores the subrepos in the state they were
147 originally committed in target changeset. If the recorded
151 originally committed in target changeset. If the recorded
148 changeset is not available in the current subrepository, Mercurial
152 changeset is not available in the current subrepository, Mercurial
149 will pull it in first before updating. This means that updating
153 will pull it in first before updating. This means that updating
150 can require network access when using subrepositories.
154 can require network access when using subrepositories.
151
155
152 Remapping Subrepositories Sources
156 Remapping Subrepositories Sources
153 =================================
157 =================================
154
158
155 A subrepository source location may change during a project life,
159 A subrepository source location may change during a project life,
156 invalidating references stored in the parent repository history. To
160 invalidating references stored in the parent repository history. To
157 fix this, rewriting rules can be defined in parent repository ``hgrc``
161 fix this, rewriting rules can be defined in parent repository ``hgrc``
158 file or in Mercurial configuration. See the ``[subpaths]`` section in
162 file or in Mercurial configuration. See the ``[subpaths]`` section in
159 hgrc(5) for more details.
163 hgrc(5) for more details.
160
164
@@ -1,1776 +1,1791 b''
1 # subrepo.py - sub-repository handling for Mercurial
1 # subrepo.py - sub-repository handling for Mercurial
2 #
2 #
3 # Copyright 2009-2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2009-2010 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 import copy
8 import copy
9 import errno, os, re, shutil, posixpath, sys
9 import errno, os, re, shutil, posixpath, sys
10 import xml.dom.minidom
10 import xml.dom.minidom
11 import stat, subprocess, tarfile
11 import stat, subprocess, tarfile
12 from i18n import _
12 from i18n import _
13 import config, util, node, error, cmdutil, scmutil, match as matchmod
13 import config, util, node, error, cmdutil, scmutil, match as matchmod
14 import phases
14 import phases
15 import pathutil
15 import pathutil
16 import exchange
16 import exchange
17 hg = None
17 hg = None
18 propertycache = util.propertycache
18 propertycache = util.propertycache
19
19
20 nullstate = ('', '', 'empty')
20 nullstate = ('', '', 'empty')
21
21
22 def _expandedabspath(path):
22 def _expandedabspath(path):
23 '''
23 '''
24 get a path or url and if it is a path expand it and return an absolute path
24 get a path or url and if it is a path expand it and return an absolute path
25 '''
25 '''
26 expandedpath = util.urllocalpath(util.expandpath(path))
26 expandedpath = util.urllocalpath(util.expandpath(path))
27 u = util.url(expandedpath)
27 u = util.url(expandedpath)
28 if not u.scheme:
28 if not u.scheme:
29 path = util.normpath(os.path.abspath(u.path))
29 path = util.normpath(os.path.abspath(u.path))
30 return path
30 return path
31
31
32 def _getstorehashcachename(remotepath):
32 def _getstorehashcachename(remotepath):
33 '''get a unique filename for the store hash cache of a remote repository'''
33 '''get a unique filename for the store hash cache of a remote repository'''
34 return util.sha1(_expandedabspath(remotepath)).hexdigest()[0:12]
34 return util.sha1(_expandedabspath(remotepath)).hexdigest()[0:12]
35
35
36 class SubrepoAbort(error.Abort):
36 class SubrepoAbort(error.Abort):
37 """Exception class used to avoid handling a subrepo error more than once"""
37 """Exception class used to avoid handling a subrepo error more than once"""
38 def __init__(self, *args, **kw):
38 def __init__(self, *args, **kw):
39 error.Abort.__init__(self, *args, **kw)
39 error.Abort.__init__(self, *args, **kw)
40 self.subrepo = kw.get('subrepo')
40 self.subrepo = kw.get('subrepo')
41 self.cause = kw.get('cause')
41 self.cause = kw.get('cause')
42
42
43 def annotatesubrepoerror(func):
43 def annotatesubrepoerror(func):
44 def decoratedmethod(self, *args, **kargs):
44 def decoratedmethod(self, *args, **kargs):
45 try:
45 try:
46 res = func(self, *args, **kargs)
46 res = func(self, *args, **kargs)
47 except SubrepoAbort, ex:
47 except SubrepoAbort, ex:
48 # This exception has already been handled
48 # This exception has already been handled
49 raise ex
49 raise ex
50 except error.Abort, ex:
50 except error.Abort, ex:
51 subrepo = subrelpath(self)
51 subrepo = subrelpath(self)
52 errormsg = str(ex) + ' ' + _('(in subrepo %s)') % subrepo
52 errormsg = str(ex) + ' ' + _('(in subrepo %s)') % subrepo
53 # avoid handling this exception by raising a SubrepoAbort exception
53 # avoid handling this exception by raising a SubrepoAbort exception
54 raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo,
54 raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo,
55 cause=sys.exc_info())
55 cause=sys.exc_info())
56 return res
56 return res
57 return decoratedmethod
57 return decoratedmethod
58
58
59 def state(ctx, ui):
59 def state(ctx, ui):
60 """return a state dict, mapping subrepo paths configured in .hgsub
60 """return a state dict, mapping subrepo paths configured in .hgsub
61 to tuple: (source from .hgsub, revision from .hgsubstate, kind
61 to tuple: (source from .hgsub, revision from .hgsubstate, kind
62 (key in types dict))
62 (key in types dict))
63 """
63 """
64 p = config.config()
64 p = config.config()
65 def read(f, sections=None, remap=None):
65 def read(f, sections=None, remap=None):
66 if f in ctx:
66 if f in ctx:
67 try:
67 try:
68 data = ctx[f].data()
68 data = ctx[f].data()
69 except IOError, err:
69 except IOError, err:
70 if err.errno != errno.ENOENT:
70 if err.errno != errno.ENOENT:
71 raise
71 raise
72 # handle missing subrepo spec files as removed
72 # handle missing subrepo spec files as removed
73 ui.warn(_("warning: subrepo spec file %s not found\n") % f)
73 ui.warn(_("warning: subrepo spec file %s not found\n") % f)
74 return
74 return
75 p.parse(f, data, sections, remap, read)
75 p.parse(f, data, sections, remap, read)
76 else:
76 else:
77 raise util.Abort(_("subrepo spec file %s not found") % f)
77 raise util.Abort(_("subrepo spec file %s not found") % f)
78
78
79 if '.hgsub' in ctx:
79 if '.hgsub' in ctx:
80 read('.hgsub')
80 read('.hgsub')
81
81
82 for path, src in ui.configitems('subpaths'):
82 for path, src in ui.configitems('subpaths'):
83 p.set('subpaths', path, src, ui.configsource('subpaths', path))
83 p.set('subpaths', path, src, ui.configsource('subpaths', path))
84
84
85 rev = {}
85 rev = {}
86 if '.hgsubstate' in ctx:
86 if '.hgsubstate' in ctx:
87 try:
87 try:
88 for i, l in enumerate(ctx['.hgsubstate'].data().splitlines()):
88 for i, l in enumerate(ctx['.hgsubstate'].data().splitlines()):
89 l = l.lstrip()
89 l = l.lstrip()
90 if not l:
90 if not l:
91 continue
91 continue
92 try:
92 try:
93 revision, path = l.split(" ", 1)
93 revision, path = l.split(" ", 1)
94 except ValueError:
94 except ValueError:
95 raise util.Abort(_("invalid subrepository revision "
95 raise util.Abort(_("invalid subrepository revision "
96 "specifier in .hgsubstate line %d")
96 "specifier in .hgsubstate line %d")
97 % (i + 1))
97 % (i + 1))
98 rev[path] = revision
98 rev[path] = revision
99 except IOError, err:
99 except IOError, err:
100 if err.errno != errno.ENOENT:
100 if err.errno != errno.ENOENT:
101 raise
101 raise
102
102
103 def remap(src):
103 def remap(src):
104 for pattern, repl in p.items('subpaths'):
104 for pattern, repl in p.items('subpaths'):
105 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
105 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
106 # does a string decode.
106 # does a string decode.
107 repl = repl.encode('string-escape')
107 repl = repl.encode('string-escape')
108 # However, we still want to allow back references to go
108 # However, we still want to allow back references to go
109 # through unharmed, so we turn r'\\1' into r'\1'. Again,
109 # through unharmed, so we turn r'\\1' into r'\1'. Again,
110 # extra escapes are needed because re.sub string decodes.
110 # extra escapes are needed because re.sub string decodes.
111 repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
111 repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
112 try:
112 try:
113 src = re.sub(pattern, repl, src, 1)
113 src = re.sub(pattern, repl, src, 1)
114 except re.error, e:
114 except re.error, e:
115 raise util.Abort(_("bad subrepository pattern in %s: %s")
115 raise util.Abort(_("bad subrepository pattern in %s: %s")
116 % (p.source('subpaths', pattern), e))
116 % (p.source('subpaths', pattern), e))
117 return src
117 return src
118
118
119 state = {}
119 state = {}
120 for path, src in p[''].items():
120 for path, src in p[''].items():
121 kind = 'hg'
121 kind = 'hg'
122 if src.startswith('['):
122 if src.startswith('['):
123 if ']' not in src:
123 if ']' not in src:
124 raise util.Abort(_('missing ] in subrepo source'))
124 raise util.Abort(_('missing ] in subrepo source'))
125 kind, src = src.split(']', 1)
125 kind, src = src.split(']', 1)
126 kind = kind[1:]
126 kind = kind[1:]
127 src = src.lstrip() # strip any extra whitespace after ']'
127 src = src.lstrip() # strip any extra whitespace after ']'
128
128
129 if not util.url(src).isabs():
129 if not util.url(src).isabs():
130 parent = _abssource(ctx.repo(), abort=False)
130 parent = _abssource(ctx.repo(), abort=False)
131 if parent:
131 if parent:
132 parent = util.url(parent)
132 parent = util.url(parent)
133 parent.path = posixpath.join(parent.path or '', src)
133 parent.path = posixpath.join(parent.path or '', src)
134 parent.path = posixpath.normpath(parent.path)
134 parent.path = posixpath.normpath(parent.path)
135 joined = str(parent)
135 joined = str(parent)
136 # Remap the full joined path and use it if it changes,
136 # Remap the full joined path and use it if it changes,
137 # else remap the original source.
137 # else remap the original source.
138 remapped = remap(joined)
138 remapped = remap(joined)
139 if remapped == joined:
139 if remapped == joined:
140 src = remap(src)
140 src = remap(src)
141 else:
141 else:
142 src = remapped
142 src = remapped
143
143
144 src = remap(src)
144 src = remap(src)
145 state[util.pconvert(path)] = (src.strip(), rev.get(path, ''), kind)
145 state[util.pconvert(path)] = (src.strip(), rev.get(path, ''), kind)
146
146
147 return state
147 return state
148
148
149 def writestate(repo, state):
149 def writestate(repo, state):
150 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
150 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
151 lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)]
151 lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)]
152 repo.wwrite('.hgsubstate', ''.join(lines), '')
152 repo.wwrite('.hgsubstate', ''.join(lines), '')
153
153
154 def submerge(repo, wctx, mctx, actx, overwrite):
154 def submerge(repo, wctx, mctx, actx, overwrite):
155 """delegated from merge.applyupdates: merging of .hgsubstate file
155 """delegated from merge.applyupdates: merging of .hgsubstate file
156 in working context, merging context and ancestor context"""
156 in working context, merging context and ancestor context"""
157 if mctx == actx: # backwards?
157 if mctx == actx: # backwards?
158 actx = wctx.p1()
158 actx = wctx.p1()
159 s1 = wctx.substate
159 s1 = wctx.substate
160 s2 = mctx.substate
160 s2 = mctx.substate
161 sa = actx.substate
161 sa = actx.substate
162 sm = {}
162 sm = {}
163
163
164 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
164 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
165
165
166 def debug(s, msg, r=""):
166 def debug(s, msg, r=""):
167 if r:
167 if r:
168 r = "%s:%s:%s" % r
168 r = "%s:%s:%s" % r
169 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
169 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
170
170
171 for s, l in sorted(s1.iteritems()):
171 for s, l in sorted(s1.iteritems()):
172 a = sa.get(s, nullstate)
172 a = sa.get(s, nullstate)
173 ld = l # local state with possible dirty flag for compares
173 ld = l # local state with possible dirty flag for compares
174 if wctx.sub(s).dirty():
174 if wctx.sub(s).dirty():
175 ld = (l[0], l[1] + "+")
175 ld = (l[0], l[1] + "+")
176 if wctx == actx: # overwrite
176 if wctx == actx: # overwrite
177 a = ld
177 a = ld
178
178
179 if s in s2:
179 if s in s2:
180 r = s2[s]
180 r = s2[s]
181 if ld == r or r == a: # no change or local is newer
181 if ld == r or r == a: # no change or local is newer
182 sm[s] = l
182 sm[s] = l
183 continue
183 continue
184 elif ld == a: # other side changed
184 elif ld == a: # other side changed
185 debug(s, "other changed, get", r)
185 debug(s, "other changed, get", r)
186 wctx.sub(s).get(r, overwrite)
186 wctx.sub(s).get(r, overwrite)
187 sm[s] = r
187 sm[s] = r
188 elif ld[0] != r[0]: # sources differ
188 elif ld[0] != r[0]: # sources differ
189 if repo.ui.promptchoice(
189 if repo.ui.promptchoice(
190 _(' subrepository sources for %s differ\n'
190 _(' subrepository sources for %s differ\n'
191 'use (l)ocal source (%s) or (r)emote source (%s)?'
191 'use (l)ocal source (%s) or (r)emote source (%s)?'
192 '$$ &Local $$ &Remote') % (s, l[0], r[0]), 0):
192 '$$ &Local $$ &Remote') % (s, l[0], r[0]), 0):
193 debug(s, "prompt changed, get", r)
193 debug(s, "prompt changed, get", r)
194 wctx.sub(s).get(r, overwrite)
194 wctx.sub(s).get(r, overwrite)
195 sm[s] = r
195 sm[s] = r
196 elif ld[1] == a[1]: # local side is unchanged
196 elif ld[1] == a[1]: # local side is unchanged
197 debug(s, "other side changed, get", r)
197 debug(s, "other side changed, get", r)
198 wctx.sub(s).get(r, overwrite)
198 wctx.sub(s).get(r, overwrite)
199 sm[s] = r
199 sm[s] = r
200 else:
200 else:
201 debug(s, "both sides changed")
201 debug(s, "both sides changed")
202 srepo = wctx.sub(s)
202 srepo = wctx.sub(s)
203 option = repo.ui.promptchoice(
203 option = repo.ui.promptchoice(
204 _(' subrepository %s diverged (local revision: %s, '
204 _(' subrepository %s diverged (local revision: %s, '
205 'remote revision: %s)\n'
205 'remote revision: %s)\n'
206 '(M)erge, keep (l)ocal or keep (r)emote?'
206 '(M)erge, keep (l)ocal or keep (r)emote?'
207 '$$ &Merge $$ &Local $$ &Remote')
207 '$$ &Merge $$ &Local $$ &Remote')
208 % (s, srepo.shortid(l[1]), srepo.shortid(r[1])), 0)
208 % (s, srepo.shortid(l[1]), srepo.shortid(r[1])), 0)
209 if option == 0:
209 if option == 0:
210 wctx.sub(s).merge(r)
210 wctx.sub(s).merge(r)
211 sm[s] = l
211 sm[s] = l
212 debug(s, "merge with", r)
212 debug(s, "merge with", r)
213 elif option == 1:
213 elif option == 1:
214 sm[s] = l
214 sm[s] = l
215 debug(s, "keep local subrepo revision", l)
215 debug(s, "keep local subrepo revision", l)
216 else:
216 else:
217 wctx.sub(s).get(r, overwrite)
217 wctx.sub(s).get(r, overwrite)
218 sm[s] = r
218 sm[s] = r
219 debug(s, "get remote subrepo revision", r)
219 debug(s, "get remote subrepo revision", r)
220 elif ld == a: # remote removed, local unchanged
220 elif ld == a: # remote removed, local unchanged
221 debug(s, "remote removed, remove")
221 debug(s, "remote removed, remove")
222 wctx.sub(s).remove()
222 wctx.sub(s).remove()
223 elif a == nullstate: # not present in remote or ancestor
223 elif a == nullstate: # not present in remote or ancestor
224 debug(s, "local added, keep")
224 debug(s, "local added, keep")
225 sm[s] = l
225 sm[s] = l
226 continue
226 continue
227 else:
227 else:
228 if repo.ui.promptchoice(
228 if repo.ui.promptchoice(
229 _(' local changed subrepository %s which remote removed\n'
229 _(' local changed subrepository %s which remote removed\n'
230 'use (c)hanged version or (d)elete?'
230 'use (c)hanged version or (d)elete?'
231 '$$ &Changed $$ &Delete') % s, 0):
231 '$$ &Changed $$ &Delete') % s, 0):
232 debug(s, "prompt remove")
232 debug(s, "prompt remove")
233 wctx.sub(s).remove()
233 wctx.sub(s).remove()
234
234
235 for s, r in sorted(s2.items()):
235 for s, r in sorted(s2.items()):
236 if s in s1:
236 if s in s1:
237 continue
237 continue
238 elif s not in sa:
238 elif s not in sa:
239 debug(s, "remote added, get", r)
239 debug(s, "remote added, get", r)
240 mctx.sub(s).get(r)
240 mctx.sub(s).get(r)
241 sm[s] = r
241 sm[s] = r
242 elif r != sa[s]:
242 elif r != sa[s]:
243 if repo.ui.promptchoice(
243 if repo.ui.promptchoice(
244 _(' remote changed subrepository %s which local removed\n'
244 _(' remote changed subrepository %s which local removed\n'
245 'use (c)hanged version or (d)elete?'
245 'use (c)hanged version or (d)elete?'
246 '$$ &Changed $$ &Delete') % s, 0) == 0:
246 '$$ &Changed $$ &Delete') % s, 0) == 0:
247 debug(s, "prompt recreate", r)
247 debug(s, "prompt recreate", r)
248 mctx.sub(s).get(r)
248 mctx.sub(s).get(r)
249 sm[s] = r
249 sm[s] = r
250
250
251 # record merged .hgsubstate
251 # record merged .hgsubstate
252 writestate(repo, sm)
252 writestate(repo, sm)
253 return sm
253 return sm
254
254
255 def _updateprompt(ui, sub, dirty, local, remote):
255 def _updateprompt(ui, sub, dirty, local, remote):
256 if dirty:
256 if dirty:
257 msg = (_(' subrepository sources for %s differ\n'
257 msg = (_(' subrepository sources for %s differ\n'
258 'use (l)ocal source (%s) or (r)emote source (%s)?'
258 'use (l)ocal source (%s) or (r)emote source (%s)?'
259 '$$ &Local $$ &Remote')
259 '$$ &Local $$ &Remote')
260 % (subrelpath(sub), local, remote))
260 % (subrelpath(sub), local, remote))
261 else:
261 else:
262 msg = (_(' subrepository sources for %s differ (in checked out '
262 msg = (_(' subrepository sources for %s differ (in checked out '
263 'version)\n'
263 'version)\n'
264 'use (l)ocal source (%s) or (r)emote source (%s)?'
264 'use (l)ocal source (%s) or (r)emote source (%s)?'
265 '$$ &Local $$ &Remote')
265 '$$ &Local $$ &Remote')
266 % (subrelpath(sub), local, remote))
266 % (subrelpath(sub), local, remote))
267 return ui.promptchoice(msg, 0)
267 return ui.promptchoice(msg, 0)
268
268
269 def reporelpath(repo):
269 def reporelpath(repo):
270 """return path to this (sub)repo as seen from outermost repo"""
270 """return path to this (sub)repo as seen from outermost repo"""
271 parent = repo
271 parent = repo
272 while util.safehasattr(parent, '_subparent'):
272 while util.safehasattr(parent, '_subparent'):
273 parent = parent._subparent
273 parent = parent._subparent
274 return repo.root[len(pathutil.normasprefix(parent.root)):]
274 return repo.root[len(pathutil.normasprefix(parent.root)):]
275
275
276 def subrelpath(sub):
276 def subrelpath(sub):
277 """return path to this subrepo as seen from outermost repo"""
277 """return path to this subrepo as seen from outermost repo"""
278 if util.safehasattr(sub, '_relpath'):
278 if util.safehasattr(sub, '_relpath'):
279 return sub._relpath
279 return sub._relpath
280 if not util.safehasattr(sub, '_repo'):
280 if not util.safehasattr(sub, '_repo'):
281 return sub._path
281 return sub._path
282 return reporelpath(sub._repo)
282 return reporelpath(sub._repo)
283
283
284 def _abssource(repo, push=False, abort=True):
284 def _abssource(repo, push=False, abort=True):
285 """return pull/push path of repo - either based on parent repo .hgsub info
285 """return pull/push path of repo - either based on parent repo .hgsub info
286 or on the top repo config. Abort or return None if no source found."""
286 or on the top repo config. Abort or return None if no source found."""
287 if util.safehasattr(repo, '_subparent'):
287 if util.safehasattr(repo, '_subparent'):
288 source = util.url(repo._subsource)
288 source = util.url(repo._subsource)
289 if source.isabs():
289 if source.isabs():
290 return str(source)
290 return str(source)
291 source.path = posixpath.normpath(source.path)
291 source.path = posixpath.normpath(source.path)
292 parent = _abssource(repo._subparent, push, abort=False)
292 parent = _abssource(repo._subparent, push, abort=False)
293 if parent:
293 if parent:
294 parent = util.url(util.pconvert(parent))
294 parent = util.url(util.pconvert(parent))
295 parent.path = posixpath.join(parent.path or '', source.path)
295 parent.path = posixpath.join(parent.path or '', source.path)
296 parent.path = posixpath.normpath(parent.path)
296 parent.path = posixpath.normpath(parent.path)
297 return str(parent)
297 return str(parent)
298 else: # recursion reached top repo
298 else: # recursion reached top repo
299 if util.safehasattr(repo, '_subtoppath'):
299 if util.safehasattr(repo, '_subtoppath'):
300 return repo._subtoppath
300 return repo._subtoppath
301 if push and repo.ui.config('paths', 'default-push'):
301 if push and repo.ui.config('paths', 'default-push'):
302 return repo.ui.config('paths', 'default-push')
302 return repo.ui.config('paths', 'default-push')
303 if repo.ui.config('paths', 'default'):
303 if repo.ui.config('paths', 'default'):
304 return repo.ui.config('paths', 'default')
304 return repo.ui.config('paths', 'default')
305 if repo.shared():
305 if repo.shared():
306 # chop off the .hg component to get the default path form
306 # chop off the .hg component to get the default path form
307 return os.path.dirname(repo.sharedpath)
307 return os.path.dirname(repo.sharedpath)
308 if abort:
308 if abort:
309 raise util.Abort(_("default path for subrepository not found"))
309 raise util.Abort(_("default path for subrepository not found"))
310
310
311 def _sanitize(ui, path, ignore):
311 def _sanitize(ui, path, ignore):
312 for dirname, dirs, names in os.walk(path):
312 for dirname, dirs, names in os.walk(path):
313 for i, d in enumerate(dirs):
313 for i, d in enumerate(dirs):
314 if d.lower() == ignore:
314 if d.lower() == ignore:
315 del dirs[i]
315 del dirs[i]
316 break
316 break
317 if os.path.basename(dirname).lower() != '.hg':
317 if os.path.basename(dirname).lower() != '.hg':
318 continue
318 continue
319 for f in names:
319 for f in names:
320 if f.lower() == 'hgrc':
320 if f.lower() == 'hgrc':
321 ui.warn(_("warning: removing potentially hostile 'hgrc' "
321 ui.warn(_("warning: removing potentially hostile 'hgrc' "
322 "in '%s'\n") % dirname)
322 "in '%s'\n") % dirname)
323 os.unlink(os.path.join(dirname, f))
323 os.unlink(os.path.join(dirname, f))
324
324
325 def subrepo(ctx, path):
325 def subrepo(ctx, path):
326 """return instance of the right subrepo class for subrepo in path"""
326 """return instance of the right subrepo class for subrepo in path"""
327 # subrepo inherently violates our import layering rules
327 # subrepo inherently violates our import layering rules
328 # because it wants to make repo objects from deep inside the stack
328 # because it wants to make repo objects from deep inside the stack
329 # so we manually delay the circular imports to not break
329 # so we manually delay the circular imports to not break
330 # scripts that don't use our demand-loading
330 # scripts that don't use our demand-loading
331 global hg
331 global hg
332 import hg as h
332 import hg as h
333 hg = h
333 hg = h
334
334
335 pathutil.pathauditor(ctx.repo().root)(path)
335 pathutil.pathauditor(ctx.repo().root)(path)
336 state = ctx.substate[path]
336 state = ctx.substate[path]
337 if state[2] not in types:
337 if state[2] not in types:
338 raise util.Abort(_('unknown subrepo type %s') % state[2])
338 raise util.Abort(_('unknown subrepo type %s') % state[2])
339 return types[state[2]](ctx, path, state[:2])
339 return types[state[2]](ctx, path, state[:2])
340
340
341 def newcommitphase(ui, ctx):
341 def newcommitphase(ui, ctx):
342 commitphase = phases.newcommitphase(ui)
342 commitphase = phases.newcommitphase(ui)
343 substate = getattr(ctx, "substate", None)
343 substate = getattr(ctx, "substate", None)
344 if not substate:
344 if not substate:
345 return commitphase
345 return commitphase
346 check = ui.config('phases', 'checksubrepos', 'follow')
346 check = ui.config('phases', 'checksubrepos', 'follow')
347 if check not in ('ignore', 'follow', 'abort'):
347 if check not in ('ignore', 'follow', 'abort'):
348 raise util.Abort(_('invalid phases.checksubrepos configuration: %s')
348 raise util.Abort(_('invalid phases.checksubrepos configuration: %s')
349 % (check))
349 % (check))
350 if check == 'ignore':
350 if check == 'ignore':
351 return commitphase
351 return commitphase
352 maxphase = phases.public
352 maxphase = phases.public
353 maxsub = None
353 maxsub = None
354 for s in sorted(substate):
354 for s in sorted(substate):
355 sub = ctx.sub(s)
355 sub = ctx.sub(s)
356 subphase = sub.phase(substate[s][1])
356 subphase = sub.phase(substate[s][1])
357 if maxphase < subphase:
357 if maxphase < subphase:
358 maxphase = subphase
358 maxphase = subphase
359 maxsub = s
359 maxsub = s
360 if commitphase < maxphase:
360 if commitphase < maxphase:
361 if check == 'abort':
361 if check == 'abort':
362 raise util.Abort(_("can't commit in %s phase"
362 raise util.Abort(_("can't commit in %s phase"
363 " conflicting %s from subrepository %s") %
363 " conflicting %s from subrepository %s") %
364 (phases.phasenames[commitphase],
364 (phases.phasenames[commitphase],
365 phases.phasenames[maxphase], maxsub))
365 phases.phasenames[maxphase], maxsub))
366 ui.warn(_("warning: changes are committed in"
366 ui.warn(_("warning: changes are committed in"
367 " %s phase from subrepository %s\n") %
367 " %s phase from subrepository %s\n") %
368 (phases.phasenames[maxphase], maxsub))
368 (phases.phasenames[maxphase], maxsub))
369 return maxphase
369 return maxphase
370 return commitphase
370 return commitphase
371
371
372 # subrepo classes need to implement the following abstract class:
372 # subrepo classes need to implement the following abstract class:
373
373
374 class abstractsubrepo(object):
374 class abstractsubrepo(object):
375
375
376 def __init__(self, ui):
376 def __init__(self, ui):
377 self.ui = ui
377 self.ui = ui
378
378
379 def storeclean(self, path):
379 def storeclean(self, path):
380 """
380 """
381 returns true if the repository has not changed since it was last
381 returns true if the repository has not changed since it was last
382 cloned from or pushed to a given repository.
382 cloned from or pushed to a given repository.
383 """
383 """
384 return False
384 return False
385
385
386 def dirty(self, ignoreupdate=False):
386 def dirty(self, ignoreupdate=False):
387 """returns true if the dirstate of the subrepo is dirty or does not
387 """returns true if the dirstate of the subrepo is dirty or does not
388 match current stored state. If ignoreupdate is true, only check
388 match current stored state. If ignoreupdate is true, only check
389 whether the subrepo has uncommitted changes in its dirstate.
389 whether the subrepo has uncommitted changes in its dirstate.
390 """
390 """
391 raise NotImplementedError
391 raise NotImplementedError
392
392
393 def basestate(self):
393 def basestate(self):
394 """current working directory base state, disregarding .hgsubstate
394 """current working directory base state, disregarding .hgsubstate
395 state and working directory modifications"""
395 state and working directory modifications"""
396 raise NotImplementedError
396 raise NotImplementedError
397
397
398 def checknested(self, path):
398 def checknested(self, path):
399 """check if path is a subrepository within this repository"""
399 """check if path is a subrepository within this repository"""
400 return False
400 return False
401
401
402 def commit(self, text, user, date):
402 def commit(self, text, user, date):
403 """commit the current changes to the subrepo with the given
403 """commit the current changes to the subrepo with the given
404 log message. Use given user and date if possible. Return the
404 log message. Use given user and date if possible. Return the
405 new state of the subrepo.
405 new state of the subrepo.
406 """
406 """
407 raise NotImplementedError
407 raise NotImplementedError
408
408
409 def phase(self, state):
409 def phase(self, state):
410 """returns phase of specified state in the subrepository.
410 """returns phase of specified state in the subrepository.
411 """
411 """
412 return phases.public
412 return phases.public
413
413
414 def remove(self):
414 def remove(self):
415 """remove the subrepo
415 """remove the subrepo
416
416
417 (should verify the dirstate is not dirty first)
417 (should verify the dirstate is not dirty first)
418 """
418 """
419 raise NotImplementedError
419 raise NotImplementedError
420
420
421 def get(self, state, overwrite=False):
421 def get(self, state, overwrite=False):
422 """run whatever commands are needed to put the subrepo into
422 """run whatever commands are needed to put the subrepo into
423 this state
423 this state
424 """
424 """
425 raise NotImplementedError
425 raise NotImplementedError
426
426
427 def merge(self, state):
427 def merge(self, state):
428 """merge currently-saved state with the new state."""
428 """merge currently-saved state with the new state."""
429 raise NotImplementedError
429 raise NotImplementedError
430
430
431 def push(self, opts):
431 def push(self, opts):
432 """perform whatever action is analogous to 'hg push'
432 """perform whatever action is analogous to 'hg push'
433
433
434 This may be a no-op on some systems.
434 This may be a no-op on some systems.
435 """
435 """
436 raise NotImplementedError
436 raise NotImplementedError
437
437
438 def add(self, ui, match, prefix, explicitonly, **opts):
438 def add(self, ui, match, prefix, explicitonly, **opts):
439 return []
439 return []
440
440
441 def addremove(self, matcher, prefix, opts, dry_run, similarity):
441 def addremove(self, matcher, prefix, opts, dry_run, similarity):
442 self.ui.warn("%s: %s" % (prefix, _("addremove is not supported")))
442 self.ui.warn("%s: %s" % (prefix, _("addremove is not supported")))
443 return 1
443 return 1
444
444
445 def cat(self, match, prefix, **opts):
445 def cat(self, match, prefix, **opts):
446 return 1
446 return 1
447
447
448 def status(self, rev2, **opts):
448 def status(self, rev2, **opts):
449 return scmutil.status([], [], [], [], [], [], [])
449 return scmutil.status([], [], [], [], [], [], [])
450
450
451 def diff(self, ui, diffopts, node2, match, prefix, **opts):
451 def diff(self, ui, diffopts, node2, match, prefix, **opts):
452 pass
452 pass
453
453
454 def outgoing(self, ui, dest, opts):
454 def outgoing(self, ui, dest, opts):
455 return 1
455 return 1
456
456
457 def incoming(self, ui, source, opts):
457 def incoming(self, ui, source, opts):
458 return 1
458 return 1
459
459
460 def files(self):
460 def files(self):
461 """return filename iterator"""
461 """return filename iterator"""
462 raise NotImplementedError
462 raise NotImplementedError
463
463
464 def filedata(self, name):
464 def filedata(self, name):
465 """return file data"""
465 """return file data"""
466 raise NotImplementedError
466 raise NotImplementedError
467
467
468 def fileflags(self, name):
468 def fileflags(self, name):
469 """return file flags"""
469 """return file flags"""
470 return ''
470 return ''
471
471
472 def printfiles(self, ui, m, fm, fmt):
473 """handle the files command for this subrepo"""
474 return 1
475
472 def archive(self, archiver, prefix, match=None):
476 def archive(self, archiver, prefix, match=None):
473 if match is not None:
477 if match is not None:
474 files = [f for f in self.files() if match(f)]
478 files = [f for f in self.files() if match(f)]
475 else:
479 else:
476 files = self.files()
480 files = self.files()
477 total = len(files)
481 total = len(files)
478 relpath = subrelpath(self)
482 relpath = subrelpath(self)
479 self.ui.progress(_('archiving (%s)') % relpath, 0,
483 self.ui.progress(_('archiving (%s)') % relpath, 0,
480 unit=_('files'), total=total)
484 unit=_('files'), total=total)
481 for i, name in enumerate(files):
485 for i, name in enumerate(files):
482 flags = self.fileflags(name)
486 flags = self.fileflags(name)
483 mode = 'x' in flags and 0755 or 0644
487 mode = 'x' in flags and 0755 or 0644
484 symlink = 'l' in flags
488 symlink = 'l' in flags
485 archiver.addfile(os.path.join(prefix, self._path, name),
489 archiver.addfile(os.path.join(prefix, self._path, name),
486 mode, symlink, self.filedata(name))
490 mode, symlink, self.filedata(name))
487 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
491 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
488 unit=_('files'), total=total)
492 unit=_('files'), total=total)
489 self.ui.progress(_('archiving (%s)') % relpath, None)
493 self.ui.progress(_('archiving (%s)') % relpath, None)
490 return total
494 return total
491
495
492 def walk(self, match):
496 def walk(self, match):
493 '''
497 '''
494 walk recursively through the directory tree, finding all files
498 walk recursively through the directory tree, finding all files
495 matched by the match function
499 matched by the match function
496 '''
500 '''
497 pass
501 pass
498
502
499 def forget(self, match, prefix):
503 def forget(self, match, prefix):
500 return ([], [])
504 return ([], [])
501
505
502 def removefiles(self, matcher, prefix, after, force, subrepos):
506 def removefiles(self, matcher, prefix, after, force, subrepos):
503 """remove the matched files from the subrepository and the filesystem,
507 """remove the matched files from the subrepository and the filesystem,
504 possibly by force and/or after the file has been removed from the
508 possibly by force and/or after the file has been removed from the
505 filesystem. Return 0 on success, 1 on any warning.
509 filesystem. Return 0 on success, 1 on any warning.
506 """
510 """
507 return 1
511 return 1
508
512
509 def revert(self, substate, *pats, **opts):
513 def revert(self, substate, *pats, **opts):
510 self.ui.warn('%s: reverting %s subrepos is unsupported\n' \
514 self.ui.warn('%s: reverting %s subrepos is unsupported\n' \
511 % (substate[0], substate[2]))
515 % (substate[0], substate[2]))
512 return []
516 return []
513
517
514 def shortid(self, revid):
518 def shortid(self, revid):
515 return revid
519 return revid
516
520
517 class hgsubrepo(abstractsubrepo):
521 class hgsubrepo(abstractsubrepo):
518 def __init__(self, ctx, path, state):
522 def __init__(self, ctx, path, state):
519 super(hgsubrepo, self).__init__(ctx.repo().ui)
523 super(hgsubrepo, self).__init__(ctx.repo().ui)
520 self._path = path
524 self._path = path
521 self._state = state
525 self._state = state
522 self._ctx = ctx
526 self._ctx = ctx
523 r = ctx.repo()
527 r = ctx.repo()
524 root = r.wjoin(path)
528 root = r.wjoin(path)
525 create = not r.wvfs.exists('%s/.hg' % path)
529 create = not r.wvfs.exists('%s/.hg' % path)
526 self._repo = hg.repository(r.baseui, root, create=create)
530 self._repo = hg.repository(r.baseui, root, create=create)
527 self.ui = self._repo.ui
531 self.ui = self._repo.ui
528 for s, k in [('ui', 'commitsubrepos')]:
532 for s, k in [('ui', 'commitsubrepos')]:
529 v = r.ui.config(s, k)
533 v = r.ui.config(s, k)
530 if v:
534 if v:
531 self.ui.setconfig(s, k, v, 'subrepo')
535 self.ui.setconfig(s, k, v, 'subrepo')
532 self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
536 self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
533 self._initrepo(r, state[0], create)
537 self._initrepo(r, state[0], create)
534
538
535 def storeclean(self, path):
539 def storeclean(self, path):
536 lock = self._repo.lock()
540 lock = self._repo.lock()
537 try:
541 try:
538 return self._storeclean(path)
542 return self._storeclean(path)
539 finally:
543 finally:
540 lock.release()
544 lock.release()
541
545
542 def _storeclean(self, path):
546 def _storeclean(self, path):
543 clean = True
547 clean = True
544 itercache = self._calcstorehash(path)
548 itercache = self._calcstorehash(path)
545 try:
549 try:
546 for filehash in self._readstorehashcache(path):
550 for filehash in self._readstorehashcache(path):
547 if filehash != itercache.next():
551 if filehash != itercache.next():
548 clean = False
552 clean = False
549 break
553 break
550 except StopIteration:
554 except StopIteration:
551 # the cached and current pull states have a different size
555 # the cached and current pull states have a different size
552 clean = False
556 clean = False
553 if clean:
557 if clean:
554 try:
558 try:
555 itercache.next()
559 itercache.next()
556 # the cached and current pull states have a different size
560 # the cached and current pull states have a different size
557 clean = False
561 clean = False
558 except StopIteration:
562 except StopIteration:
559 pass
563 pass
560 return clean
564 return clean
561
565
562 def _calcstorehash(self, remotepath):
566 def _calcstorehash(self, remotepath):
563 '''calculate a unique "store hash"
567 '''calculate a unique "store hash"
564
568
565 This method is used to to detect when there are changes that may
569 This method is used to to detect when there are changes that may
566 require a push to a given remote path.'''
570 require a push to a given remote path.'''
567 # sort the files that will be hashed in increasing (likely) file size
571 # sort the files that will be hashed in increasing (likely) file size
568 filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i')
572 filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i')
569 yield '# %s\n' % _expandedabspath(remotepath)
573 yield '# %s\n' % _expandedabspath(remotepath)
570 vfs = self._repo.vfs
574 vfs = self._repo.vfs
571 for relname in filelist:
575 for relname in filelist:
572 filehash = util.sha1(vfs.tryread(relname)).hexdigest()
576 filehash = util.sha1(vfs.tryread(relname)).hexdigest()
573 yield '%s = %s\n' % (relname, filehash)
577 yield '%s = %s\n' % (relname, filehash)
574
578
575 @propertycache
579 @propertycache
576 def _cachestorehashvfs(self):
580 def _cachestorehashvfs(self):
577 return scmutil.vfs(self._repo.join('cache/storehash'))
581 return scmutil.vfs(self._repo.join('cache/storehash'))
578
582
579 def _readstorehashcache(self, remotepath):
583 def _readstorehashcache(self, remotepath):
580 '''read the store hash cache for a given remote repository'''
584 '''read the store hash cache for a given remote repository'''
581 cachefile = _getstorehashcachename(remotepath)
585 cachefile = _getstorehashcachename(remotepath)
582 return self._cachestorehashvfs.tryreadlines(cachefile, 'r')
586 return self._cachestorehashvfs.tryreadlines(cachefile, 'r')
583
587
584 def _cachestorehash(self, remotepath):
588 def _cachestorehash(self, remotepath):
585 '''cache the current store hash
589 '''cache the current store hash
586
590
587 Each remote repo requires its own store hash cache, because a subrepo
591 Each remote repo requires its own store hash cache, because a subrepo
588 store may be "clean" versus a given remote repo, but not versus another
592 store may be "clean" versus a given remote repo, but not versus another
589 '''
593 '''
590 cachefile = _getstorehashcachename(remotepath)
594 cachefile = _getstorehashcachename(remotepath)
591 lock = self._repo.lock()
595 lock = self._repo.lock()
592 try:
596 try:
593 storehash = list(self._calcstorehash(remotepath))
597 storehash = list(self._calcstorehash(remotepath))
594 vfs = self._cachestorehashvfs
598 vfs = self._cachestorehashvfs
595 vfs.writelines(cachefile, storehash, mode='w', notindexed=True)
599 vfs.writelines(cachefile, storehash, mode='w', notindexed=True)
596 finally:
600 finally:
597 lock.release()
601 lock.release()
598
602
599 @annotatesubrepoerror
603 @annotatesubrepoerror
600 def _initrepo(self, parentrepo, source, create):
604 def _initrepo(self, parentrepo, source, create):
601 self._repo._subparent = parentrepo
605 self._repo._subparent = parentrepo
602 self._repo._subsource = source
606 self._repo._subsource = source
603
607
604 if create:
608 if create:
605 lines = ['[paths]\n']
609 lines = ['[paths]\n']
606
610
607 def addpathconfig(key, value):
611 def addpathconfig(key, value):
608 if value:
612 if value:
609 lines.append('%s = %s\n' % (key, value))
613 lines.append('%s = %s\n' % (key, value))
610 self.ui.setconfig('paths', key, value, 'subrepo')
614 self.ui.setconfig('paths', key, value, 'subrepo')
611
615
612 defpath = _abssource(self._repo, abort=False)
616 defpath = _abssource(self._repo, abort=False)
613 defpushpath = _abssource(self._repo, True, abort=False)
617 defpushpath = _abssource(self._repo, True, abort=False)
614 addpathconfig('default', defpath)
618 addpathconfig('default', defpath)
615 if defpath != defpushpath:
619 if defpath != defpushpath:
616 addpathconfig('default-push', defpushpath)
620 addpathconfig('default-push', defpushpath)
617
621
618 fp = self._repo.vfs("hgrc", "w", text=True)
622 fp = self._repo.vfs("hgrc", "w", text=True)
619 try:
623 try:
620 fp.write(''.join(lines))
624 fp.write(''.join(lines))
621 finally:
625 finally:
622 fp.close()
626 fp.close()
623
627
624 @annotatesubrepoerror
628 @annotatesubrepoerror
625 def add(self, ui, match, prefix, explicitonly, **opts):
629 def add(self, ui, match, prefix, explicitonly, **opts):
626 return cmdutil.add(ui, self._repo, match,
630 return cmdutil.add(ui, self._repo, match,
627 os.path.join(prefix, self._path), explicitonly,
631 os.path.join(prefix, self._path), explicitonly,
628 **opts)
632 **opts)
629
633
630 @annotatesubrepoerror
634 @annotatesubrepoerror
631 def addremove(self, m, prefix, opts, dry_run, similarity):
635 def addremove(self, m, prefix, opts, dry_run, similarity):
632 # In the same way as sub directories are processed, once in a subrepo,
636 # In the same way as sub directories are processed, once in a subrepo,
633 # always entry any of its subrepos. Don't corrupt the options that will
637 # always entry any of its subrepos. Don't corrupt the options that will
634 # be used to process sibling subrepos however.
638 # be used to process sibling subrepos however.
635 opts = copy.copy(opts)
639 opts = copy.copy(opts)
636 opts['subrepos'] = True
640 opts['subrepos'] = True
637 return scmutil.addremove(self._repo, m,
641 return scmutil.addremove(self._repo, m,
638 os.path.join(prefix, self._path), opts,
642 os.path.join(prefix, self._path), opts,
639 dry_run, similarity)
643 dry_run, similarity)
640
644
641 @annotatesubrepoerror
645 @annotatesubrepoerror
642 def cat(self, match, prefix, **opts):
646 def cat(self, match, prefix, **opts):
643 rev = self._state[1]
647 rev = self._state[1]
644 ctx = self._repo[rev]
648 ctx = self._repo[rev]
645 return cmdutil.cat(self.ui, self._repo, ctx, match, prefix, **opts)
649 return cmdutil.cat(self.ui, self._repo, ctx, match, prefix, **opts)
646
650
647 @annotatesubrepoerror
651 @annotatesubrepoerror
648 def status(self, rev2, **opts):
652 def status(self, rev2, **opts):
649 try:
653 try:
650 rev1 = self._state[1]
654 rev1 = self._state[1]
651 ctx1 = self._repo[rev1]
655 ctx1 = self._repo[rev1]
652 ctx2 = self._repo[rev2]
656 ctx2 = self._repo[rev2]
653 return self._repo.status(ctx1, ctx2, **opts)
657 return self._repo.status(ctx1, ctx2, **opts)
654 except error.RepoLookupError, inst:
658 except error.RepoLookupError, inst:
655 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
659 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
656 % (inst, subrelpath(self)))
660 % (inst, subrelpath(self)))
657 return scmutil.status([], [], [], [], [], [], [])
661 return scmutil.status([], [], [], [], [], [], [])
658
662
659 @annotatesubrepoerror
663 @annotatesubrepoerror
660 def diff(self, ui, diffopts, node2, match, prefix, **opts):
664 def diff(self, ui, diffopts, node2, match, prefix, **opts):
661 try:
665 try:
662 node1 = node.bin(self._state[1])
666 node1 = node.bin(self._state[1])
663 # We currently expect node2 to come from substate and be
667 # We currently expect node2 to come from substate and be
664 # in hex format
668 # in hex format
665 if node2 is not None:
669 if node2 is not None:
666 node2 = node.bin(node2)
670 node2 = node.bin(node2)
667 cmdutil.diffordiffstat(ui, self._repo, diffopts,
671 cmdutil.diffordiffstat(ui, self._repo, diffopts,
668 node1, node2, match,
672 node1, node2, match,
669 prefix=posixpath.join(prefix, self._path),
673 prefix=posixpath.join(prefix, self._path),
670 listsubrepos=True, **opts)
674 listsubrepos=True, **opts)
671 except error.RepoLookupError, inst:
675 except error.RepoLookupError, inst:
672 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
676 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
673 % (inst, subrelpath(self)))
677 % (inst, subrelpath(self)))
674
678
675 @annotatesubrepoerror
679 @annotatesubrepoerror
676 def archive(self, archiver, prefix, match=None):
680 def archive(self, archiver, prefix, match=None):
677 self._get(self._state + ('hg',))
681 self._get(self._state + ('hg',))
678 total = abstractsubrepo.archive(self, archiver, prefix, match)
682 total = abstractsubrepo.archive(self, archiver, prefix, match)
679 rev = self._state[1]
683 rev = self._state[1]
680 ctx = self._repo[rev]
684 ctx = self._repo[rev]
681 for subpath in ctx.substate:
685 for subpath in ctx.substate:
682 s = subrepo(ctx, subpath)
686 s = subrepo(ctx, subpath)
683 submatch = matchmod.narrowmatcher(subpath, match)
687 submatch = matchmod.narrowmatcher(subpath, match)
684 total += s.archive(
688 total += s.archive(
685 archiver, os.path.join(prefix, self._path), submatch)
689 archiver, os.path.join(prefix, self._path), submatch)
686 return total
690 return total
687
691
688 @annotatesubrepoerror
692 @annotatesubrepoerror
689 def dirty(self, ignoreupdate=False):
693 def dirty(self, ignoreupdate=False):
690 r = self._state[1]
694 r = self._state[1]
691 if r == '' and not ignoreupdate: # no state recorded
695 if r == '' and not ignoreupdate: # no state recorded
692 return True
696 return True
693 w = self._repo[None]
697 w = self._repo[None]
694 if r != w.p1().hex() and not ignoreupdate:
698 if r != w.p1().hex() and not ignoreupdate:
695 # different version checked out
699 # different version checked out
696 return True
700 return True
697 return w.dirty() # working directory changed
701 return w.dirty() # working directory changed
698
702
699 def basestate(self):
703 def basestate(self):
700 return self._repo['.'].hex()
704 return self._repo['.'].hex()
701
705
702 def checknested(self, path):
706 def checknested(self, path):
703 return self._repo._checknested(self._repo.wjoin(path))
707 return self._repo._checknested(self._repo.wjoin(path))
704
708
705 @annotatesubrepoerror
709 @annotatesubrepoerror
706 def commit(self, text, user, date):
710 def commit(self, text, user, date):
707 # don't bother committing in the subrepo if it's only been
711 # don't bother committing in the subrepo if it's only been
708 # updated
712 # updated
709 if not self.dirty(True):
713 if not self.dirty(True):
710 return self._repo['.'].hex()
714 return self._repo['.'].hex()
711 self.ui.debug("committing subrepo %s\n" % subrelpath(self))
715 self.ui.debug("committing subrepo %s\n" % subrelpath(self))
712 n = self._repo.commit(text, user, date)
716 n = self._repo.commit(text, user, date)
713 if not n:
717 if not n:
714 return self._repo['.'].hex() # different version checked out
718 return self._repo['.'].hex() # different version checked out
715 return node.hex(n)
719 return node.hex(n)
716
720
717 @annotatesubrepoerror
721 @annotatesubrepoerror
718 def phase(self, state):
722 def phase(self, state):
719 return self._repo[state].phase()
723 return self._repo[state].phase()
720
724
721 @annotatesubrepoerror
725 @annotatesubrepoerror
722 def remove(self):
726 def remove(self):
723 # we can't fully delete the repository as it may contain
727 # we can't fully delete the repository as it may contain
724 # local-only history
728 # local-only history
725 self.ui.note(_('removing subrepo %s\n') % subrelpath(self))
729 self.ui.note(_('removing subrepo %s\n') % subrelpath(self))
726 hg.clean(self._repo, node.nullid, False)
730 hg.clean(self._repo, node.nullid, False)
727
731
728 def _get(self, state):
732 def _get(self, state):
729 source, revision, kind = state
733 source, revision, kind = state
730 if revision in self._repo.unfiltered():
734 if revision in self._repo.unfiltered():
731 return True
735 return True
732 self._repo._subsource = source
736 self._repo._subsource = source
733 srcurl = _abssource(self._repo)
737 srcurl = _abssource(self._repo)
734 other = hg.peer(self._repo, {}, srcurl)
738 other = hg.peer(self._repo, {}, srcurl)
735 if len(self._repo) == 0:
739 if len(self._repo) == 0:
736 self.ui.status(_('cloning subrepo %s from %s\n')
740 self.ui.status(_('cloning subrepo %s from %s\n')
737 % (subrelpath(self), srcurl))
741 % (subrelpath(self), srcurl))
738 parentrepo = self._repo._subparent
742 parentrepo = self._repo._subparent
739 shutil.rmtree(self._repo.path)
743 shutil.rmtree(self._repo.path)
740 other, cloned = hg.clone(self._repo._subparent.baseui, {},
744 other, cloned = hg.clone(self._repo._subparent.baseui, {},
741 other, self._repo.root,
745 other, self._repo.root,
742 update=False)
746 update=False)
743 self._repo = cloned.local()
747 self._repo = cloned.local()
744 self._initrepo(parentrepo, source, create=True)
748 self._initrepo(parentrepo, source, create=True)
745 self._cachestorehash(srcurl)
749 self._cachestorehash(srcurl)
746 else:
750 else:
747 self.ui.status(_('pulling subrepo %s from %s\n')
751 self.ui.status(_('pulling subrepo %s from %s\n')
748 % (subrelpath(self), srcurl))
752 % (subrelpath(self), srcurl))
749 cleansub = self.storeclean(srcurl)
753 cleansub = self.storeclean(srcurl)
750 exchange.pull(self._repo, other)
754 exchange.pull(self._repo, other)
751 if cleansub:
755 if cleansub:
752 # keep the repo clean after pull
756 # keep the repo clean after pull
753 self._cachestorehash(srcurl)
757 self._cachestorehash(srcurl)
754 return False
758 return False
755
759
756 @annotatesubrepoerror
760 @annotatesubrepoerror
757 def get(self, state, overwrite=False):
761 def get(self, state, overwrite=False):
758 inrepo = self._get(state)
762 inrepo = self._get(state)
759 source, revision, kind = state
763 source, revision, kind = state
760 repo = self._repo
764 repo = self._repo
761 repo.ui.debug("getting subrepo %s\n" % self._path)
765 repo.ui.debug("getting subrepo %s\n" % self._path)
762 if inrepo:
766 if inrepo:
763 urepo = repo.unfiltered()
767 urepo = repo.unfiltered()
764 ctx = urepo[revision]
768 ctx = urepo[revision]
765 if ctx.hidden():
769 if ctx.hidden():
766 urepo.ui.warn(
770 urepo.ui.warn(
767 _('revision %s in subrepo %s is hidden\n') \
771 _('revision %s in subrepo %s is hidden\n') \
768 % (revision[0:12], self._path))
772 % (revision[0:12], self._path))
769 repo = urepo
773 repo = urepo
770 hg.updaterepo(repo, revision, overwrite)
774 hg.updaterepo(repo, revision, overwrite)
771
775
772 @annotatesubrepoerror
776 @annotatesubrepoerror
773 def merge(self, state):
777 def merge(self, state):
774 self._get(state)
778 self._get(state)
775 cur = self._repo['.']
779 cur = self._repo['.']
776 dst = self._repo[state[1]]
780 dst = self._repo[state[1]]
777 anc = dst.ancestor(cur)
781 anc = dst.ancestor(cur)
778
782
779 def mergefunc():
783 def mergefunc():
780 if anc == cur and dst.branch() == cur.branch():
784 if anc == cur and dst.branch() == cur.branch():
781 self.ui.debug("updating subrepo %s\n" % subrelpath(self))
785 self.ui.debug("updating subrepo %s\n" % subrelpath(self))
782 hg.update(self._repo, state[1])
786 hg.update(self._repo, state[1])
783 elif anc == dst:
787 elif anc == dst:
784 self.ui.debug("skipping subrepo %s\n" % subrelpath(self))
788 self.ui.debug("skipping subrepo %s\n" % subrelpath(self))
785 else:
789 else:
786 self.ui.debug("merging subrepo %s\n" % subrelpath(self))
790 self.ui.debug("merging subrepo %s\n" % subrelpath(self))
787 hg.merge(self._repo, state[1], remind=False)
791 hg.merge(self._repo, state[1], remind=False)
788
792
789 wctx = self._repo[None]
793 wctx = self._repo[None]
790 if self.dirty():
794 if self.dirty():
791 if anc != dst:
795 if anc != dst:
792 if _updateprompt(self.ui, self, wctx.dirty(), cur, dst):
796 if _updateprompt(self.ui, self, wctx.dirty(), cur, dst):
793 mergefunc()
797 mergefunc()
794 else:
798 else:
795 mergefunc()
799 mergefunc()
796 else:
800 else:
797 mergefunc()
801 mergefunc()
798
802
799 @annotatesubrepoerror
803 @annotatesubrepoerror
800 def push(self, opts):
804 def push(self, opts):
801 force = opts.get('force')
805 force = opts.get('force')
802 newbranch = opts.get('new_branch')
806 newbranch = opts.get('new_branch')
803 ssh = opts.get('ssh')
807 ssh = opts.get('ssh')
804
808
805 # push subrepos depth-first for coherent ordering
809 # push subrepos depth-first for coherent ordering
806 c = self._repo['']
810 c = self._repo['']
807 subs = c.substate # only repos that are committed
811 subs = c.substate # only repos that are committed
808 for s in sorted(subs):
812 for s in sorted(subs):
809 if c.sub(s).push(opts) == 0:
813 if c.sub(s).push(opts) == 0:
810 return False
814 return False
811
815
812 dsturl = _abssource(self._repo, True)
816 dsturl = _abssource(self._repo, True)
813 if not force:
817 if not force:
814 if self.storeclean(dsturl):
818 if self.storeclean(dsturl):
815 self.ui.status(
819 self.ui.status(
816 _('no changes made to subrepo %s since last push to %s\n')
820 _('no changes made to subrepo %s since last push to %s\n')
817 % (subrelpath(self), dsturl))
821 % (subrelpath(self), dsturl))
818 return None
822 return None
819 self.ui.status(_('pushing subrepo %s to %s\n') %
823 self.ui.status(_('pushing subrepo %s to %s\n') %
820 (subrelpath(self), dsturl))
824 (subrelpath(self), dsturl))
821 other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
825 other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
822 res = exchange.push(self._repo, other, force, newbranch=newbranch)
826 res = exchange.push(self._repo, other, force, newbranch=newbranch)
823
827
824 # the repo is now clean
828 # the repo is now clean
825 self._cachestorehash(dsturl)
829 self._cachestorehash(dsturl)
826 return res.cgresult
830 return res.cgresult
827
831
828 @annotatesubrepoerror
832 @annotatesubrepoerror
829 def outgoing(self, ui, dest, opts):
833 def outgoing(self, ui, dest, opts):
830 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
834 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
831
835
832 @annotatesubrepoerror
836 @annotatesubrepoerror
833 def incoming(self, ui, source, opts):
837 def incoming(self, ui, source, opts):
834 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
838 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
835
839
836 @annotatesubrepoerror
840 @annotatesubrepoerror
837 def files(self):
841 def files(self):
838 rev = self._state[1]
842 rev = self._state[1]
839 ctx = self._repo[rev]
843 ctx = self._repo[rev]
840 return ctx.manifest().keys()
844 return ctx.manifest().keys()
841
845
842 def filedata(self, name):
846 def filedata(self, name):
843 rev = self._state[1]
847 rev = self._state[1]
844 return self._repo[rev][name].data()
848 return self._repo[rev][name].data()
845
849
846 def fileflags(self, name):
850 def fileflags(self, name):
847 rev = self._state[1]
851 rev = self._state[1]
848 ctx = self._repo[rev]
852 ctx = self._repo[rev]
849 return ctx.flags(name)
853 return ctx.flags(name)
850
854
855 @annotatesubrepoerror
856 def printfiles(self, ui, m, fm, fmt):
857 # If the parent context is a workingctx, use the workingctx here for
858 # consistency.
859 if self._ctx.rev() is None:
860 ctx = self._repo[None]
861 else:
862 rev = self._state[1]
863 ctx = self._repo[rev]
864 return cmdutil.files(ui, ctx, m, fm, fmt, True)
865
851 def walk(self, match):
866 def walk(self, match):
852 ctx = self._repo[None]
867 ctx = self._repo[None]
853 return ctx.walk(match)
868 return ctx.walk(match)
854
869
855 @annotatesubrepoerror
870 @annotatesubrepoerror
856 def forget(self, match, prefix):
871 def forget(self, match, prefix):
857 return cmdutil.forget(self.ui, self._repo, match,
872 return cmdutil.forget(self.ui, self._repo, match,
858 os.path.join(prefix, self._path), True)
873 os.path.join(prefix, self._path), True)
859
874
860 @annotatesubrepoerror
875 @annotatesubrepoerror
861 def removefiles(self, matcher, prefix, after, force, subrepos):
876 def removefiles(self, matcher, prefix, after, force, subrepos):
862 return cmdutil.remove(self.ui, self._repo, matcher,
877 return cmdutil.remove(self.ui, self._repo, matcher,
863 os.path.join(prefix, self._path), after, force,
878 os.path.join(prefix, self._path), after, force,
864 subrepos)
879 subrepos)
865
880
866 @annotatesubrepoerror
881 @annotatesubrepoerror
867 def revert(self, substate, *pats, **opts):
882 def revert(self, substate, *pats, **opts):
868 # reverting a subrepo is a 2 step process:
883 # reverting a subrepo is a 2 step process:
869 # 1. if the no_backup is not set, revert all modified
884 # 1. if the no_backup is not set, revert all modified
870 # files inside the subrepo
885 # files inside the subrepo
871 # 2. update the subrepo to the revision specified in
886 # 2. update the subrepo to the revision specified in
872 # the corresponding substate dictionary
887 # the corresponding substate dictionary
873 self.ui.status(_('reverting subrepo %s\n') % substate[0])
888 self.ui.status(_('reverting subrepo %s\n') % substate[0])
874 if not opts.get('no_backup'):
889 if not opts.get('no_backup'):
875 # Revert all files on the subrepo, creating backups
890 # Revert all files on the subrepo, creating backups
876 # Note that this will not recursively revert subrepos
891 # Note that this will not recursively revert subrepos
877 # We could do it if there was a set:subrepos() predicate
892 # We could do it if there was a set:subrepos() predicate
878 opts = opts.copy()
893 opts = opts.copy()
879 opts['date'] = None
894 opts['date'] = None
880 opts['rev'] = substate[1]
895 opts['rev'] = substate[1]
881
896
882 self.filerevert(*pats, **opts)
897 self.filerevert(*pats, **opts)
883
898
884 # Update the repo to the revision specified in the given substate
899 # Update the repo to the revision specified in the given substate
885 if not opts.get('dry_run'):
900 if not opts.get('dry_run'):
886 self.get(substate, overwrite=True)
901 self.get(substate, overwrite=True)
887
902
888 def filerevert(self, *pats, **opts):
903 def filerevert(self, *pats, **opts):
889 ctx = self._repo[opts['rev']]
904 ctx = self._repo[opts['rev']]
890 parents = self._repo.dirstate.parents()
905 parents = self._repo.dirstate.parents()
891 if opts.get('all'):
906 if opts.get('all'):
892 pats = ['set:modified()']
907 pats = ['set:modified()']
893 else:
908 else:
894 pats = []
909 pats = []
895 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts)
910 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts)
896
911
897 def shortid(self, revid):
912 def shortid(self, revid):
898 return revid[:12]
913 return revid[:12]
899
914
900 class svnsubrepo(abstractsubrepo):
915 class svnsubrepo(abstractsubrepo):
901 def __init__(self, ctx, path, state):
916 def __init__(self, ctx, path, state):
902 super(svnsubrepo, self).__init__(ctx.repo().ui)
917 super(svnsubrepo, self).__init__(ctx.repo().ui)
903 self._path = path
918 self._path = path
904 self._state = state
919 self._state = state
905 self._ctx = ctx
920 self._ctx = ctx
906 self._exe = util.findexe('svn')
921 self._exe = util.findexe('svn')
907 if not self._exe:
922 if not self._exe:
908 raise util.Abort(_("'svn' executable not found for subrepo '%s'")
923 raise util.Abort(_("'svn' executable not found for subrepo '%s'")
909 % self._path)
924 % self._path)
910
925
911 def _svncommand(self, commands, filename='', failok=False):
926 def _svncommand(self, commands, filename='', failok=False):
912 cmd = [self._exe]
927 cmd = [self._exe]
913 extrakw = {}
928 extrakw = {}
914 if not self.ui.interactive():
929 if not self.ui.interactive():
915 # Making stdin be a pipe should prevent svn from behaving
930 # Making stdin be a pipe should prevent svn from behaving
916 # interactively even if we can't pass --non-interactive.
931 # interactively even if we can't pass --non-interactive.
917 extrakw['stdin'] = subprocess.PIPE
932 extrakw['stdin'] = subprocess.PIPE
918 # Starting in svn 1.5 --non-interactive is a global flag
933 # Starting in svn 1.5 --non-interactive is a global flag
919 # instead of being per-command, but we need to support 1.4 so
934 # instead of being per-command, but we need to support 1.4 so
920 # we have to be intelligent about what commands take
935 # we have to be intelligent about what commands take
921 # --non-interactive.
936 # --non-interactive.
922 if commands[0] in ('update', 'checkout', 'commit'):
937 if commands[0] in ('update', 'checkout', 'commit'):
923 cmd.append('--non-interactive')
938 cmd.append('--non-interactive')
924 cmd.extend(commands)
939 cmd.extend(commands)
925 if filename is not None:
940 if filename is not None:
926 path = os.path.join(self._ctx.repo().origroot, self._path, filename)
941 path = os.path.join(self._ctx.repo().origroot, self._path, filename)
927 cmd.append(path)
942 cmd.append(path)
928 env = dict(os.environ)
943 env = dict(os.environ)
929 # Avoid localized output, preserve current locale for everything else.
944 # Avoid localized output, preserve current locale for everything else.
930 lc_all = env.get('LC_ALL')
945 lc_all = env.get('LC_ALL')
931 if lc_all:
946 if lc_all:
932 env['LANG'] = lc_all
947 env['LANG'] = lc_all
933 del env['LC_ALL']
948 del env['LC_ALL']
934 env['LC_MESSAGES'] = 'C'
949 env['LC_MESSAGES'] = 'C'
935 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds,
950 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds,
936 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
951 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
937 universal_newlines=True, env=env, **extrakw)
952 universal_newlines=True, env=env, **extrakw)
938 stdout, stderr = p.communicate()
953 stdout, stderr = p.communicate()
939 stderr = stderr.strip()
954 stderr = stderr.strip()
940 if not failok:
955 if not failok:
941 if p.returncode:
956 if p.returncode:
942 raise util.Abort(stderr or 'exited with code %d' % p.returncode)
957 raise util.Abort(stderr or 'exited with code %d' % p.returncode)
943 if stderr:
958 if stderr:
944 self.ui.warn(stderr + '\n')
959 self.ui.warn(stderr + '\n')
945 return stdout, stderr
960 return stdout, stderr
946
961
947 @propertycache
962 @propertycache
948 def _svnversion(self):
963 def _svnversion(self):
949 output, err = self._svncommand(['--version', '--quiet'], filename=None)
964 output, err = self._svncommand(['--version', '--quiet'], filename=None)
950 m = re.search(r'^(\d+)\.(\d+)', output)
965 m = re.search(r'^(\d+)\.(\d+)', output)
951 if not m:
966 if not m:
952 raise util.Abort(_('cannot retrieve svn tool version'))
967 raise util.Abort(_('cannot retrieve svn tool version'))
953 return (int(m.group(1)), int(m.group(2)))
968 return (int(m.group(1)), int(m.group(2)))
954
969
955 def _wcrevs(self):
970 def _wcrevs(self):
956 # Get the working directory revision as well as the last
971 # Get the working directory revision as well as the last
957 # commit revision so we can compare the subrepo state with
972 # commit revision so we can compare the subrepo state with
958 # both. We used to store the working directory one.
973 # both. We used to store the working directory one.
959 output, err = self._svncommand(['info', '--xml'])
974 output, err = self._svncommand(['info', '--xml'])
960 doc = xml.dom.minidom.parseString(output)
975 doc = xml.dom.minidom.parseString(output)
961 entries = doc.getElementsByTagName('entry')
976 entries = doc.getElementsByTagName('entry')
962 lastrev, rev = '0', '0'
977 lastrev, rev = '0', '0'
963 if entries:
978 if entries:
964 rev = str(entries[0].getAttribute('revision')) or '0'
979 rev = str(entries[0].getAttribute('revision')) or '0'
965 commits = entries[0].getElementsByTagName('commit')
980 commits = entries[0].getElementsByTagName('commit')
966 if commits:
981 if commits:
967 lastrev = str(commits[0].getAttribute('revision')) or '0'
982 lastrev = str(commits[0].getAttribute('revision')) or '0'
968 return (lastrev, rev)
983 return (lastrev, rev)
969
984
970 def _wcrev(self):
985 def _wcrev(self):
971 return self._wcrevs()[0]
986 return self._wcrevs()[0]
972
987
973 def _wcchanged(self):
988 def _wcchanged(self):
974 """Return (changes, extchanges, missing) where changes is True
989 """Return (changes, extchanges, missing) where changes is True
975 if the working directory was changed, extchanges is
990 if the working directory was changed, extchanges is
976 True if any of these changes concern an external entry and missing
991 True if any of these changes concern an external entry and missing
977 is True if any change is a missing entry.
992 is True if any change is a missing entry.
978 """
993 """
979 output, err = self._svncommand(['status', '--xml'])
994 output, err = self._svncommand(['status', '--xml'])
980 externals, changes, missing = [], [], []
995 externals, changes, missing = [], [], []
981 doc = xml.dom.minidom.parseString(output)
996 doc = xml.dom.minidom.parseString(output)
982 for e in doc.getElementsByTagName('entry'):
997 for e in doc.getElementsByTagName('entry'):
983 s = e.getElementsByTagName('wc-status')
998 s = e.getElementsByTagName('wc-status')
984 if not s:
999 if not s:
985 continue
1000 continue
986 item = s[0].getAttribute('item')
1001 item = s[0].getAttribute('item')
987 props = s[0].getAttribute('props')
1002 props = s[0].getAttribute('props')
988 path = e.getAttribute('path')
1003 path = e.getAttribute('path')
989 if item == 'external':
1004 if item == 'external':
990 externals.append(path)
1005 externals.append(path)
991 elif item == 'missing':
1006 elif item == 'missing':
992 missing.append(path)
1007 missing.append(path)
993 if (item not in ('', 'normal', 'unversioned', 'external')
1008 if (item not in ('', 'normal', 'unversioned', 'external')
994 or props not in ('', 'none', 'normal')):
1009 or props not in ('', 'none', 'normal')):
995 changes.append(path)
1010 changes.append(path)
996 for path in changes:
1011 for path in changes:
997 for ext in externals:
1012 for ext in externals:
998 if path == ext or path.startswith(ext + os.sep):
1013 if path == ext or path.startswith(ext + os.sep):
999 return True, True, bool(missing)
1014 return True, True, bool(missing)
1000 return bool(changes), False, bool(missing)
1015 return bool(changes), False, bool(missing)
1001
1016
1002 def dirty(self, ignoreupdate=False):
1017 def dirty(self, ignoreupdate=False):
1003 if not self._wcchanged()[0]:
1018 if not self._wcchanged()[0]:
1004 if self._state[1] in self._wcrevs() or ignoreupdate:
1019 if self._state[1] in self._wcrevs() or ignoreupdate:
1005 return False
1020 return False
1006 return True
1021 return True
1007
1022
1008 def basestate(self):
1023 def basestate(self):
1009 lastrev, rev = self._wcrevs()
1024 lastrev, rev = self._wcrevs()
1010 if lastrev != rev:
1025 if lastrev != rev:
1011 # Last committed rev is not the same than rev. We would
1026 # Last committed rev is not the same than rev. We would
1012 # like to take lastrev but we do not know if the subrepo
1027 # like to take lastrev but we do not know if the subrepo
1013 # URL exists at lastrev. Test it and fallback to rev it
1028 # URL exists at lastrev. Test it and fallback to rev it
1014 # is not there.
1029 # is not there.
1015 try:
1030 try:
1016 self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)])
1031 self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)])
1017 return lastrev
1032 return lastrev
1018 except error.Abort:
1033 except error.Abort:
1019 pass
1034 pass
1020 return rev
1035 return rev
1021
1036
1022 @annotatesubrepoerror
1037 @annotatesubrepoerror
1023 def commit(self, text, user, date):
1038 def commit(self, text, user, date):
1024 # user and date are out of our hands since svn is centralized
1039 # user and date are out of our hands since svn is centralized
1025 changed, extchanged, missing = self._wcchanged()
1040 changed, extchanged, missing = self._wcchanged()
1026 if not changed:
1041 if not changed:
1027 return self.basestate()
1042 return self.basestate()
1028 if extchanged:
1043 if extchanged:
1029 # Do not try to commit externals
1044 # Do not try to commit externals
1030 raise util.Abort(_('cannot commit svn externals'))
1045 raise util.Abort(_('cannot commit svn externals'))
1031 if missing:
1046 if missing:
1032 # svn can commit with missing entries but aborting like hg
1047 # svn can commit with missing entries but aborting like hg
1033 # seems a better approach.
1048 # seems a better approach.
1034 raise util.Abort(_('cannot commit missing svn entries'))
1049 raise util.Abort(_('cannot commit missing svn entries'))
1035 commitinfo, err = self._svncommand(['commit', '-m', text])
1050 commitinfo, err = self._svncommand(['commit', '-m', text])
1036 self.ui.status(commitinfo)
1051 self.ui.status(commitinfo)
1037 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
1052 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
1038 if not newrev:
1053 if not newrev:
1039 if not commitinfo.strip():
1054 if not commitinfo.strip():
1040 # Sometimes, our definition of "changed" differs from
1055 # Sometimes, our definition of "changed" differs from
1041 # svn one. For instance, svn ignores missing files
1056 # svn one. For instance, svn ignores missing files
1042 # when committing. If there are only missing files, no
1057 # when committing. If there are only missing files, no
1043 # commit is made, no output and no error code.
1058 # commit is made, no output and no error code.
1044 raise util.Abort(_('failed to commit svn changes'))
1059 raise util.Abort(_('failed to commit svn changes'))
1045 raise util.Abort(commitinfo.splitlines()[-1])
1060 raise util.Abort(commitinfo.splitlines()[-1])
1046 newrev = newrev.groups()[0]
1061 newrev = newrev.groups()[0]
1047 self.ui.status(self._svncommand(['update', '-r', newrev])[0])
1062 self.ui.status(self._svncommand(['update', '-r', newrev])[0])
1048 return newrev
1063 return newrev
1049
1064
1050 @annotatesubrepoerror
1065 @annotatesubrepoerror
1051 def remove(self):
1066 def remove(self):
1052 if self.dirty():
1067 if self.dirty():
1053 self.ui.warn(_('not removing repo %s because '
1068 self.ui.warn(_('not removing repo %s because '
1054 'it has changes.\n') % self._path)
1069 'it has changes.\n') % self._path)
1055 return
1070 return
1056 self.ui.note(_('removing subrepo %s\n') % self._path)
1071 self.ui.note(_('removing subrepo %s\n') % self._path)
1057
1072
1058 def onerror(function, path, excinfo):
1073 def onerror(function, path, excinfo):
1059 if function is not os.remove:
1074 if function is not os.remove:
1060 raise
1075 raise
1061 # read-only files cannot be unlinked under Windows
1076 # read-only files cannot be unlinked under Windows
1062 s = os.stat(path)
1077 s = os.stat(path)
1063 if (s.st_mode & stat.S_IWRITE) != 0:
1078 if (s.st_mode & stat.S_IWRITE) != 0:
1064 raise
1079 raise
1065 os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE)
1080 os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE)
1066 os.remove(path)
1081 os.remove(path)
1067
1082
1068 path = self._ctx.repo().wjoin(self._path)
1083 path = self._ctx.repo().wjoin(self._path)
1069 shutil.rmtree(path, onerror=onerror)
1084 shutil.rmtree(path, onerror=onerror)
1070 try:
1085 try:
1071 os.removedirs(os.path.dirname(path))
1086 os.removedirs(os.path.dirname(path))
1072 except OSError:
1087 except OSError:
1073 pass
1088 pass
1074
1089
1075 @annotatesubrepoerror
1090 @annotatesubrepoerror
1076 def get(self, state, overwrite=False):
1091 def get(self, state, overwrite=False):
1077 if overwrite:
1092 if overwrite:
1078 self._svncommand(['revert', '--recursive'])
1093 self._svncommand(['revert', '--recursive'])
1079 args = ['checkout']
1094 args = ['checkout']
1080 if self._svnversion >= (1, 5):
1095 if self._svnversion >= (1, 5):
1081 args.append('--force')
1096 args.append('--force')
1082 # The revision must be specified at the end of the URL to properly
1097 # The revision must be specified at the end of the URL to properly
1083 # update to a directory which has since been deleted and recreated.
1098 # update to a directory which has since been deleted and recreated.
1084 args.append('%s@%s' % (state[0], state[1]))
1099 args.append('%s@%s' % (state[0], state[1]))
1085 status, err = self._svncommand(args, failok=True)
1100 status, err = self._svncommand(args, failok=True)
1086 _sanitize(self.ui, self._ctx.repo().wjoin(self._path), '.svn')
1101 _sanitize(self.ui, self._ctx.repo().wjoin(self._path), '.svn')
1087 if not re.search('Checked out revision [0-9]+.', status):
1102 if not re.search('Checked out revision [0-9]+.', status):
1088 if ('is already a working copy for a different URL' in err
1103 if ('is already a working copy for a different URL' in err
1089 and (self._wcchanged()[:2] == (False, False))):
1104 and (self._wcchanged()[:2] == (False, False))):
1090 # obstructed but clean working copy, so just blow it away.
1105 # obstructed but clean working copy, so just blow it away.
1091 self.remove()
1106 self.remove()
1092 self.get(state, overwrite=False)
1107 self.get(state, overwrite=False)
1093 return
1108 return
1094 raise util.Abort((status or err).splitlines()[-1])
1109 raise util.Abort((status or err).splitlines()[-1])
1095 self.ui.status(status)
1110 self.ui.status(status)
1096
1111
1097 @annotatesubrepoerror
1112 @annotatesubrepoerror
1098 def merge(self, state):
1113 def merge(self, state):
1099 old = self._state[1]
1114 old = self._state[1]
1100 new = state[1]
1115 new = state[1]
1101 wcrev = self._wcrev()
1116 wcrev = self._wcrev()
1102 if new != wcrev:
1117 if new != wcrev:
1103 dirty = old == wcrev or self._wcchanged()[0]
1118 dirty = old == wcrev or self._wcchanged()[0]
1104 if _updateprompt(self.ui, self, dirty, wcrev, new):
1119 if _updateprompt(self.ui, self, dirty, wcrev, new):
1105 self.get(state, False)
1120 self.get(state, False)
1106
1121
1107 def push(self, opts):
1122 def push(self, opts):
1108 # push is a no-op for SVN
1123 # push is a no-op for SVN
1109 return True
1124 return True
1110
1125
1111 @annotatesubrepoerror
1126 @annotatesubrepoerror
1112 def files(self):
1127 def files(self):
1113 output = self._svncommand(['list', '--recursive', '--xml'])[0]
1128 output = self._svncommand(['list', '--recursive', '--xml'])[0]
1114 doc = xml.dom.minidom.parseString(output)
1129 doc = xml.dom.minidom.parseString(output)
1115 paths = []
1130 paths = []
1116 for e in doc.getElementsByTagName('entry'):
1131 for e in doc.getElementsByTagName('entry'):
1117 kind = str(e.getAttribute('kind'))
1132 kind = str(e.getAttribute('kind'))
1118 if kind != 'file':
1133 if kind != 'file':
1119 continue
1134 continue
1120 name = ''.join(c.data for c
1135 name = ''.join(c.data for c
1121 in e.getElementsByTagName('name')[0].childNodes
1136 in e.getElementsByTagName('name')[0].childNodes
1122 if c.nodeType == c.TEXT_NODE)
1137 if c.nodeType == c.TEXT_NODE)
1123 paths.append(name.encode('utf-8'))
1138 paths.append(name.encode('utf-8'))
1124 return paths
1139 return paths
1125
1140
1126 def filedata(self, name):
1141 def filedata(self, name):
1127 return self._svncommand(['cat'], name)[0]
1142 return self._svncommand(['cat'], name)[0]
1128
1143
1129
1144
1130 class gitsubrepo(abstractsubrepo):
1145 class gitsubrepo(abstractsubrepo):
1131 def __init__(self, ctx, path, state):
1146 def __init__(self, ctx, path, state):
1132 super(gitsubrepo, self).__init__(ctx.repo().ui)
1147 super(gitsubrepo, self).__init__(ctx.repo().ui)
1133 self._state = state
1148 self._state = state
1134 self._ctx = ctx
1149 self._ctx = ctx
1135 self._path = path
1150 self._path = path
1136 self._relpath = os.path.join(reporelpath(ctx.repo()), path)
1151 self._relpath = os.path.join(reporelpath(ctx.repo()), path)
1137 self._abspath = ctx.repo().wjoin(path)
1152 self._abspath = ctx.repo().wjoin(path)
1138 self._subparent = ctx.repo()
1153 self._subparent = ctx.repo()
1139 self._ensuregit()
1154 self._ensuregit()
1140
1155
1141 def _ensuregit(self):
1156 def _ensuregit(self):
1142 try:
1157 try:
1143 self._gitexecutable = 'git'
1158 self._gitexecutable = 'git'
1144 out, err = self._gitnodir(['--version'])
1159 out, err = self._gitnodir(['--version'])
1145 except OSError, e:
1160 except OSError, e:
1146 if e.errno != 2 or os.name != 'nt':
1161 if e.errno != 2 or os.name != 'nt':
1147 raise
1162 raise
1148 self._gitexecutable = 'git.cmd'
1163 self._gitexecutable = 'git.cmd'
1149 out, err = self._gitnodir(['--version'])
1164 out, err = self._gitnodir(['--version'])
1150 versionstatus = self._checkversion(out)
1165 versionstatus = self._checkversion(out)
1151 if versionstatus == 'unknown':
1166 if versionstatus == 'unknown':
1152 self.ui.warn(_('cannot retrieve git version\n'))
1167 self.ui.warn(_('cannot retrieve git version\n'))
1153 elif versionstatus == 'abort':
1168 elif versionstatus == 'abort':
1154 raise util.Abort(_('git subrepo requires at least 1.6.0 or later'))
1169 raise util.Abort(_('git subrepo requires at least 1.6.0 or later'))
1155 elif versionstatus == 'warning':
1170 elif versionstatus == 'warning':
1156 self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
1171 self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
1157
1172
1158 @staticmethod
1173 @staticmethod
1159 def _gitversion(out):
1174 def _gitversion(out):
1160 m = re.search(r'^git version (\d+)\.(\d+)\.(\d+)', out)
1175 m = re.search(r'^git version (\d+)\.(\d+)\.(\d+)', out)
1161 if m:
1176 if m:
1162 return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
1177 return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
1163
1178
1164 m = re.search(r'^git version (\d+)\.(\d+)', out)
1179 m = re.search(r'^git version (\d+)\.(\d+)', out)
1165 if m:
1180 if m:
1166 return (int(m.group(1)), int(m.group(2)), 0)
1181 return (int(m.group(1)), int(m.group(2)), 0)
1167
1182
1168 return -1
1183 return -1
1169
1184
1170 @staticmethod
1185 @staticmethod
1171 def _checkversion(out):
1186 def _checkversion(out):
1172 '''ensure git version is new enough
1187 '''ensure git version is new enough
1173
1188
1174 >>> _checkversion = gitsubrepo._checkversion
1189 >>> _checkversion = gitsubrepo._checkversion
1175 >>> _checkversion('git version 1.6.0')
1190 >>> _checkversion('git version 1.6.0')
1176 'ok'
1191 'ok'
1177 >>> _checkversion('git version 1.8.5')
1192 >>> _checkversion('git version 1.8.5')
1178 'ok'
1193 'ok'
1179 >>> _checkversion('git version 1.4.0')
1194 >>> _checkversion('git version 1.4.0')
1180 'abort'
1195 'abort'
1181 >>> _checkversion('git version 1.5.0')
1196 >>> _checkversion('git version 1.5.0')
1182 'warning'
1197 'warning'
1183 >>> _checkversion('git version 1.9-rc0')
1198 >>> _checkversion('git version 1.9-rc0')
1184 'ok'
1199 'ok'
1185 >>> _checkversion('git version 1.9.0.265.g81cdec2')
1200 >>> _checkversion('git version 1.9.0.265.g81cdec2')
1186 'ok'
1201 'ok'
1187 >>> _checkversion('git version 1.9.0.GIT')
1202 >>> _checkversion('git version 1.9.0.GIT')
1188 'ok'
1203 'ok'
1189 >>> _checkversion('git version 12345')
1204 >>> _checkversion('git version 12345')
1190 'unknown'
1205 'unknown'
1191 >>> _checkversion('no')
1206 >>> _checkversion('no')
1192 'unknown'
1207 'unknown'
1193 '''
1208 '''
1194 version = gitsubrepo._gitversion(out)
1209 version = gitsubrepo._gitversion(out)
1195 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
1210 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
1196 # despite the docstring comment. For now, error on 1.4.0, warn on
1211 # despite the docstring comment. For now, error on 1.4.0, warn on
1197 # 1.5.0 but attempt to continue.
1212 # 1.5.0 but attempt to continue.
1198 if version == -1:
1213 if version == -1:
1199 return 'unknown'
1214 return 'unknown'
1200 if version < (1, 5, 0):
1215 if version < (1, 5, 0):
1201 return 'abort'
1216 return 'abort'
1202 elif version < (1, 6, 0):
1217 elif version < (1, 6, 0):
1203 return 'warning'
1218 return 'warning'
1204 return 'ok'
1219 return 'ok'
1205
1220
1206 def _gitcommand(self, commands, env=None, stream=False):
1221 def _gitcommand(self, commands, env=None, stream=False):
1207 return self._gitdir(commands, env=env, stream=stream)[0]
1222 return self._gitdir(commands, env=env, stream=stream)[0]
1208
1223
1209 def _gitdir(self, commands, env=None, stream=False):
1224 def _gitdir(self, commands, env=None, stream=False):
1210 return self._gitnodir(commands, env=env, stream=stream,
1225 return self._gitnodir(commands, env=env, stream=stream,
1211 cwd=self._abspath)
1226 cwd=self._abspath)
1212
1227
1213 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
1228 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
1214 """Calls the git command
1229 """Calls the git command
1215
1230
1216 The methods tries to call the git command. versions prior to 1.6.0
1231 The methods tries to call the git command. versions prior to 1.6.0
1217 are not supported and very probably fail.
1232 are not supported and very probably fail.
1218 """
1233 """
1219 self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
1234 self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
1220 # unless ui.quiet is set, print git's stderr,
1235 # unless ui.quiet is set, print git's stderr,
1221 # which is mostly progress and useful info
1236 # which is mostly progress and useful info
1222 errpipe = None
1237 errpipe = None
1223 if self.ui.quiet:
1238 if self.ui.quiet:
1224 errpipe = open(os.devnull, 'w')
1239 errpipe = open(os.devnull, 'w')
1225 p = subprocess.Popen([self._gitexecutable] + commands, bufsize=-1,
1240 p = subprocess.Popen([self._gitexecutable] + commands, bufsize=-1,
1226 cwd=cwd, env=env, close_fds=util.closefds,
1241 cwd=cwd, env=env, close_fds=util.closefds,
1227 stdout=subprocess.PIPE, stderr=errpipe)
1242 stdout=subprocess.PIPE, stderr=errpipe)
1228 if stream:
1243 if stream:
1229 return p.stdout, None
1244 return p.stdout, None
1230
1245
1231 retdata = p.stdout.read().strip()
1246 retdata = p.stdout.read().strip()
1232 # wait for the child to exit to avoid race condition.
1247 # wait for the child to exit to avoid race condition.
1233 p.wait()
1248 p.wait()
1234
1249
1235 if p.returncode != 0 and p.returncode != 1:
1250 if p.returncode != 0 and p.returncode != 1:
1236 # there are certain error codes that are ok
1251 # there are certain error codes that are ok
1237 command = commands[0]
1252 command = commands[0]
1238 if command in ('cat-file', 'symbolic-ref'):
1253 if command in ('cat-file', 'symbolic-ref'):
1239 return retdata, p.returncode
1254 return retdata, p.returncode
1240 # for all others, abort
1255 # for all others, abort
1241 raise util.Abort('git %s error %d in %s' %
1256 raise util.Abort('git %s error %d in %s' %
1242 (command, p.returncode, self._relpath))
1257 (command, p.returncode, self._relpath))
1243
1258
1244 return retdata, p.returncode
1259 return retdata, p.returncode
1245
1260
1246 def _gitmissing(self):
1261 def _gitmissing(self):
1247 return not os.path.exists(os.path.join(self._abspath, '.git'))
1262 return not os.path.exists(os.path.join(self._abspath, '.git'))
1248
1263
1249 def _gitstate(self):
1264 def _gitstate(self):
1250 return self._gitcommand(['rev-parse', 'HEAD'])
1265 return self._gitcommand(['rev-parse', 'HEAD'])
1251
1266
1252 def _gitcurrentbranch(self):
1267 def _gitcurrentbranch(self):
1253 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
1268 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
1254 if err:
1269 if err:
1255 current = None
1270 current = None
1256 return current
1271 return current
1257
1272
1258 def _gitremote(self, remote):
1273 def _gitremote(self, remote):
1259 out = self._gitcommand(['remote', 'show', '-n', remote])
1274 out = self._gitcommand(['remote', 'show', '-n', remote])
1260 line = out.split('\n')[1]
1275 line = out.split('\n')[1]
1261 i = line.index('URL: ') + len('URL: ')
1276 i = line.index('URL: ') + len('URL: ')
1262 return line[i:]
1277 return line[i:]
1263
1278
1264 def _githavelocally(self, revision):
1279 def _githavelocally(self, revision):
1265 out, code = self._gitdir(['cat-file', '-e', revision])
1280 out, code = self._gitdir(['cat-file', '-e', revision])
1266 return code == 0
1281 return code == 0
1267
1282
1268 def _gitisancestor(self, r1, r2):
1283 def _gitisancestor(self, r1, r2):
1269 base = self._gitcommand(['merge-base', r1, r2])
1284 base = self._gitcommand(['merge-base', r1, r2])
1270 return base == r1
1285 return base == r1
1271
1286
1272 def _gitisbare(self):
1287 def _gitisbare(self):
1273 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
1288 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
1274
1289
1275 def _gitupdatestat(self):
1290 def _gitupdatestat(self):
1276 """This must be run before git diff-index.
1291 """This must be run before git diff-index.
1277 diff-index only looks at changes to file stat;
1292 diff-index only looks at changes to file stat;
1278 this command looks at file contents and updates the stat."""
1293 this command looks at file contents and updates the stat."""
1279 self._gitcommand(['update-index', '-q', '--refresh'])
1294 self._gitcommand(['update-index', '-q', '--refresh'])
1280
1295
1281 def _gitbranchmap(self):
1296 def _gitbranchmap(self):
1282 '''returns 2 things:
1297 '''returns 2 things:
1283 a map from git branch to revision
1298 a map from git branch to revision
1284 a map from revision to branches'''
1299 a map from revision to branches'''
1285 branch2rev = {}
1300 branch2rev = {}
1286 rev2branch = {}
1301 rev2branch = {}
1287
1302
1288 out = self._gitcommand(['for-each-ref', '--format',
1303 out = self._gitcommand(['for-each-ref', '--format',
1289 '%(objectname) %(refname)'])
1304 '%(objectname) %(refname)'])
1290 for line in out.split('\n'):
1305 for line in out.split('\n'):
1291 revision, ref = line.split(' ')
1306 revision, ref = line.split(' ')
1292 if (not ref.startswith('refs/heads/') and
1307 if (not ref.startswith('refs/heads/') and
1293 not ref.startswith('refs/remotes/')):
1308 not ref.startswith('refs/remotes/')):
1294 continue
1309 continue
1295 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
1310 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
1296 continue # ignore remote/HEAD redirects
1311 continue # ignore remote/HEAD redirects
1297 branch2rev[ref] = revision
1312 branch2rev[ref] = revision
1298 rev2branch.setdefault(revision, []).append(ref)
1313 rev2branch.setdefault(revision, []).append(ref)
1299 return branch2rev, rev2branch
1314 return branch2rev, rev2branch
1300
1315
1301 def _gittracking(self, branches):
1316 def _gittracking(self, branches):
1302 'return map of remote branch to local tracking branch'
1317 'return map of remote branch to local tracking branch'
1303 # assumes no more than one local tracking branch for each remote
1318 # assumes no more than one local tracking branch for each remote
1304 tracking = {}
1319 tracking = {}
1305 for b in branches:
1320 for b in branches:
1306 if b.startswith('refs/remotes/'):
1321 if b.startswith('refs/remotes/'):
1307 continue
1322 continue
1308 bname = b.split('/', 2)[2]
1323 bname = b.split('/', 2)[2]
1309 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
1324 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
1310 if remote:
1325 if remote:
1311 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
1326 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
1312 tracking['refs/remotes/%s/%s' %
1327 tracking['refs/remotes/%s/%s' %
1313 (remote, ref.split('/', 2)[2])] = b
1328 (remote, ref.split('/', 2)[2])] = b
1314 return tracking
1329 return tracking
1315
1330
1316 def _abssource(self, source):
1331 def _abssource(self, source):
1317 if '://' not in source:
1332 if '://' not in source:
1318 # recognize the scp syntax as an absolute source
1333 # recognize the scp syntax as an absolute source
1319 colon = source.find(':')
1334 colon = source.find(':')
1320 if colon != -1 and '/' not in source[:colon]:
1335 if colon != -1 and '/' not in source[:colon]:
1321 return source
1336 return source
1322 self._subsource = source
1337 self._subsource = source
1323 return _abssource(self)
1338 return _abssource(self)
1324
1339
1325 def _fetch(self, source, revision):
1340 def _fetch(self, source, revision):
1326 if self._gitmissing():
1341 if self._gitmissing():
1327 source = self._abssource(source)
1342 source = self._abssource(source)
1328 self.ui.status(_('cloning subrepo %s from %s\n') %
1343 self.ui.status(_('cloning subrepo %s from %s\n') %
1329 (self._relpath, source))
1344 (self._relpath, source))
1330 self._gitnodir(['clone', source, self._abspath])
1345 self._gitnodir(['clone', source, self._abspath])
1331 if self._githavelocally(revision):
1346 if self._githavelocally(revision):
1332 return
1347 return
1333 self.ui.status(_('pulling subrepo %s from %s\n') %
1348 self.ui.status(_('pulling subrepo %s from %s\n') %
1334 (self._relpath, self._gitremote('origin')))
1349 (self._relpath, self._gitremote('origin')))
1335 # try only origin: the originally cloned repo
1350 # try only origin: the originally cloned repo
1336 self._gitcommand(['fetch'])
1351 self._gitcommand(['fetch'])
1337 if not self._githavelocally(revision):
1352 if not self._githavelocally(revision):
1338 raise util.Abort(_("revision %s does not exist in subrepo %s\n") %
1353 raise util.Abort(_("revision %s does not exist in subrepo %s\n") %
1339 (revision, self._relpath))
1354 (revision, self._relpath))
1340
1355
1341 @annotatesubrepoerror
1356 @annotatesubrepoerror
1342 def dirty(self, ignoreupdate=False):
1357 def dirty(self, ignoreupdate=False):
1343 if self._gitmissing():
1358 if self._gitmissing():
1344 return self._state[1] != ''
1359 return self._state[1] != ''
1345 if self._gitisbare():
1360 if self._gitisbare():
1346 return True
1361 return True
1347 if not ignoreupdate and self._state[1] != self._gitstate():
1362 if not ignoreupdate and self._state[1] != self._gitstate():
1348 # different version checked out
1363 # different version checked out
1349 return True
1364 return True
1350 # check for staged changes or modified files; ignore untracked files
1365 # check for staged changes or modified files; ignore untracked files
1351 self._gitupdatestat()
1366 self._gitupdatestat()
1352 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1367 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1353 return code == 1
1368 return code == 1
1354
1369
1355 def basestate(self):
1370 def basestate(self):
1356 return self._gitstate()
1371 return self._gitstate()
1357
1372
1358 @annotatesubrepoerror
1373 @annotatesubrepoerror
1359 def get(self, state, overwrite=False):
1374 def get(self, state, overwrite=False):
1360 source, revision, kind = state
1375 source, revision, kind = state
1361 if not revision:
1376 if not revision:
1362 self.remove()
1377 self.remove()
1363 return
1378 return
1364 self._fetch(source, revision)
1379 self._fetch(source, revision)
1365 # if the repo was set to be bare, unbare it
1380 # if the repo was set to be bare, unbare it
1366 if self._gitisbare():
1381 if self._gitisbare():
1367 self._gitcommand(['config', 'core.bare', 'false'])
1382 self._gitcommand(['config', 'core.bare', 'false'])
1368 if self._gitstate() == revision:
1383 if self._gitstate() == revision:
1369 self._gitcommand(['reset', '--hard', 'HEAD'])
1384 self._gitcommand(['reset', '--hard', 'HEAD'])
1370 return
1385 return
1371 elif self._gitstate() == revision:
1386 elif self._gitstate() == revision:
1372 if overwrite:
1387 if overwrite:
1373 # first reset the index to unmark new files for commit, because
1388 # first reset the index to unmark new files for commit, because
1374 # reset --hard will otherwise throw away files added for commit,
1389 # reset --hard will otherwise throw away files added for commit,
1375 # not just unmark them.
1390 # not just unmark them.
1376 self._gitcommand(['reset', 'HEAD'])
1391 self._gitcommand(['reset', 'HEAD'])
1377 self._gitcommand(['reset', '--hard', 'HEAD'])
1392 self._gitcommand(['reset', '--hard', 'HEAD'])
1378 return
1393 return
1379 branch2rev, rev2branch = self._gitbranchmap()
1394 branch2rev, rev2branch = self._gitbranchmap()
1380
1395
1381 def checkout(args):
1396 def checkout(args):
1382 cmd = ['checkout']
1397 cmd = ['checkout']
1383 if overwrite:
1398 if overwrite:
1384 # first reset the index to unmark new files for commit, because
1399 # first reset the index to unmark new files for commit, because
1385 # the -f option will otherwise throw away files added for
1400 # the -f option will otherwise throw away files added for
1386 # commit, not just unmark them.
1401 # commit, not just unmark them.
1387 self._gitcommand(['reset', 'HEAD'])
1402 self._gitcommand(['reset', 'HEAD'])
1388 cmd.append('-f')
1403 cmd.append('-f')
1389 self._gitcommand(cmd + args)
1404 self._gitcommand(cmd + args)
1390 _sanitize(self.ui, self._abspath, '.git')
1405 _sanitize(self.ui, self._abspath, '.git')
1391
1406
1392 def rawcheckout():
1407 def rawcheckout():
1393 # no branch to checkout, check it out with no branch
1408 # no branch to checkout, check it out with no branch
1394 self.ui.warn(_('checking out detached HEAD in subrepo %s\n') %
1409 self.ui.warn(_('checking out detached HEAD in subrepo %s\n') %
1395 self._relpath)
1410 self._relpath)
1396 self.ui.warn(_('check out a git branch if you intend '
1411 self.ui.warn(_('check out a git branch if you intend '
1397 'to make changes\n'))
1412 'to make changes\n'))
1398 checkout(['-q', revision])
1413 checkout(['-q', revision])
1399
1414
1400 if revision not in rev2branch:
1415 if revision not in rev2branch:
1401 rawcheckout()
1416 rawcheckout()
1402 return
1417 return
1403 branches = rev2branch[revision]
1418 branches = rev2branch[revision]
1404 firstlocalbranch = None
1419 firstlocalbranch = None
1405 for b in branches:
1420 for b in branches:
1406 if b == 'refs/heads/master':
1421 if b == 'refs/heads/master':
1407 # master trumps all other branches
1422 # master trumps all other branches
1408 checkout(['refs/heads/master'])
1423 checkout(['refs/heads/master'])
1409 return
1424 return
1410 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1425 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1411 firstlocalbranch = b
1426 firstlocalbranch = b
1412 if firstlocalbranch:
1427 if firstlocalbranch:
1413 checkout([firstlocalbranch])
1428 checkout([firstlocalbranch])
1414 return
1429 return
1415
1430
1416 tracking = self._gittracking(branch2rev.keys())
1431 tracking = self._gittracking(branch2rev.keys())
1417 # choose a remote branch already tracked if possible
1432 # choose a remote branch already tracked if possible
1418 remote = branches[0]
1433 remote = branches[0]
1419 if remote not in tracking:
1434 if remote not in tracking:
1420 for b in branches:
1435 for b in branches:
1421 if b in tracking:
1436 if b in tracking:
1422 remote = b
1437 remote = b
1423 break
1438 break
1424
1439
1425 if remote not in tracking:
1440 if remote not in tracking:
1426 # create a new local tracking branch
1441 # create a new local tracking branch
1427 local = remote.split('/', 3)[3]
1442 local = remote.split('/', 3)[3]
1428 checkout(['-b', local, remote])
1443 checkout(['-b', local, remote])
1429 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1444 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1430 # When updating to a tracked remote branch,
1445 # When updating to a tracked remote branch,
1431 # if the local tracking branch is downstream of it,
1446 # if the local tracking branch is downstream of it,
1432 # a normal `git pull` would have performed a "fast-forward merge"
1447 # a normal `git pull` would have performed a "fast-forward merge"
1433 # which is equivalent to updating the local branch to the remote.
1448 # which is equivalent to updating the local branch to the remote.
1434 # Since we are only looking at branching at update, we need to
1449 # Since we are only looking at branching at update, we need to
1435 # detect this situation and perform this action lazily.
1450 # detect this situation and perform this action lazily.
1436 if tracking[remote] != self._gitcurrentbranch():
1451 if tracking[remote] != self._gitcurrentbranch():
1437 checkout([tracking[remote]])
1452 checkout([tracking[remote]])
1438 self._gitcommand(['merge', '--ff', remote])
1453 self._gitcommand(['merge', '--ff', remote])
1439 _sanitize(self.ui, self._abspath, '.git')
1454 _sanitize(self.ui, self._abspath, '.git')
1440 else:
1455 else:
1441 # a real merge would be required, just checkout the revision
1456 # a real merge would be required, just checkout the revision
1442 rawcheckout()
1457 rawcheckout()
1443
1458
1444 @annotatesubrepoerror
1459 @annotatesubrepoerror
1445 def commit(self, text, user, date):
1460 def commit(self, text, user, date):
1446 if self._gitmissing():
1461 if self._gitmissing():
1447 raise util.Abort(_("subrepo %s is missing") % self._relpath)
1462 raise util.Abort(_("subrepo %s is missing") % self._relpath)
1448 cmd = ['commit', '-a', '-m', text]
1463 cmd = ['commit', '-a', '-m', text]
1449 env = os.environ.copy()
1464 env = os.environ.copy()
1450 if user:
1465 if user:
1451 cmd += ['--author', user]
1466 cmd += ['--author', user]
1452 if date:
1467 if date:
1453 # git's date parser silently ignores when seconds < 1e9
1468 # git's date parser silently ignores when seconds < 1e9
1454 # convert to ISO8601
1469 # convert to ISO8601
1455 env['GIT_AUTHOR_DATE'] = util.datestr(date,
1470 env['GIT_AUTHOR_DATE'] = util.datestr(date,
1456 '%Y-%m-%dT%H:%M:%S %1%2')
1471 '%Y-%m-%dT%H:%M:%S %1%2')
1457 self._gitcommand(cmd, env=env)
1472 self._gitcommand(cmd, env=env)
1458 # make sure commit works otherwise HEAD might not exist under certain
1473 # make sure commit works otherwise HEAD might not exist under certain
1459 # circumstances
1474 # circumstances
1460 return self._gitstate()
1475 return self._gitstate()
1461
1476
1462 @annotatesubrepoerror
1477 @annotatesubrepoerror
1463 def merge(self, state):
1478 def merge(self, state):
1464 source, revision, kind = state
1479 source, revision, kind = state
1465 self._fetch(source, revision)
1480 self._fetch(source, revision)
1466 base = self._gitcommand(['merge-base', revision, self._state[1]])
1481 base = self._gitcommand(['merge-base', revision, self._state[1]])
1467 self._gitupdatestat()
1482 self._gitupdatestat()
1468 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1483 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1469
1484
1470 def mergefunc():
1485 def mergefunc():
1471 if base == revision:
1486 if base == revision:
1472 self.get(state) # fast forward merge
1487 self.get(state) # fast forward merge
1473 elif base != self._state[1]:
1488 elif base != self._state[1]:
1474 self._gitcommand(['merge', '--no-commit', revision])
1489 self._gitcommand(['merge', '--no-commit', revision])
1475 _sanitize(self.ui, self._abspath, '.git')
1490 _sanitize(self.ui, self._abspath, '.git')
1476
1491
1477 if self.dirty():
1492 if self.dirty():
1478 if self._gitstate() != revision:
1493 if self._gitstate() != revision:
1479 dirty = self._gitstate() == self._state[1] or code != 0
1494 dirty = self._gitstate() == self._state[1] or code != 0
1480 if _updateprompt(self.ui, self, dirty,
1495 if _updateprompt(self.ui, self, dirty,
1481 self._state[1][:7], revision[:7]):
1496 self._state[1][:7], revision[:7]):
1482 mergefunc()
1497 mergefunc()
1483 else:
1498 else:
1484 mergefunc()
1499 mergefunc()
1485
1500
1486 @annotatesubrepoerror
1501 @annotatesubrepoerror
1487 def push(self, opts):
1502 def push(self, opts):
1488 force = opts.get('force')
1503 force = opts.get('force')
1489
1504
1490 if not self._state[1]:
1505 if not self._state[1]:
1491 return True
1506 return True
1492 if self._gitmissing():
1507 if self._gitmissing():
1493 raise util.Abort(_("subrepo %s is missing") % self._relpath)
1508 raise util.Abort(_("subrepo %s is missing") % self._relpath)
1494 # if a branch in origin contains the revision, nothing to do
1509 # if a branch in origin contains the revision, nothing to do
1495 branch2rev, rev2branch = self._gitbranchmap()
1510 branch2rev, rev2branch = self._gitbranchmap()
1496 if self._state[1] in rev2branch:
1511 if self._state[1] in rev2branch:
1497 for b in rev2branch[self._state[1]]:
1512 for b in rev2branch[self._state[1]]:
1498 if b.startswith('refs/remotes/origin/'):
1513 if b.startswith('refs/remotes/origin/'):
1499 return True
1514 return True
1500 for b, revision in branch2rev.iteritems():
1515 for b, revision in branch2rev.iteritems():
1501 if b.startswith('refs/remotes/origin/'):
1516 if b.startswith('refs/remotes/origin/'):
1502 if self._gitisancestor(self._state[1], revision):
1517 if self._gitisancestor(self._state[1], revision):
1503 return True
1518 return True
1504 # otherwise, try to push the currently checked out branch
1519 # otherwise, try to push the currently checked out branch
1505 cmd = ['push']
1520 cmd = ['push']
1506 if force:
1521 if force:
1507 cmd.append('--force')
1522 cmd.append('--force')
1508
1523
1509 current = self._gitcurrentbranch()
1524 current = self._gitcurrentbranch()
1510 if current:
1525 if current:
1511 # determine if the current branch is even useful
1526 # determine if the current branch is even useful
1512 if not self._gitisancestor(self._state[1], current):
1527 if not self._gitisancestor(self._state[1], current):
1513 self.ui.warn(_('unrelated git branch checked out '
1528 self.ui.warn(_('unrelated git branch checked out '
1514 'in subrepo %s\n') % self._relpath)
1529 'in subrepo %s\n') % self._relpath)
1515 return False
1530 return False
1516 self.ui.status(_('pushing branch %s of subrepo %s\n') %
1531 self.ui.status(_('pushing branch %s of subrepo %s\n') %
1517 (current.split('/', 2)[2], self._relpath))
1532 (current.split('/', 2)[2], self._relpath))
1518 ret = self._gitdir(cmd + ['origin', current])
1533 ret = self._gitdir(cmd + ['origin', current])
1519 return ret[1] == 0
1534 return ret[1] == 0
1520 else:
1535 else:
1521 self.ui.warn(_('no branch checked out in subrepo %s\n'
1536 self.ui.warn(_('no branch checked out in subrepo %s\n'
1522 'cannot push revision %s\n') %
1537 'cannot push revision %s\n') %
1523 (self._relpath, self._state[1]))
1538 (self._relpath, self._state[1]))
1524 return False
1539 return False
1525
1540
1526 @annotatesubrepoerror
1541 @annotatesubrepoerror
1527 def add(self, ui, match, prefix, explicitonly, **opts):
1542 def add(self, ui, match, prefix, explicitonly, **opts):
1528 if self._gitmissing():
1543 if self._gitmissing():
1529 return []
1544 return []
1530
1545
1531 (modified, added, removed,
1546 (modified, added, removed,
1532 deleted, unknown, ignored, clean) = self.status(None, unknown=True,
1547 deleted, unknown, ignored, clean) = self.status(None, unknown=True,
1533 clean=True)
1548 clean=True)
1534
1549
1535 tracked = set()
1550 tracked = set()
1536 # dirstates 'amn' warn, 'r' is added again
1551 # dirstates 'amn' warn, 'r' is added again
1537 for l in (modified, added, deleted, clean):
1552 for l in (modified, added, deleted, clean):
1538 tracked.update(l)
1553 tracked.update(l)
1539
1554
1540 # Unknown files not of interest will be rejected by the matcher
1555 # Unknown files not of interest will be rejected by the matcher
1541 files = unknown
1556 files = unknown
1542 files.extend(match.files())
1557 files.extend(match.files())
1543
1558
1544 rejected = []
1559 rejected = []
1545
1560
1546 files = [f for f in sorted(set(files)) if match(f)]
1561 files = [f for f in sorted(set(files)) if match(f)]
1547 for f in files:
1562 for f in files:
1548 exact = match.exact(f)
1563 exact = match.exact(f)
1549 command = ["add"]
1564 command = ["add"]
1550 if exact:
1565 if exact:
1551 command.append("-f") #should be added, even if ignored
1566 command.append("-f") #should be added, even if ignored
1552 if ui.verbose or not exact:
1567 if ui.verbose or not exact:
1553 ui.status(_('adding %s\n') % match.rel(f))
1568 ui.status(_('adding %s\n') % match.rel(f))
1554
1569
1555 if f in tracked: # hg prints 'adding' even if already tracked
1570 if f in tracked: # hg prints 'adding' even if already tracked
1556 if exact:
1571 if exact:
1557 rejected.append(f)
1572 rejected.append(f)
1558 continue
1573 continue
1559 if not opts.get('dry_run'):
1574 if not opts.get('dry_run'):
1560 self._gitcommand(command + [f])
1575 self._gitcommand(command + [f])
1561
1576
1562 for f in rejected:
1577 for f in rejected:
1563 ui.warn(_("%s already tracked!\n") % match.abs(f))
1578 ui.warn(_("%s already tracked!\n") % match.abs(f))
1564
1579
1565 return rejected
1580 return rejected
1566
1581
1567 @annotatesubrepoerror
1582 @annotatesubrepoerror
1568 def remove(self):
1583 def remove(self):
1569 if self._gitmissing():
1584 if self._gitmissing():
1570 return
1585 return
1571 if self.dirty():
1586 if self.dirty():
1572 self.ui.warn(_('not removing repo %s because '
1587 self.ui.warn(_('not removing repo %s because '
1573 'it has changes.\n') % self._relpath)
1588 'it has changes.\n') % self._relpath)
1574 return
1589 return
1575 # we can't fully delete the repository as it may contain
1590 # we can't fully delete the repository as it may contain
1576 # local-only history
1591 # local-only history
1577 self.ui.note(_('removing subrepo %s\n') % self._relpath)
1592 self.ui.note(_('removing subrepo %s\n') % self._relpath)
1578 self._gitcommand(['config', 'core.bare', 'true'])
1593 self._gitcommand(['config', 'core.bare', 'true'])
1579 for f in os.listdir(self._abspath):
1594 for f in os.listdir(self._abspath):
1580 if f == '.git':
1595 if f == '.git':
1581 continue
1596 continue
1582 path = os.path.join(self._abspath, f)
1597 path = os.path.join(self._abspath, f)
1583 if os.path.isdir(path) and not os.path.islink(path):
1598 if os.path.isdir(path) and not os.path.islink(path):
1584 shutil.rmtree(path)
1599 shutil.rmtree(path)
1585 else:
1600 else:
1586 os.remove(path)
1601 os.remove(path)
1587
1602
1588 def archive(self, archiver, prefix, match=None):
1603 def archive(self, archiver, prefix, match=None):
1589 total = 0
1604 total = 0
1590 source, revision = self._state
1605 source, revision = self._state
1591 if not revision:
1606 if not revision:
1592 return total
1607 return total
1593 self._fetch(source, revision)
1608 self._fetch(source, revision)
1594
1609
1595 # Parse git's native archive command.
1610 # Parse git's native archive command.
1596 # This should be much faster than manually traversing the trees
1611 # This should be much faster than manually traversing the trees
1597 # and objects with many subprocess calls.
1612 # and objects with many subprocess calls.
1598 tarstream = self._gitcommand(['archive', revision], stream=True)
1613 tarstream = self._gitcommand(['archive', revision], stream=True)
1599 tar = tarfile.open(fileobj=tarstream, mode='r|')
1614 tar = tarfile.open(fileobj=tarstream, mode='r|')
1600 relpath = subrelpath(self)
1615 relpath = subrelpath(self)
1601 self.ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files'))
1616 self.ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files'))
1602 for i, info in enumerate(tar):
1617 for i, info in enumerate(tar):
1603 if info.isdir():
1618 if info.isdir():
1604 continue
1619 continue
1605 if match and not match(info.name):
1620 if match and not match(info.name):
1606 continue
1621 continue
1607 if info.issym():
1622 if info.issym():
1608 data = info.linkname
1623 data = info.linkname
1609 else:
1624 else:
1610 data = tar.extractfile(info).read()
1625 data = tar.extractfile(info).read()
1611 archiver.addfile(os.path.join(prefix, self._path, info.name),
1626 archiver.addfile(os.path.join(prefix, self._path, info.name),
1612 info.mode, info.issym(), data)
1627 info.mode, info.issym(), data)
1613 total += 1
1628 total += 1
1614 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
1629 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
1615 unit=_('files'))
1630 unit=_('files'))
1616 self.ui.progress(_('archiving (%s)') % relpath, None)
1631 self.ui.progress(_('archiving (%s)') % relpath, None)
1617 return total
1632 return total
1618
1633
1619
1634
1620 @annotatesubrepoerror
1635 @annotatesubrepoerror
1621 def cat(self, match, prefix, **opts):
1636 def cat(self, match, prefix, **opts):
1622 rev = self._state[1]
1637 rev = self._state[1]
1623 if match.anypats():
1638 if match.anypats():
1624 return 1 #No support for include/exclude yet
1639 return 1 #No support for include/exclude yet
1625
1640
1626 if not match.files():
1641 if not match.files():
1627 return 1
1642 return 1
1628
1643
1629 for f in match.files():
1644 for f in match.files():
1630 output = self._gitcommand(["show", "%s:%s" % (rev, f)])
1645 output = self._gitcommand(["show", "%s:%s" % (rev, f)])
1631 fp = cmdutil.makefileobj(self._subparent, opts.get('output'),
1646 fp = cmdutil.makefileobj(self._subparent, opts.get('output'),
1632 self._ctx.node(),
1647 self._ctx.node(),
1633 pathname=os.path.join(prefix, f))
1648 pathname=os.path.join(prefix, f))
1634 fp.write(output)
1649 fp.write(output)
1635 fp.close()
1650 fp.close()
1636 return 0
1651 return 0
1637
1652
1638
1653
1639 @annotatesubrepoerror
1654 @annotatesubrepoerror
1640 def status(self, rev2, **opts):
1655 def status(self, rev2, **opts):
1641 rev1 = self._state[1]
1656 rev1 = self._state[1]
1642 if self._gitmissing() or not rev1:
1657 if self._gitmissing() or not rev1:
1643 # if the repo is missing, return no results
1658 # if the repo is missing, return no results
1644 return scmutil.status([], [], [], [], [], [], [])
1659 return scmutil.status([], [], [], [], [], [], [])
1645 modified, added, removed = [], [], []
1660 modified, added, removed = [], [], []
1646 self._gitupdatestat()
1661 self._gitupdatestat()
1647 if rev2:
1662 if rev2:
1648 command = ['diff-tree', rev1, rev2]
1663 command = ['diff-tree', rev1, rev2]
1649 else:
1664 else:
1650 command = ['diff-index', rev1]
1665 command = ['diff-index', rev1]
1651 out = self._gitcommand(command)
1666 out = self._gitcommand(command)
1652 for line in out.split('\n'):
1667 for line in out.split('\n'):
1653 tab = line.find('\t')
1668 tab = line.find('\t')
1654 if tab == -1:
1669 if tab == -1:
1655 continue
1670 continue
1656 status, f = line[tab - 1], line[tab + 1:]
1671 status, f = line[tab - 1], line[tab + 1:]
1657 if status == 'M':
1672 if status == 'M':
1658 modified.append(f)
1673 modified.append(f)
1659 elif status == 'A':
1674 elif status == 'A':
1660 added.append(f)
1675 added.append(f)
1661 elif status == 'D':
1676 elif status == 'D':
1662 removed.append(f)
1677 removed.append(f)
1663
1678
1664 deleted, unknown, ignored, clean = [], [], [], []
1679 deleted, unknown, ignored, clean = [], [], [], []
1665
1680
1666 command = ['status', '--porcelain', '-z']
1681 command = ['status', '--porcelain', '-z']
1667 if opts.get('unknown'):
1682 if opts.get('unknown'):
1668 command += ['--untracked-files=all']
1683 command += ['--untracked-files=all']
1669 if opts.get('ignored'):
1684 if opts.get('ignored'):
1670 command += ['--ignored']
1685 command += ['--ignored']
1671 out = self._gitcommand(command)
1686 out = self._gitcommand(command)
1672
1687
1673 changedfiles = set()
1688 changedfiles = set()
1674 changedfiles.update(modified)
1689 changedfiles.update(modified)
1675 changedfiles.update(added)
1690 changedfiles.update(added)
1676 changedfiles.update(removed)
1691 changedfiles.update(removed)
1677 for line in out.split('\0'):
1692 for line in out.split('\0'):
1678 if not line:
1693 if not line:
1679 continue
1694 continue
1680 st = line[0:2]
1695 st = line[0:2]
1681 #moves and copies show 2 files on one line
1696 #moves and copies show 2 files on one line
1682 if line.find('\0') >= 0:
1697 if line.find('\0') >= 0:
1683 filename1, filename2 = line[3:].split('\0')
1698 filename1, filename2 = line[3:].split('\0')
1684 else:
1699 else:
1685 filename1 = line[3:]
1700 filename1 = line[3:]
1686 filename2 = None
1701 filename2 = None
1687
1702
1688 changedfiles.add(filename1)
1703 changedfiles.add(filename1)
1689 if filename2:
1704 if filename2:
1690 changedfiles.add(filename2)
1705 changedfiles.add(filename2)
1691
1706
1692 if st == '??':
1707 if st == '??':
1693 unknown.append(filename1)
1708 unknown.append(filename1)
1694 elif st == '!!':
1709 elif st == '!!':
1695 ignored.append(filename1)
1710 ignored.append(filename1)
1696
1711
1697 if opts.get('clean'):
1712 if opts.get('clean'):
1698 out = self._gitcommand(['ls-files'])
1713 out = self._gitcommand(['ls-files'])
1699 for f in out.split('\n'):
1714 for f in out.split('\n'):
1700 if not f in changedfiles:
1715 if not f in changedfiles:
1701 clean.append(f)
1716 clean.append(f)
1702
1717
1703 return scmutil.status(modified, added, removed, deleted,
1718 return scmutil.status(modified, added, removed, deleted,
1704 unknown, ignored, clean)
1719 unknown, ignored, clean)
1705
1720
1706 @annotatesubrepoerror
1721 @annotatesubrepoerror
1707 def diff(self, ui, diffopts, node2, match, prefix, **opts):
1722 def diff(self, ui, diffopts, node2, match, prefix, **opts):
1708 node1 = self._state[1]
1723 node1 = self._state[1]
1709 cmd = ['diff']
1724 cmd = ['diff']
1710 if opts['stat']:
1725 if opts['stat']:
1711 cmd.append('--stat')
1726 cmd.append('--stat')
1712 else:
1727 else:
1713 # for Git, this also implies '-p'
1728 # for Git, this also implies '-p'
1714 cmd.append('-U%d' % diffopts.context)
1729 cmd.append('-U%d' % diffopts.context)
1715
1730
1716 gitprefix = os.path.join(prefix, self._path)
1731 gitprefix = os.path.join(prefix, self._path)
1717
1732
1718 if diffopts.noprefix:
1733 if diffopts.noprefix:
1719 cmd.extend(['--src-prefix=%s/' % gitprefix,
1734 cmd.extend(['--src-prefix=%s/' % gitprefix,
1720 '--dst-prefix=%s/' % gitprefix])
1735 '--dst-prefix=%s/' % gitprefix])
1721 else:
1736 else:
1722 cmd.extend(['--src-prefix=a/%s/' % gitprefix,
1737 cmd.extend(['--src-prefix=a/%s/' % gitprefix,
1723 '--dst-prefix=b/%s/' % gitprefix])
1738 '--dst-prefix=b/%s/' % gitprefix])
1724
1739
1725 if diffopts.ignorews:
1740 if diffopts.ignorews:
1726 cmd.append('--ignore-all-space')
1741 cmd.append('--ignore-all-space')
1727 if diffopts.ignorewsamount:
1742 if diffopts.ignorewsamount:
1728 cmd.append('--ignore-space-change')
1743 cmd.append('--ignore-space-change')
1729 if self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4) \
1744 if self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4) \
1730 and diffopts.ignoreblanklines:
1745 and diffopts.ignoreblanklines:
1731 cmd.append('--ignore-blank-lines')
1746 cmd.append('--ignore-blank-lines')
1732
1747
1733 cmd.append(node1)
1748 cmd.append(node1)
1734 if node2:
1749 if node2:
1735 cmd.append(node2)
1750 cmd.append(node2)
1736
1751
1737 if match.anypats():
1752 if match.anypats():
1738 return #No support for include/exclude yet
1753 return #No support for include/exclude yet
1739
1754
1740 output = ""
1755 output = ""
1741 if match.always():
1756 if match.always():
1742 output += self._gitcommand(cmd) + '\n'
1757 output += self._gitcommand(cmd) + '\n'
1743 elif match.files():
1758 elif match.files():
1744 for f in match.files():
1759 for f in match.files():
1745 output += self._gitcommand(cmd + [f]) + '\n'
1760 output += self._gitcommand(cmd + [f]) + '\n'
1746 elif match(gitprefix): #Subrepo is matched
1761 elif match(gitprefix): #Subrepo is matched
1747 output += self._gitcommand(cmd) + '\n'
1762 output += self._gitcommand(cmd) + '\n'
1748
1763
1749 if output.strip():
1764 if output.strip():
1750 ui.write(output)
1765 ui.write(output)
1751
1766
1752 @annotatesubrepoerror
1767 @annotatesubrepoerror
1753 def revert(self, substate, *pats, **opts):
1768 def revert(self, substate, *pats, **opts):
1754 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1769 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1755 if not opts.get('no_backup'):
1770 if not opts.get('no_backup'):
1756 status = self.status(None)
1771 status = self.status(None)
1757 names = status.modified
1772 names = status.modified
1758 for name in names:
1773 for name in names:
1759 bakname = "%s.orig" % name
1774 bakname = "%s.orig" % name
1760 self.ui.note(_('saving current version of %s as %s\n') %
1775 self.ui.note(_('saving current version of %s as %s\n') %
1761 (name, bakname))
1776 (name, bakname))
1762 util.rename(os.path.join(self._abspath, name),
1777 util.rename(os.path.join(self._abspath, name),
1763 os.path.join(self._abspath, bakname))
1778 os.path.join(self._abspath, bakname))
1764
1779
1765 if not opts.get('dry_run'):
1780 if not opts.get('dry_run'):
1766 self.get(substate, overwrite=True)
1781 self.get(substate, overwrite=True)
1767 return []
1782 return []
1768
1783
1769 def shortid(self, revid):
1784 def shortid(self, revid):
1770 return revid[:7]
1785 return revid[:7]
1771
1786
1772 types = {
1787 types = {
1773 'hg': hgsubrepo,
1788 'hg': hgsubrepo,
1774 'svn': svnsubrepo,
1789 'svn': svnsubrepo,
1775 'git': gitsubrepo,
1790 'git': gitsubrepo,
1776 }
1791 }
@@ -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, 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, 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
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, 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, 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
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,442 +1,461 b''
1 Preparing the subrepository 'sub2'
1 Preparing the subrepository 'sub2'
2
2
3 $ hg init sub2
3 $ hg init sub2
4 $ echo sub2 > sub2/sub2
4 $ echo sub2 > sub2/sub2
5 $ hg add -R sub2
5 $ hg add -R sub2
6 adding sub2/sub2 (glob)
6 adding sub2/sub2 (glob)
7 $ hg commit -R sub2 -m "sub2 import"
7 $ hg commit -R sub2 -m "sub2 import"
8
8
9 Preparing the 'sub1' repo which depends on the subrepo 'sub2'
9 Preparing the 'sub1' repo which depends on the subrepo 'sub2'
10
10
11 $ hg init sub1
11 $ hg init sub1
12 $ echo sub1 > sub1/sub1
12 $ echo sub1 > sub1/sub1
13 $ echo "sub2 = ../sub2" > sub1/.hgsub
13 $ echo "sub2 = ../sub2" > sub1/.hgsub
14 $ hg clone sub2 sub1/sub2
14 $ hg clone sub2 sub1/sub2
15 updating to branch default
15 updating to branch default
16 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
16 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
17 $ hg add -R sub1
17 $ hg add -R sub1
18 adding sub1/.hgsub (glob)
18 adding sub1/.hgsub (glob)
19 adding sub1/sub1 (glob)
19 adding sub1/sub1 (glob)
20 $ hg commit -R sub1 -m "sub1 import"
20 $ hg commit -R sub1 -m "sub1 import"
21
21
22 Preparing the 'main' repo which depends on the subrepo 'sub1'
22 Preparing the 'main' repo which depends on the subrepo 'sub1'
23
23
24 $ hg init main
24 $ hg init main
25 $ echo main > main/main
25 $ echo main > main/main
26 $ echo "sub1 = ../sub1" > main/.hgsub
26 $ echo "sub1 = ../sub1" > main/.hgsub
27 $ hg clone sub1 main/sub1
27 $ hg clone sub1 main/sub1
28 updating to branch default
28 updating to branch default
29 cloning subrepo sub2 from $TESTTMP/sub2
29 cloning subrepo sub2 from $TESTTMP/sub2
30 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
30 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
31 $ hg add -R main
31 $ hg add -R main
32 adding main/.hgsub (glob)
32 adding main/.hgsub (glob)
33 adding main/main (glob)
33 adding main/main (glob)
34 $ hg commit -R main -m "main import"
34 $ hg commit -R main -m "main import"
35
35
36 Cleaning both repositories, just as a clone -U
36 Cleaning both repositories, just as a clone -U
37
37
38 $ hg up -C -R sub2 null
38 $ hg up -C -R sub2 null
39 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
39 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
40 $ hg up -C -R sub1 null
40 $ hg up -C -R sub1 null
41 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
41 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
42 $ hg up -C -R main null
42 $ hg up -C -R main null
43 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
43 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
44 $ rm -rf main/sub1
44 $ rm -rf main/sub1
45 $ rm -rf sub1/sub2
45 $ rm -rf sub1/sub2
46
46
47 Clone main
47 Clone main
48
48
49 $ hg --config extensions.largefiles= clone main cloned
49 $ hg --config extensions.largefiles= clone main cloned
50 updating to branch default
50 updating to branch default
51 cloning subrepo sub1 from $TESTTMP/sub1
51 cloning subrepo sub1 from $TESTTMP/sub1
52 cloning subrepo sub1/sub2 from $TESTTMP/sub2 (glob)
52 cloning subrepo sub1/sub2 from $TESTTMP/sub2 (glob)
53 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
53 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
54
54
55 Largefiles is NOT enabled in the clone if the source repo doesn't require it
55 Largefiles is NOT enabled in the clone if the source repo doesn't require it
56 $ cat cloned/.hg/hgrc
56 $ cat cloned/.hg/hgrc
57 # example repository config (see "hg help config" for more info)
57 # example repository config (see "hg help config" for more info)
58 [paths]
58 [paths]
59 default = $TESTTMP/main (glob)
59 default = $TESTTMP/main (glob)
60
60
61 # path aliases to other clones of this repo in URLs or filesystem paths
61 # path aliases to other clones of this repo in URLs or filesystem paths
62 # (see "hg help config.paths" for more info)
62 # (see "hg help config.paths" for more info)
63 #
63 #
64 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
64 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
65 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
65 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
66 # my-clone = /home/jdoe/jdoes-clone
66 # my-clone = /home/jdoe/jdoes-clone
67
67
68 [ui]
68 [ui]
69 # name and email (local to this repository, optional), e.g.
69 # name and email (local to this repository, optional), e.g.
70 # username = Jane Doe <jdoe@example.com>
70 # username = Jane Doe <jdoe@example.com>
71
71
72 Checking cloned repo ids
72 Checking cloned repo ids
73
73
74 $ printf "cloned " ; hg id -R cloned
74 $ printf "cloned " ; hg id -R cloned
75 cloned 7f491f53a367 tip
75 cloned 7f491f53a367 tip
76 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
76 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
77 cloned/sub1 fc3b4ce2696f tip
77 cloned/sub1 fc3b4ce2696f tip
78 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
78 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
79 cloned/sub1/sub2 c57a0840e3ba tip
79 cloned/sub1/sub2 c57a0840e3ba tip
80
80
81 debugsub output for main and sub1
81 debugsub output for main and sub1
82
82
83 $ hg debugsub -R cloned
83 $ hg debugsub -R cloned
84 path sub1
84 path sub1
85 source ../sub1
85 source ../sub1
86 revision fc3b4ce2696f7741438c79207583768f2ce6b0dd
86 revision fc3b4ce2696f7741438c79207583768f2ce6b0dd
87 $ hg debugsub -R cloned/sub1
87 $ hg debugsub -R cloned/sub1
88 path sub2
88 path sub2
89 source ../sub2
89 source ../sub2
90 revision c57a0840e3badd667ef3c3ef65471609acb2ba3c
90 revision c57a0840e3badd667ef3c3ef65471609acb2ba3c
91
91
92 Modifying deeply nested 'sub2'
92 Modifying deeply nested 'sub2'
93
93
94 $ echo modified > cloned/sub1/sub2/sub2
94 $ echo modified > cloned/sub1/sub2/sub2
95 $ hg commit --subrepos -m "deep nested modif should trigger a commit" -R cloned
95 $ hg commit --subrepos -m "deep nested modif should trigger a commit" -R cloned
96 committing subrepository sub1
96 committing subrepository sub1
97 committing subrepository sub1/sub2 (glob)
97 committing subrepository sub1/sub2 (glob)
98
98
99 Checking modified node ids
99 Checking modified node ids
100
100
101 $ printf "cloned " ; hg id -R cloned
101 $ printf "cloned " ; hg id -R cloned
102 cloned ffe6649062fe tip
102 cloned ffe6649062fe tip
103 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
103 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
104 cloned/sub1 2ecb03bf44a9 tip
104 cloned/sub1 2ecb03bf44a9 tip
105 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
105 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
106 cloned/sub1/sub2 53dd3430bcaf tip
106 cloned/sub1/sub2 53dd3430bcaf tip
107
107
108 debugsub output for main and sub1
108 debugsub output for main and sub1
109
109
110 $ hg debugsub -R cloned
110 $ hg debugsub -R cloned
111 path sub1
111 path sub1
112 source ../sub1
112 source ../sub1
113 revision 2ecb03bf44a94e749e8669481dd9069526ce7cb9
113 revision 2ecb03bf44a94e749e8669481dd9069526ce7cb9
114 $ hg debugsub -R cloned/sub1
114 $ hg debugsub -R cloned/sub1
115 path sub2
115 path sub2
116 source ../sub2
116 source ../sub2
117 revision 53dd3430bcaf5ab4a7c48262bcad6d441f510487
117 revision 53dd3430bcaf5ab4a7c48262bcad6d441f510487
118
118
119 Check that deep archiving works
119 Check that deep archiving works
120
120
121 $ cd cloned
121 $ cd cloned
122 $ echo 'test' > sub1/sub2/test.txt
122 $ echo 'test' > sub1/sub2/test.txt
123 $ hg --config extensions.largefiles=! add sub1/sub2/test.txt
123 $ hg --config extensions.largefiles=! add sub1/sub2/test.txt
124 $ mkdir sub1/sub2/folder
124 $ mkdir sub1/sub2/folder
125 $ echo 'subfolder' > sub1/sub2/folder/test.txt
125 $ echo 'subfolder' > sub1/sub2/folder/test.txt
126 $ hg ci -ASm "add test.txt"
126 $ hg ci -ASm "add test.txt"
127 adding sub1/sub2/folder/test.txt
127 adding sub1/sub2/folder/test.txt
128 committing subrepository sub1
128 committing subrepository sub1
129 committing subrepository sub1/sub2 (glob)
129 committing subrepository sub1/sub2 (glob)
130
130
131 .. but first take a detour through some deep removal testing
131 .. but first take a detour through some deep removal testing
132
132
133 $ hg remove -S -I 're:.*.txt' .
133 $ hg remove -S -I 're:.*.txt' .
134 removing sub1/sub2/folder/test.txt (glob)
134 removing sub1/sub2/folder/test.txt (glob)
135 removing sub1/sub2/test.txt (glob)
135 removing sub1/sub2/test.txt (glob)
136 $ hg status -S
136 $ hg status -S
137 R sub1/sub2/folder/test.txt
137 R sub1/sub2/folder/test.txt
138 R sub1/sub2/test.txt
138 R sub1/sub2/test.txt
139 $ hg update -Cq
139 $ hg update -Cq
140 $ hg remove -I 're:.*.txt' sub1
140 $ hg remove -I 're:.*.txt' sub1
141 $ hg status -S
141 $ hg status -S
142 $ hg remove sub1/sub2/folder/test.txt
142 $ hg remove sub1/sub2/folder/test.txt
143 $ hg remove sub1/.hgsubstate
143 $ hg remove sub1/.hgsubstate
144 $ hg status -S
144 $ hg status -S
145 R sub1/.hgsubstate
145 R sub1/.hgsubstate
146 R sub1/sub2/folder/test.txt
146 R sub1/sub2/folder/test.txt
147 $ hg update -Cq
147 $ hg update -Cq
148 $ touch sub1/foo
148 $ touch sub1/foo
149 $ hg forget sub1/sub2/folder/test.txt
149 $ hg forget sub1/sub2/folder/test.txt
150 $ rm sub1/sub2/test.txt
150 $ rm sub1/sub2/test.txt
151
151
152 Test relative path printing + subrepos
152 Test relative path printing + subrepos
153 $ mkdir -p foo/bar
153 $ mkdir -p foo/bar
154 $ cd foo
154 $ cd foo
155 $ touch bar/abc
155 $ touch bar/abc
156 $ hg addremove -S ..
156 $ hg addremove -S ..
157 adding ../sub1/sub2/folder/test.txt (glob)
157 adding ../sub1/sub2/folder/test.txt (glob)
158 removing ../sub1/sub2/test.txt (glob)
158 removing ../sub1/sub2/test.txt (glob)
159 adding ../sub1/foo (glob)
159 adding ../sub1/foo (glob)
160 adding bar/abc (glob)
160 adding bar/abc (glob)
161 $ cd ..
161 $ cd ..
162 $ hg status -S
162 $ hg status -S
163 A foo/bar/abc
163 A foo/bar/abc
164 A sub1/foo
164 A sub1/foo
165 R sub1/sub2/test.txt
165 R sub1/sub2/test.txt
166 $ hg update -Cq
166 $ hg update -Cq
167 $ touch sub1/sub2/folder/bar
167 $ touch sub1/sub2/folder/bar
168 $ hg addremove sub1/sub2
168 $ hg addremove sub1/sub2
169 adding sub1/sub2/folder/bar (glob)
169 adding sub1/sub2/folder/bar (glob)
170 $ hg status -S
170 $ hg status -S
171 A sub1/sub2/folder/bar
171 A sub1/sub2/folder/bar
172 ? foo/bar/abc
172 ? foo/bar/abc
173 ? sub1/foo
173 ? sub1/foo
174 $ hg update -Cq
174 $ hg update -Cq
175 $ hg addremove sub1
175 $ hg addremove sub1
176 adding sub1/sub2/folder/bar (glob)
176 adding sub1/sub2/folder/bar (glob)
177 adding sub1/foo (glob)
177 adding sub1/foo (glob)
178 $ hg update -Cq
178 $ hg update -Cq
179 $ rm sub1/sub2/folder/test.txt
179 $ rm sub1/sub2/folder/test.txt
180 $ rm sub1/sub2/test.txt
180 $ rm sub1/sub2/test.txt
181 $ hg ci -ASm "remove test.txt"
181 $ hg ci -ASm "remove test.txt"
182 adding sub1/sub2/folder/bar
182 adding sub1/sub2/folder/bar
183 removing sub1/sub2/folder/test.txt
183 removing sub1/sub2/folder/test.txt
184 removing sub1/sub2/test.txt
184 removing sub1/sub2/test.txt
185 adding sub1/foo
185 adding sub1/foo
186 adding foo/bar/abc
186 adding foo/bar/abc
187 committing subrepository sub1
187 committing subrepository sub1
188 committing subrepository sub1/sub2 (glob)
188 committing subrepository sub1/sub2 (glob)
189
190 $ hg forget sub1/sub2/sub2
191 $ echo x > sub1/sub2/x.txt
192 $ hg add sub1/sub2/x.txt
193
194 Files sees uncommitted adds and removes in subrepos
195 $ hg files -S
196 .hgsub
197 .hgsubstate
198 foo/bar/abc (glob)
199 main
200 sub1/.hgsub (glob)
201 sub1/.hgsubstate (glob)
202 sub1/foo (glob)
203 sub1/sub1 (glob)
204 sub1/sub2/folder/bar (glob)
205 sub1/sub2/x.txt (glob)
206
189 $ hg rollback -q
207 $ hg rollback -q
190 $ hg up -Cq
208 $ hg up -Cq
191
209
192 $ hg --config extensions.largefiles=! archive -S ../archive_all
210 $ hg --config extensions.largefiles=! archive -S ../archive_all
193 $ find ../archive_all | sort
211 $ find ../archive_all | sort
194 ../archive_all
212 ../archive_all
195 ../archive_all/.hg_archival.txt
213 ../archive_all/.hg_archival.txt
196 ../archive_all/.hgsub
214 ../archive_all/.hgsub
197 ../archive_all/.hgsubstate
215 ../archive_all/.hgsubstate
198 ../archive_all/main
216 ../archive_all/main
199 ../archive_all/sub1
217 ../archive_all/sub1
200 ../archive_all/sub1/.hgsub
218 ../archive_all/sub1/.hgsub
201 ../archive_all/sub1/.hgsubstate
219 ../archive_all/sub1/.hgsubstate
202 ../archive_all/sub1/sub1
220 ../archive_all/sub1/sub1
203 ../archive_all/sub1/sub2
221 ../archive_all/sub1/sub2
204 ../archive_all/sub1/sub2/folder
222 ../archive_all/sub1/sub2/folder
205 ../archive_all/sub1/sub2/folder/test.txt
223 ../archive_all/sub1/sub2/folder/test.txt
206 ../archive_all/sub1/sub2/sub2
224 ../archive_all/sub1/sub2/sub2
207 ../archive_all/sub1/sub2/test.txt
225 ../archive_all/sub1/sub2/test.txt
208
226
209 Check that archive -X works in deep subrepos
227 Check that archive -X works in deep subrepos
210
228
211 $ hg --config extensions.largefiles=! archive -S -X '**test*' ../archive_exclude
229 $ hg --config extensions.largefiles=! archive -S -X '**test*' ../archive_exclude
212 $ find ../archive_exclude | sort
230 $ find ../archive_exclude | sort
213 ../archive_exclude
231 ../archive_exclude
214 ../archive_exclude/.hg_archival.txt
232 ../archive_exclude/.hg_archival.txt
215 ../archive_exclude/.hgsub
233 ../archive_exclude/.hgsub
216 ../archive_exclude/.hgsubstate
234 ../archive_exclude/.hgsubstate
217 ../archive_exclude/main
235 ../archive_exclude/main
218 ../archive_exclude/sub1
236 ../archive_exclude/sub1
219 ../archive_exclude/sub1/.hgsub
237 ../archive_exclude/sub1/.hgsub
220 ../archive_exclude/sub1/.hgsubstate
238 ../archive_exclude/sub1/.hgsubstate
221 ../archive_exclude/sub1/sub1
239 ../archive_exclude/sub1/sub1
222 ../archive_exclude/sub1/sub2
240 ../archive_exclude/sub1/sub2
223 ../archive_exclude/sub1/sub2/sub2
241 ../archive_exclude/sub1/sub2/sub2
224
242
225 $ hg --config extensions.largefiles=! archive -S -I '**test*' ../archive_include
243 $ hg --config extensions.largefiles=! archive -S -I '**test*' ../archive_include
226 $ find ../archive_include | sort
244 $ find ../archive_include | sort
227 ../archive_include
245 ../archive_include
228 ../archive_include/sub1
246 ../archive_include/sub1
229 ../archive_include/sub1/sub2
247 ../archive_include/sub1/sub2
230 ../archive_include/sub1/sub2/folder
248 ../archive_include/sub1/sub2/folder
231 ../archive_include/sub1/sub2/folder/test.txt
249 ../archive_include/sub1/sub2/folder/test.txt
232 ../archive_include/sub1/sub2/test.txt
250 ../archive_include/sub1/sub2/test.txt
233
251
234 Check that deep archive works with largefiles (which overrides hgsubrepo impl)
252 Check that deep archive works with largefiles (which overrides hgsubrepo impl)
235 This also tests the repo.ui regression in 43fb170a23bd, and that lf subrepo
253 This also tests the repo.ui regression in 43fb170a23bd, and that lf subrepo
236 subrepos are archived properly.
254 subrepos are archived properly.
237 Note that add --large through a subrepo currently adds the file as a normal file
255 Note that add --large through a subrepo currently adds the file as a normal file
238
256
239 $ echo "large" > sub1/sub2/large.bin
257 $ echo "large" > sub1/sub2/large.bin
240 $ hg --config extensions.largefiles= add --large -R sub1/sub2 sub1/sub2/large.bin
258 $ hg --config extensions.largefiles= add --large -R sub1/sub2 sub1/sub2/large.bin
241 $ echo "large" > large.bin
259 $ echo "large" > large.bin
242 $ hg --config extensions.largefiles= add --large large.bin
260 $ hg --config extensions.largefiles= add --large large.bin
243 $ hg --config extensions.largefiles= ci -S -m "add large files"
261 $ hg --config extensions.largefiles= ci -S -m "add large files"
244 committing subrepository sub1
262 committing subrepository sub1
245 committing subrepository sub1/sub2 (glob)
263 committing subrepository sub1/sub2 (glob)
246
264
247 $ hg --config extensions.largefiles= archive -S ../archive_lf
265 $ hg --config extensions.largefiles= archive -S ../archive_lf
248 $ find ../archive_lf | sort
266 $ find ../archive_lf | sort
249 ../archive_lf
267 ../archive_lf
250 ../archive_lf/.hg_archival.txt
268 ../archive_lf/.hg_archival.txt
251 ../archive_lf/.hgsub
269 ../archive_lf/.hgsub
252 ../archive_lf/.hgsubstate
270 ../archive_lf/.hgsubstate
253 ../archive_lf/large.bin
271 ../archive_lf/large.bin
254 ../archive_lf/main
272 ../archive_lf/main
255 ../archive_lf/sub1
273 ../archive_lf/sub1
256 ../archive_lf/sub1/.hgsub
274 ../archive_lf/sub1/.hgsub
257 ../archive_lf/sub1/.hgsubstate
275 ../archive_lf/sub1/.hgsubstate
258 ../archive_lf/sub1/sub1
276 ../archive_lf/sub1/sub1
259 ../archive_lf/sub1/sub2
277 ../archive_lf/sub1/sub2
260 ../archive_lf/sub1/sub2/folder
278 ../archive_lf/sub1/sub2/folder
261 ../archive_lf/sub1/sub2/folder/test.txt
279 ../archive_lf/sub1/sub2/folder/test.txt
262 ../archive_lf/sub1/sub2/large.bin
280 ../archive_lf/sub1/sub2/large.bin
263 ../archive_lf/sub1/sub2/sub2
281 ../archive_lf/sub1/sub2/sub2
264 ../archive_lf/sub1/sub2/test.txt
282 ../archive_lf/sub1/sub2/test.txt
265 $ rm -rf ../archive_lf
283 $ rm -rf ../archive_lf
266
284
267 Exclude large files from main and sub-sub repo
285 Exclude large files from main and sub-sub repo
268
286
269 $ hg --config extensions.largefiles= archive -S -X '**.bin' ../archive_lf
287 $ hg --config extensions.largefiles= archive -S -X '**.bin' ../archive_lf
270 $ find ../archive_lf | sort
288 $ find ../archive_lf | sort
271 ../archive_lf
289 ../archive_lf
272 ../archive_lf/.hg_archival.txt
290 ../archive_lf/.hg_archival.txt
273 ../archive_lf/.hgsub
291 ../archive_lf/.hgsub
274 ../archive_lf/.hgsubstate
292 ../archive_lf/.hgsubstate
275 ../archive_lf/main
293 ../archive_lf/main
276 ../archive_lf/sub1
294 ../archive_lf/sub1
277 ../archive_lf/sub1/.hgsub
295 ../archive_lf/sub1/.hgsub
278 ../archive_lf/sub1/.hgsubstate
296 ../archive_lf/sub1/.hgsubstate
279 ../archive_lf/sub1/sub1
297 ../archive_lf/sub1/sub1
280 ../archive_lf/sub1/sub2
298 ../archive_lf/sub1/sub2
281 ../archive_lf/sub1/sub2/folder
299 ../archive_lf/sub1/sub2/folder
282 ../archive_lf/sub1/sub2/folder/test.txt
300 ../archive_lf/sub1/sub2/folder/test.txt
283 ../archive_lf/sub1/sub2/sub2
301 ../archive_lf/sub1/sub2/sub2
284 ../archive_lf/sub1/sub2/test.txt
302 ../archive_lf/sub1/sub2/test.txt
285 $ rm -rf ../archive_lf
303 $ rm -rf ../archive_lf
286
304
287 Exclude normal files from main and sub-sub repo
305 Exclude normal files from main and sub-sub repo
288
306
289 $ hg --config extensions.largefiles= archive -S -X '**.txt' ../archive_lf
307 $ hg --config extensions.largefiles= archive -S -X '**.txt' ../archive_lf
290 $ find ../archive_lf | sort
308 $ find ../archive_lf | sort
291 ../archive_lf
309 ../archive_lf
292 ../archive_lf/.hgsub
310 ../archive_lf/.hgsub
293 ../archive_lf/.hgsubstate
311 ../archive_lf/.hgsubstate
294 ../archive_lf/large.bin
312 ../archive_lf/large.bin
295 ../archive_lf/main
313 ../archive_lf/main
296 ../archive_lf/sub1
314 ../archive_lf/sub1
297 ../archive_lf/sub1/.hgsub
315 ../archive_lf/sub1/.hgsub
298 ../archive_lf/sub1/.hgsubstate
316 ../archive_lf/sub1/.hgsubstate
299 ../archive_lf/sub1/sub1
317 ../archive_lf/sub1/sub1
300 ../archive_lf/sub1/sub2
318 ../archive_lf/sub1/sub2
301 ../archive_lf/sub1/sub2/large.bin
319 ../archive_lf/sub1/sub2/large.bin
302 ../archive_lf/sub1/sub2/sub2
320 ../archive_lf/sub1/sub2/sub2
303 $ rm -rf ../archive_lf
321 $ rm -rf ../archive_lf
304
322
305 Include normal files from within a largefiles subrepo
323 Include normal files from within a largefiles subrepo
306
324
307 $ hg --config extensions.largefiles= archive -S -I '**.txt' ../archive_lf
325 $ hg --config extensions.largefiles= archive -S -I '**.txt' ../archive_lf
308 $ find ../archive_lf | sort
326 $ find ../archive_lf | sort
309 ../archive_lf
327 ../archive_lf
310 ../archive_lf/.hg_archival.txt
328 ../archive_lf/.hg_archival.txt
311 ../archive_lf/sub1
329 ../archive_lf/sub1
312 ../archive_lf/sub1/sub2
330 ../archive_lf/sub1/sub2
313 ../archive_lf/sub1/sub2/folder
331 ../archive_lf/sub1/sub2/folder
314 ../archive_lf/sub1/sub2/folder/test.txt
332 ../archive_lf/sub1/sub2/folder/test.txt
315 ../archive_lf/sub1/sub2/test.txt
333 ../archive_lf/sub1/sub2/test.txt
316 $ rm -rf ../archive_lf
334 $ rm -rf ../archive_lf
317
335
318 Include large files from within a largefiles subrepo
336 Include large files from within a largefiles subrepo
319
337
320 $ hg --config extensions.largefiles= archive -S -I '**.bin' ../archive_lf
338 $ hg --config extensions.largefiles= archive -S -I '**.bin' ../archive_lf
321 $ find ../archive_lf | sort
339 $ find ../archive_lf | sort
322 ../archive_lf
340 ../archive_lf
323 ../archive_lf/large.bin
341 ../archive_lf/large.bin
324 ../archive_lf/sub1
342 ../archive_lf/sub1
325 ../archive_lf/sub1/sub2
343 ../archive_lf/sub1/sub2
326 ../archive_lf/sub1/sub2/large.bin
344 ../archive_lf/sub1/sub2/large.bin
327 $ rm -rf ../archive_lf
345 $ rm -rf ../archive_lf
328
346
329 Find an exact largefile match in a largefiles subrepo
347 Find an exact largefile match in a largefiles subrepo
330
348
331 $ hg --config extensions.largefiles= archive -S -I 'sub1/sub2/large.bin' ../archive_lf
349 $ hg --config extensions.largefiles= archive -S -I 'sub1/sub2/large.bin' ../archive_lf
332 $ find ../archive_lf | sort
350 $ find ../archive_lf | sort
333 ../archive_lf
351 ../archive_lf
334 ../archive_lf/sub1
352 ../archive_lf/sub1
335 ../archive_lf/sub1/sub2
353 ../archive_lf/sub1/sub2
336 ../archive_lf/sub1/sub2/large.bin
354 ../archive_lf/sub1/sub2/large.bin
337 $ rm -rf ../archive_lf
355 $ rm -rf ../archive_lf
338
356
339 The local repo enables largefiles if a largefiles repo is cloned
357 The local repo enables largefiles if a largefiles repo is cloned
340 $ hg showconfig extensions
358 $ hg showconfig extensions
341 abort: repository requires features unknown to this Mercurial: largefiles!
359 abort: repository requires features unknown to this Mercurial: largefiles!
342 (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
360 (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
343 [255]
361 [255]
344 $ hg --config extensions.largefiles= clone -qU . ../lfclone
362 $ hg --config extensions.largefiles= clone -qU . ../lfclone
345 $ cat ../lfclone/.hg/hgrc
363 $ cat ../lfclone/.hg/hgrc
346 # example repository config (see "hg help config" for more info)
364 # example repository config (see "hg help config" for more info)
347 [paths]
365 [paths]
348 default = $TESTTMP/cloned (glob)
366 default = $TESTTMP/cloned (glob)
349
367
350 # path aliases to other clones of this repo in URLs or filesystem paths
368 # path aliases to other clones of this repo in URLs or filesystem paths
351 # (see "hg help config.paths" for more info)
369 # (see "hg help config.paths" for more info)
352 #
370 #
353 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
371 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
354 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
372 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
355 # my-clone = /home/jdoe/jdoes-clone
373 # my-clone = /home/jdoe/jdoes-clone
356
374
357 [ui]
375 [ui]
358 # name and email (local to this repository, optional), e.g.
376 # name and email (local to this repository, optional), e.g.
359 # username = Jane Doe <jdoe@example.com>
377 # username = Jane Doe <jdoe@example.com>
360
378
361 [extensions]
379 [extensions]
362 largefiles=
380 largefiles=
363
381
364 Find an exact match to a standin (should archive nothing)
382 Find an exact match to a standin (should archive nothing)
365 $ hg --config extensions.largefiles= archive -S -I 'sub/sub2/.hglf/large.bin' ../archive_lf
383 $ hg --config extensions.largefiles= archive -S -I 'sub/sub2/.hglf/large.bin' ../archive_lf
366 $ find ../archive_lf 2> /dev/null | sort
384 $ find ../archive_lf 2> /dev/null | sort
367
385
368 $ cat >> $HGRCPATH <<EOF
386 $ cat >> $HGRCPATH <<EOF
369 > [extensions]
387 > [extensions]
370 > largefiles=
388 > largefiles=
371 > [largefiles]
389 > [largefiles]
372 > patterns=glob:**.dat
390 > patterns=glob:**.dat
373 > EOF
391 > EOF
374
392
375 Test forget through a deep subrepo with the largefiles extension, both a
393 Test forget through a deep subrepo with the largefiles extension, both a
376 largefile and a normal file. Then a largefile that hasn't been committed yet.
394 largefile and a normal file. Then a largefile that hasn't been committed yet.
377 $ touch sub1/sub2/untracked.txt
395 $ touch sub1/sub2/untracked.txt
378 $ touch sub1/sub2/large.dat
396 $ touch sub1/sub2/large.dat
379 $ hg forget sub1/sub2/large.bin sub1/sub2/test.txt sub1/sub2/untracked.txt
397 $ hg forget sub1/sub2/large.bin sub1/sub2/test.txt sub1/sub2/untracked.txt
380 not removing sub1/sub2/untracked.txt: file is already untracked (glob)
398 not removing sub1/sub2/untracked.txt: file is already untracked (glob)
381 [1]
399 [1]
382 $ hg add --large --dry-run -v sub1/sub2/untracked.txt
400 $ hg add --large --dry-run -v sub1/sub2/untracked.txt
383 adding sub1/sub2/untracked.txt as a largefile (glob)
401 adding sub1/sub2/untracked.txt as a largefile (glob)
384 $ hg add --large -v sub1/sub2/untracked.txt
402 $ hg add --large -v sub1/sub2/untracked.txt
385 adding sub1/sub2/untracked.txt as a largefile (glob)
403 adding sub1/sub2/untracked.txt as a largefile (glob)
386 $ hg add --normal -v sub1/sub2/large.dat
404 $ hg add --normal -v sub1/sub2/large.dat
387 adding sub1/sub2/large.dat (glob)
405 adding sub1/sub2/large.dat (glob)
388 $ hg forget -v sub1/sub2/untracked.txt
406 $ hg forget -v sub1/sub2/untracked.txt
389 removing sub1/sub2/untracked.txt (glob)
407 removing sub1/sub2/untracked.txt (glob)
390 $ hg status -S
408 $ hg status -S
391 A sub1/sub2/large.dat
409 A sub1/sub2/large.dat
392 R sub1/sub2/large.bin
410 R sub1/sub2/large.bin
393 R sub1/sub2/test.txt
411 R sub1/sub2/test.txt
394 ? foo/bar/abc
412 ? foo/bar/abc
395 ? sub1/sub2/untracked.txt
413 ? sub1/sub2/untracked.txt
414 ? sub1/sub2/x.txt
396 $ hg add sub1/sub2
415 $ hg add sub1/sub2
397 $ hg ci -Sqm 'forget testing'
416 $ hg ci -Sqm 'forget testing'
398
417
399 Test issue4330: commit a directory where only normal files have changed
418 Test issue4330: commit a directory where only normal files have changed
400 $ touch foo/bar/large.dat
419 $ touch foo/bar/large.dat
401 $ hg add --large foo/bar/large.dat
420 $ hg add --large foo/bar/large.dat
402 $ hg ci -m 'add foo/bar/large.dat'
421 $ hg ci -m 'add foo/bar/large.dat'
403 $ touch a.txt
422 $ touch a.txt
404 $ touch a.dat
423 $ touch a.dat
405 $ hg add -v foo/bar/abc a.txt a.dat
424 $ hg add -v foo/bar/abc a.txt a.dat
406 adding a.dat as a largefile
425 adding a.dat as a largefile
407 adding a.txt
426 adding a.txt
408 adding foo/bar/abc (glob)
427 adding foo/bar/abc (glob)
409 $ hg ci -m 'dir commit with only normal file deltas' foo/bar
428 $ hg ci -m 'dir commit with only normal file deltas' foo/bar
410 $ hg status
429 $ hg status
411 A a.dat
430 A a.dat
412 A a.txt
431 A a.txt
413
432
414 Test a directory commit with a changed largefile and a changed normal file
433 Test a directory commit with a changed largefile and a changed normal file
415 $ echo changed > foo/bar/large.dat
434 $ echo changed > foo/bar/large.dat
416 $ echo changed > foo/bar/abc
435 $ echo changed > foo/bar/abc
417 $ hg ci -m 'dir commit with normal and lf file deltas' foo
436 $ hg ci -m 'dir commit with normal and lf file deltas' foo
418 $ hg status
437 $ hg status
419 A a.dat
438 A a.dat
420 A a.txt
439 A a.txt
421
440
422 $ hg ci -m "add a.*"
441 $ hg ci -m "add a.*"
423 $ hg mv a.dat b.dat
442 $ hg mv a.dat b.dat
424 $ hg mv foo/bar/abc foo/bar/def
443 $ hg mv foo/bar/abc foo/bar/def
425 $ hg status -C
444 $ hg status -C
426 A b.dat
445 A b.dat
427 a.dat
446 a.dat
428 A foo/bar/def
447 A foo/bar/def
429 foo/bar/abc
448 foo/bar/abc
430 R a.dat
449 R a.dat
431 R foo/bar/abc
450 R foo/bar/abc
432
451
433 $ hg ci -m "move large and normal"
452 $ hg ci -m "move large and normal"
434 $ hg status -C --rev '.^' --rev .
453 $ hg status -C --rev '.^' --rev .
435 A b.dat
454 A b.dat
436 a.dat
455 a.dat
437 A foo/bar/def
456 A foo/bar/def
438 foo/bar/abc
457 foo/bar/abc
439 R a.dat
458 R a.dat
440 R foo/bar/abc
459 R foo/bar/abc
441
460
442 $ cd ..
461 $ cd ..
@@ -1,1613 +1,1618 b''
1 Let commit recurse into subrepos by default to match pre-2.0 behavior:
1 Let commit recurse into subrepos by default to match pre-2.0 behavior:
2
2
3 $ echo "[ui]" >> $HGRCPATH
3 $ echo "[ui]" >> $HGRCPATH
4 $ echo "commitsubrepos = Yes" >> $HGRCPATH
4 $ echo "commitsubrepos = Yes" >> $HGRCPATH
5
5
6 $ hg init t
6 $ hg init t
7 $ cd t
7 $ cd t
8
8
9 first revision, no sub
9 first revision, no sub
10
10
11 $ echo a > a
11 $ echo a > a
12 $ hg ci -Am0
12 $ hg ci -Am0
13 adding a
13 adding a
14
14
15 add first sub
15 add first sub
16
16
17 $ echo s = s > .hgsub
17 $ echo s = s > .hgsub
18 $ hg add .hgsub
18 $ hg add .hgsub
19 $ hg init s
19 $ hg init s
20 $ echo a > s/a
20 $ echo a > s/a
21
21
22 Issue2232: committing a subrepo without .hgsub
22 Issue2232: committing a subrepo without .hgsub
23
23
24 $ hg ci -mbad s
24 $ hg ci -mbad s
25 abort: can't commit subrepos without .hgsub
25 abort: can't commit subrepos without .hgsub
26 [255]
26 [255]
27
27
28 $ hg -R s add s/a
29 $ hg files -S
30 .hgsub
31 a
32 s/a (glob)
33
28 $ hg -R s ci -Ams0
34 $ hg -R s ci -Ams0
29 adding a
30 $ hg sum
35 $ hg sum
31 parent: 0:f7b1eb17ad24 tip
36 parent: 0:f7b1eb17ad24 tip
32 0
37 0
33 branch: default
38 branch: default
34 commit: 1 added, 1 subrepos
39 commit: 1 added, 1 subrepos
35 update: (current)
40 update: (current)
36 $ hg ci -m1
41 $ hg ci -m1
37
42
38 test handling .hgsubstate "added" explicitly.
43 test handling .hgsubstate "added" explicitly.
39
44
40 $ hg parents --template '{node}\n{files}\n'
45 $ hg parents --template '{node}\n{files}\n'
41 7cf8cfea66e410e8e3336508dfeec07b3192de51
46 7cf8cfea66e410e8e3336508dfeec07b3192de51
42 .hgsub .hgsubstate
47 .hgsub .hgsubstate
43 $ hg rollback -q
48 $ hg rollback -q
44 $ hg add .hgsubstate
49 $ hg add .hgsubstate
45 $ hg ci -m1
50 $ hg ci -m1
46 $ hg parents --template '{node}\n{files}\n'
51 $ hg parents --template '{node}\n{files}\n'
47 7cf8cfea66e410e8e3336508dfeec07b3192de51
52 7cf8cfea66e410e8e3336508dfeec07b3192de51
48 .hgsub .hgsubstate
53 .hgsub .hgsubstate
49
54
50 Revert subrepo and test subrepo fileset keyword:
55 Revert subrepo and test subrepo fileset keyword:
51
56
52 $ echo b > s/a
57 $ echo b > s/a
53 $ hg revert --dry-run "set:subrepo('glob:s*')"
58 $ hg revert --dry-run "set:subrepo('glob:s*')"
54 reverting subrepo s
59 reverting subrepo s
55 reverting s/a (glob)
60 reverting s/a (glob)
56 $ cat s/a
61 $ cat s/a
57 b
62 b
58 $ hg revert "set:subrepo('glob:s*')"
63 $ hg revert "set:subrepo('glob:s*')"
59 reverting subrepo s
64 reverting subrepo s
60 reverting s/a (glob)
65 reverting s/a (glob)
61 $ cat s/a
66 $ cat s/a
62 a
67 a
63 $ rm s/a.orig
68 $ rm s/a.orig
64
69
65 Revert subrepo with no backup. The "reverting s/a" line is gone since
70 Revert subrepo with no backup. The "reverting s/a" line is gone since
66 we're really running 'hg update' in the subrepo:
71 we're really running 'hg update' in the subrepo:
67
72
68 $ echo b > s/a
73 $ echo b > s/a
69 $ hg revert --no-backup s
74 $ hg revert --no-backup s
70 reverting subrepo s
75 reverting subrepo s
71
76
72 Issue2022: update -C
77 Issue2022: update -C
73
78
74 $ echo b > s/a
79 $ echo b > s/a
75 $ hg sum
80 $ hg sum
76 parent: 1:7cf8cfea66e4 tip
81 parent: 1:7cf8cfea66e4 tip
77 1
82 1
78 branch: default
83 branch: default
79 commit: 1 subrepos
84 commit: 1 subrepos
80 update: (current)
85 update: (current)
81 $ hg co -C 1
86 $ hg co -C 1
82 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
87 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
83 $ hg sum
88 $ hg sum
84 parent: 1:7cf8cfea66e4 tip
89 parent: 1:7cf8cfea66e4 tip
85 1
90 1
86 branch: default
91 branch: default
87 commit: (clean)
92 commit: (clean)
88 update: (current)
93 update: (current)
89
94
90 commands that require a clean repo should respect subrepos
95 commands that require a clean repo should respect subrepos
91
96
92 $ echo b >> s/a
97 $ echo b >> s/a
93 $ hg backout tip
98 $ hg backout tip
94 abort: uncommitted changes in subrepo s
99 abort: uncommitted changes in subrepo s
95 [255]
100 [255]
96 $ hg revert -C -R s s/a
101 $ hg revert -C -R s s/a
97
102
98 add sub sub
103 add sub sub
99
104
100 $ echo ss = ss > s/.hgsub
105 $ echo ss = ss > s/.hgsub
101 $ hg init s/ss
106 $ hg init s/ss
102 $ echo a > s/ss/a
107 $ echo a > s/ss/a
103 $ hg -R s add s/.hgsub
108 $ hg -R s add s/.hgsub
104 $ hg -R s/ss add s/ss/a
109 $ hg -R s/ss add s/ss/a
105 $ hg sum
110 $ hg sum
106 parent: 1:7cf8cfea66e4 tip
111 parent: 1:7cf8cfea66e4 tip
107 1
112 1
108 branch: default
113 branch: default
109 commit: 1 subrepos
114 commit: 1 subrepos
110 update: (current)
115 update: (current)
111 $ hg ci -m2
116 $ hg ci -m2
112 committing subrepository s
117 committing subrepository s
113 committing subrepository s/ss (glob)
118 committing subrepository s/ss (glob)
114 $ hg sum
119 $ hg sum
115 parent: 2:df30734270ae tip
120 parent: 2:df30734270ae tip
116 2
121 2
117 branch: default
122 branch: default
118 commit: (clean)
123 commit: (clean)
119 update: (current)
124 update: (current)
120
125
121 test handling .hgsubstate "modified" explicitly.
126 test handling .hgsubstate "modified" explicitly.
122
127
123 $ hg parents --template '{node}\n{files}\n'
128 $ hg parents --template '{node}\n{files}\n'
124 df30734270ae757feb35e643b7018e818e78a9aa
129 df30734270ae757feb35e643b7018e818e78a9aa
125 .hgsubstate
130 .hgsubstate
126 $ hg rollback -q
131 $ hg rollback -q
127 $ hg status -A .hgsubstate
132 $ hg status -A .hgsubstate
128 M .hgsubstate
133 M .hgsubstate
129 $ hg ci -m2
134 $ hg ci -m2
130 $ hg parents --template '{node}\n{files}\n'
135 $ hg parents --template '{node}\n{files}\n'
131 df30734270ae757feb35e643b7018e818e78a9aa
136 df30734270ae757feb35e643b7018e818e78a9aa
132 .hgsubstate
137 .hgsubstate
133
138
134 bump sub rev (and check it is ignored by ui.commitsubrepos)
139 bump sub rev (and check it is ignored by ui.commitsubrepos)
135
140
136 $ echo b > s/a
141 $ echo b > s/a
137 $ hg -R s ci -ms1
142 $ hg -R s ci -ms1
138 $ hg --config ui.commitsubrepos=no ci -m3
143 $ hg --config ui.commitsubrepos=no ci -m3
139
144
140 leave sub dirty (and check ui.commitsubrepos=no aborts the commit)
145 leave sub dirty (and check ui.commitsubrepos=no aborts the commit)
141
146
142 $ echo c > s/a
147 $ echo c > s/a
143 $ hg --config ui.commitsubrepos=no ci -m4
148 $ hg --config ui.commitsubrepos=no ci -m4
144 abort: uncommitted changes in subrepo s
149 abort: uncommitted changes in subrepo s
145 (use --subrepos for recursive commit)
150 (use --subrepos for recursive commit)
146 [255]
151 [255]
147 $ hg id
152 $ hg id
148 f6affe3fbfaa+ tip
153 f6affe3fbfaa+ tip
149 $ hg -R s ci -mc
154 $ hg -R s ci -mc
150 $ hg id
155 $ hg id
151 f6affe3fbfaa+ tip
156 f6affe3fbfaa+ tip
152 $ echo d > s/a
157 $ echo d > s/a
153 $ hg ci -m4
158 $ hg ci -m4
154 committing subrepository s
159 committing subrepository s
155 $ hg tip -R s
160 $ hg tip -R s
156 changeset: 4:02dcf1d70411
161 changeset: 4:02dcf1d70411
157 tag: tip
162 tag: tip
158 user: test
163 user: test
159 date: Thu Jan 01 00:00:00 1970 +0000
164 date: Thu Jan 01 00:00:00 1970 +0000
160 summary: 4
165 summary: 4
161
166
162
167
163 check caching
168 check caching
164
169
165 $ hg co 0
170 $ hg co 0
166 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
171 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
167 $ hg debugsub
172 $ hg debugsub
168
173
169 restore
174 restore
170
175
171 $ hg co
176 $ hg co
172 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
177 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
173 $ hg debugsub
178 $ hg debugsub
174 path s
179 path s
175 source s
180 source s
176 revision 02dcf1d704118aee3ee306ccfa1910850d5b05ef
181 revision 02dcf1d704118aee3ee306ccfa1910850d5b05ef
177
182
178 new branch for merge tests
183 new branch for merge tests
179
184
180 $ hg co 1
185 $ hg co 1
181 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
186 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
182 $ echo t = t >> .hgsub
187 $ echo t = t >> .hgsub
183 $ hg init t
188 $ hg init t
184 $ echo t > t/t
189 $ echo t > t/t
185 $ hg -R t add t
190 $ hg -R t add t
186 adding t/t (glob)
191 adding t/t (glob)
187
192
188 5
193 5
189
194
190 $ hg ci -m5 # add sub
195 $ hg ci -m5 # add sub
191 committing subrepository t
196 committing subrepository t
192 created new head
197 created new head
193 $ echo t2 > t/t
198 $ echo t2 > t/t
194
199
195 6
200 6
196
201
197 $ hg st -R s
202 $ hg st -R s
198 $ hg ci -m6 # change sub
203 $ hg ci -m6 # change sub
199 committing subrepository t
204 committing subrepository t
200 $ hg debugsub
205 $ hg debugsub
201 path s
206 path s
202 source s
207 source s
203 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
208 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
204 path t
209 path t
205 source t
210 source t
206 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
211 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
207 $ echo t3 > t/t
212 $ echo t3 > t/t
208
213
209 7
214 7
210
215
211 $ hg ci -m7 # change sub again for conflict test
216 $ hg ci -m7 # change sub again for conflict test
212 committing subrepository t
217 committing subrepository t
213 $ hg rm .hgsub
218 $ hg rm .hgsub
214
219
215 8
220 8
216
221
217 $ hg ci -m8 # remove sub
222 $ hg ci -m8 # remove sub
218
223
219 test handling .hgsubstate "removed" explicitly.
224 test handling .hgsubstate "removed" explicitly.
220
225
221 $ hg parents --template '{node}\n{files}\n'
226 $ hg parents --template '{node}\n{files}\n'
222 96615c1dad2dc8e3796d7332c77ce69156f7b78e
227 96615c1dad2dc8e3796d7332c77ce69156f7b78e
223 .hgsub .hgsubstate
228 .hgsub .hgsubstate
224 $ hg rollback -q
229 $ hg rollback -q
225 $ hg remove .hgsubstate
230 $ hg remove .hgsubstate
226 $ hg ci -m8
231 $ hg ci -m8
227 $ hg parents --template '{node}\n{files}\n'
232 $ hg parents --template '{node}\n{files}\n'
228 96615c1dad2dc8e3796d7332c77ce69156f7b78e
233 96615c1dad2dc8e3796d7332c77ce69156f7b78e
229 .hgsub .hgsubstate
234 .hgsub .hgsubstate
230
235
231 merge tests
236 merge tests
232
237
233 $ hg co -C 3
238 $ hg co -C 3
234 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
239 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
235 $ hg merge 5 # test adding
240 $ hg merge 5 # test adding
236 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
241 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
237 (branch merge, don't forget to commit)
242 (branch merge, don't forget to commit)
238 $ hg debugsub
243 $ hg debugsub
239 path s
244 path s
240 source s
245 source s
241 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
246 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
242 path t
247 path t
243 source t
248 source t
244 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
249 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
245 $ hg ci -m9
250 $ hg ci -m9
246 created new head
251 created new head
247 $ hg merge 6 --debug # test change
252 $ hg merge 6 --debug # test change
248 searching for copies back to rev 2
253 searching for copies back to rev 2
249 resolving manifests
254 resolving manifests
250 branchmerge: True, force: False, partial: False
255 branchmerge: True, force: False, partial: False
251 ancestor: 1f14a2e2d3ec, local: f0d2028bf86d+, remote: 1831e14459c4
256 ancestor: 1f14a2e2d3ec, local: f0d2028bf86d+, remote: 1831e14459c4
252 .hgsubstate: versions differ -> m
257 .hgsubstate: versions differ -> m
253 updating: .hgsubstate 1/1 files (100.00%)
258 updating: .hgsubstate 1/1 files (100.00%)
254 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
259 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
255 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
260 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
256 getting subrepo t
261 getting subrepo t
257 resolving manifests
262 resolving manifests
258 branchmerge: False, force: False, partial: False
263 branchmerge: False, force: False, partial: False
259 ancestor: 60ca1237c194, local: 60ca1237c194+, remote: 6747d179aa9a
264 ancestor: 60ca1237c194, local: 60ca1237c194+, remote: 6747d179aa9a
260 t: remote is newer -> g
265 t: remote is newer -> g
261 getting t
266 getting t
262 updating: t 1/1 files (100.00%)
267 updating: t 1/1 files (100.00%)
263 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
268 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
264 (branch merge, don't forget to commit)
269 (branch merge, don't forget to commit)
265 $ hg debugsub
270 $ hg debugsub
266 path s
271 path s
267 source s
272 source s
268 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
273 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
269 path t
274 path t
270 source t
275 source t
271 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
276 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
272 $ echo conflict > t/t
277 $ echo conflict > t/t
273 $ hg ci -m10
278 $ hg ci -m10
274 committing subrepository t
279 committing subrepository t
275 $ HGMERGE=internal:merge hg merge --debug 7 # test conflict
280 $ HGMERGE=internal:merge hg merge --debug 7 # test conflict
276 searching for copies back to rev 2
281 searching for copies back to rev 2
277 resolving manifests
282 resolving manifests
278 branchmerge: True, force: False, partial: False
283 branchmerge: True, force: False, partial: False
279 ancestor: 1831e14459c4, local: e45c8b14af55+, remote: f94576341bcf
284 ancestor: 1831e14459c4, local: e45c8b14af55+, remote: f94576341bcf
280 .hgsubstate: versions differ -> m
285 .hgsubstate: versions differ -> m
281 updating: .hgsubstate 1/1 files (100.00%)
286 updating: .hgsubstate 1/1 files (100.00%)
282 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
287 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
283 subrepo t: both sides changed
288 subrepo t: both sides changed
284 subrepository t diverged (local revision: 20a0db6fbf6c, remote revision: 7af322bc1198)
289 subrepository t diverged (local revision: 20a0db6fbf6c, remote revision: 7af322bc1198)
285 (M)erge, keep (l)ocal or keep (r)emote? m
290 (M)erge, keep (l)ocal or keep (r)emote? m
286 merging subrepo t
291 merging subrepo t
287 searching for copies back to rev 2
292 searching for copies back to rev 2
288 resolving manifests
293 resolving manifests
289 branchmerge: True, force: False, partial: False
294 branchmerge: True, force: False, partial: False
290 ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
295 ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
291 preserving t for resolve of t
296 preserving t for resolve of t
292 t: versions differ -> m
297 t: versions differ -> m
293 updating: t 1/1 files (100.00%)
298 updating: t 1/1 files (100.00%)
294 picked tool 'internal:merge' for t (binary False symlink False)
299 picked tool 'internal:merge' for t (binary False symlink False)
295 merging t
300 merging t
296 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
301 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
297 warning: conflicts during merge.
302 warning: conflicts during merge.
298 merging t incomplete! (edit conflicts, then use 'hg resolve --mark')
303 merging t incomplete! (edit conflicts, then use 'hg resolve --mark')
299 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
304 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
300 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
305 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
301 subrepo t: merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
306 subrepo t: merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
302 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
307 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
303 (branch merge, don't forget to commit)
308 (branch merge, don't forget to commit)
304
309
305 should conflict
310 should conflict
306
311
307 $ cat t/t
312 $ cat t/t
308 <<<<<<< local: 20a0db6fbf6c - test: 10
313 <<<<<<< local: 20a0db6fbf6c - test: 10
309 conflict
314 conflict
310 =======
315 =======
311 t3
316 t3
312 >>>>>>> other: 7af322bc1198 - test: 7
317 >>>>>>> other: 7af322bc1198 - test: 7
313
318
314 11: remove subrepo t
319 11: remove subrepo t
315
320
316 $ hg co -C 5
321 $ hg co -C 5
317 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
322 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
318 $ hg revert -r 4 .hgsub # remove t
323 $ hg revert -r 4 .hgsub # remove t
319 $ hg ci -m11
324 $ hg ci -m11
320 created new head
325 created new head
321 $ hg debugsub
326 $ hg debugsub
322 path s
327 path s
323 source s
328 source s
324 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
329 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
325
330
326 local removed, remote changed, keep changed
331 local removed, remote changed, keep changed
327
332
328 $ hg merge 6
333 $ hg merge 6
329 remote changed subrepository t which local removed
334 remote changed subrepository t which local removed
330 use (c)hanged version or (d)elete? c
335 use (c)hanged version or (d)elete? c
331 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
336 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
332 (branch merge, don't forget to commit)
337 (branch merge, don't forget to commit)
333 BROKEN: should include subrepo t
338 BROKEN: should include subrepo t
334 $ hg debugsub
339 $ hg debugsub
335 path s
340 path s
336 source s
341 source s
337 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
342 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
338 $ cat .hgsubstate
343 $ cat .hgsubstate
339 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
344 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
340 6747d179aa9a688023c4b0cad32e4c92bb7f34ad t
345 6747d179aa9a688023c4b0cad32e4c92bb7f34ad t
341 $ hg ci -m 'local removed, remote changed, keep changed'
346 $ hg ci -m 'local removed, remote changed, keep changed'
342 BROKEN: should include subrepo t
347 BROKEN: should include subrepo t
343 $ hg debugsub
348 $ hg debugsub
344 path s
349 path s
345 source s
350 source s
346 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
351 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
347 BROKEN: should include subrepo t
352 BROKEN: should include subrepo t
348 $ cat .hgsubstate
353 $ cat .hgsubstate
349 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
354 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
350 $ cat t/t
355 $ cat t/t
351 t2
356 t2
352
357
353 local removed, remote changed, keep removed
358 local removed, remote changed, keep removed
354
359
355 $ hg co -C 11
360 $ hg co -C 11
356 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
361 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
357 $ hg merge --config ui.interactive=true 6 <<EOF
362 $ hg merge --config ui.interactive=true 6 <<EOF
358 > d
363 > d
359 > EOF
364 > EOF
360 remote changed subrepository t which local removed
365 remote changed subrepository t which local removed
361 use (c)hanged version or (d)elete? d
366 use (c)hanged version or (d)elete? d
362 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
367 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
363 (branch merge, don't forget to commit)
368 (branch merge, don't forget to commit)
364 $ hg debugsub
369 $ hg debugsub
365 path s
370 path s
366 source s
371 source s
367 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
372 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
368 $ cat .hgsubstate
373 $ cat .hgsubstate
369 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
374 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
370 $ hg ci -m 'local removed, remote changed, keep removed'
375 $ hg ci -m 'local removed, remote changed, keep removed'
371 created new head
376 created new head
372 $ hg debugsub
377 $ hg debugsub
373 path s
378 path s
374 source s
379 source s
375 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
380 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
376 $ cat .hgsubstate
381 $ cat .hgsubstate
377 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
382 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
378
383
379 local changed, remote removed, keep changed
384 local changed, remote removed, keep changed
380
385
381 $ hg co -C 6
386 $ hg co -C 6
382 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
387 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
383 $ hg merge 11
388 $ hg merge 11
384 local changed subrepository t which remote removed
389 local changed subrepository t which remote removed
385 use (c)hanged version or (d)elete? c
390 use (c)hanged version or (d)elete? c
386 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
391 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
387 (branch merge, don't forget to commit)
392 (branch merge, don't forget to commit)
388 BROKEN: should include subrepo t
393 BROKEN: should include subrepo t
389 $ hg debugsub
394 $ hg debugsub
390 path s
395 path s
391 source s
396 source s
392 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
397 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
393 BROKEN: should include subrepo t
398 BROKEN: should include subrepo t
394 $ cat .hgsubstate
399 $ cat .hgsubstate
395 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
400 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
396 $ hg ci -m 'local changed, remote removed, keep changed'
401 $ hg ci -m 'local changed, remote removed, keep changed'
397 created new head
402 created new head
398 BROKEN: should include subrepo t
403 BROKEN: should include subrepo t
399 $ hg debugsub
404 $ hg debugsub
400 path s
405 path s
401 source s
406 source s
402 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
407 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
403 BROKEN: should include subrepo t
408 BROKEN: should include subrepo t
404 $ cat .hgsubstate
409 $ cat .hgsubstate
405 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
410 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
406 $ cat t/t
411 $ cat t/t
407 t2
412 t2
408
413
409 local changed, remote removed, keep removed
414 local changed, remote removed, keep removed
410
415
411 $ hg co -C 6
416 $ hg co -C 6
412 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
417 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
413 $ hg merge --config ui.interactive=true 11 <<EOF
418 $ hg merge --config ui.interactive=true 11 <<EOF
414 > d
419 > d
415 > EOF
420 > EOF
416 local changed subrepository t which remote removed
421 local changed subrepository t which remote removed
417 use (c)hanged version or (d)elete? d
422 use (c)hanged version or (d)elete? d
418 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
423 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
419 (branch merge, don't forget to commit)
424 (branch merge, don't forget to commit)
420 $ hg debugsub
425 $ hg debugsub
421 path s
426 path s
422 source s
427 source s
423 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
428 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
424 $ cat .hgsubstate
429 $ cat .hgsubstate
425 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
430 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
426 $ hg ci -m 'local changed, remote removed, keep removed'
431 $ hg ci -m 'local changed, remote removed, keep removed'
427 created new head
432 created new head
428 $ hg debugsub
433 $ hg debugsub
429 path s
434 path s
430 source s
435 source s
431 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
436 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
432 $ cat .hgsubstate
437 $ cat .hgsubstate
433 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
438 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
434
439
435 clean up to avoid having to fix up the tests below
440 clean up to avoid having to fix up the tests below
436
441
437 $ hg co -C 10
442 $ hg co -C 10
438 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
443 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
439 $ cat >> $HGRCPATH <<EOF
444 $ cat >> $HGRCPATH <<EOF
440 > [extensions]
445 > [extensions]
441 > strip=
446 > strip=
442 > EOF
447 > EOF
443 $ hg strip -r 11:15
448 $ hg strip -r 11:15
444 saved backup bundle to $TESTTMP/t/.hg/strip-backup/*-backup.hg (glob)
449 saved backup bundle to $TESTTMP/t/.hg/strip-backup/*-backup.hg (glob)
445
450
446 clone
451 clone
447
452
448 $ cd ..
453 $ cd ..
449 $ hg clone t tc
454 $ hg clone t tc
450 updating to branch default
455 updating to branch default
451 cloning subrepo s from $TESTTMP/t/s
456 cloning subrepo s from $TESTTMP/t/s
452 cloning subrepo s/ss from $TESTTMP/t/s/ss (glob)
457 cloning subrepo s/ss from $TESTTMP/t/s/ss (glob)
453 cloning subrepo t from $TESTTMP/t/t
458 cloning subrepo t from $TESTTMP/t/t
454 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
459 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
455 $ cd tc
460 $ cd tc
456 $ hg debugsub
461 $ hg debugsub
457 path s
462 path s
458 source s
463 source s
459 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
464 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
460 path t
465 path t
461 source t
466 source t
462 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
467 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
463
468
464 push
469 push
465
470
466 $ echo bah > t/t
471 $ echo bah > t/t
467 $ hg ci -m11
472 $ hg ci -m11
468 committing subrepository t
473 committing subrepository t
469 $ hg push
474 $ hg push
470 pushing to $TESTTMP/t (glob)
475 pushing to $TESTTMP/t (glob)
471 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
476 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
472 no changes made to subrepo s since last push to $TESTTMP/t/s
477 no changes made to subrepo s since last push to $TESTTMP/t/s
473 pushing subrepo t to $TESTTMP/t/t
478 pushing subrepo t to $TESTTMP/t/t
474 searching for changes
479 searching for changes
475 adding changesets
480 adding changesets
476 adding manifests
481 adding manifests
477 adding file changes
482 adding file changes
478 added 1 changesets with 1 changes to 1 files
483 added 1 changesets with 1 changes to 1 files
479 searching for changes
484 searching for changes
480 adding changesets
485 adding changesets
481 adding manifests
486 adding manifests
482 adding file changes
487 adding file changes
483 added 1 changesets with 1 changes to 1 files
488 added 1 changesets with 1 changes to 1 files
484
489
485 push -f
490 push -f
486
491
487 $ echo bah > s/a
492 $ echo bah > s/a
488 $ hg ci -m12
493 $ hg ci -m12
489 committing subrepository s
494 committing subrepository s
490 $ hg push
495 $ hg push
491 pushing to $TESTTMP/t (glob)
496 pushing to $TESTTMP/t (glob)
492 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
497 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
493 pushing subrepo s to $TESTTMP/t/s
498 pushing subrepo s to $TESTTMP/t/s
494 searching for changes
499 searching for changes
495 abort: push creates new remote head 12a213df6fa9! (in subrepo s)
500 abort: push creates new remote head 12a213df6fa9! (in subrepo s)
496 (merge or see "hg help push" for details about pushing new heads)
501 (merge or see "hg help push" for details about pushing new heads)
497 [255]
502 [255]
498 $ hg push -f
503 $ hg push -f
499 pushing to $TESTTMP/t (glob)
504 pushing to $TESTTMP/t (glob)
500 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
505 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
501 searching for changes
506 searching for changes
502 no changes found
507 no changes found
503 pushing subrepo s to $TESTTMP/t/s
508 pushing subrepo s to $TESTTMP/t/s
504 searching for changes
509 searching for changes
505 adding changesets
510 adding changesets
506 adding manifests
511 adding manifests
507 adding file changes
512 adding file changes
508 added 1 changesets with 1 changes to 1 files (+1 heads)
513 added 1 changesets with 1 changes to 1 files (+1 heads)
509 pushing subrepo t to $TESTTMP/t/t
514 pushing subrepo t to $TESTTMP/t/t
510 searching for changes
515 searching for changes
511 no changes found
516 no changes found
512 searching for changes
517 searching for changes
513 adding changesets
518 adding changesets
514 adding manifests
519 adding manifests
515 adding file changes
520 adding file changes
516 added 1 changesets with 1 changes to 1 files
521 added 1 changesets with 1 changes to 1 files
517
522
518 check that unmodified subrepos are not pushed
523 check that unmodified subrepos are not pushed
519
524
520 $ hg clone . ../tcc
525 $ hg clone . ../tcc
521 updating to branch default
526 updating to branch default
522 cloning subrepo s from $TESTTMP/tc/s
527 cloning subrepo s from $TESTTMP/tc/s
523 cloning subrepo s/ss from $TESTTMP/tc/s/ss (glob)
528 cloning subrepo s/ss from $TESTTMP/tc/s/ss (glob)
524 cloning subrepo t from $TESTTMP/tc/t
529 cloning subrepo t from $TESTTMP/tc/t
525 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
530 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
526
531
527 the subrepos on the new clone have nothing to push to its source
532 the subrepos on the new clone have nothing to push to its source
528
533
529 $ hg push -R ../tcc .
534 $ hg push -R ../tcc .
530 pushing to .
535 pushing to .
531 no changes made to subrepo s/ss since last push to s/ss (glob)
536 no changes made to subrepo s/ss since last push to s/ss (glob)
532 no changes made to subrepo s since last push to s
537 no changes made to subrepo s since last push to s
533 no changes made to subrepo t since last push to t
538 no changes made to subrepo t since last push to t
534 searching for changes
539 searching for changes
535 no changes found
540 no changes found
536 [1]
541 [1]
537
542
538 the subrepos on the source do not have a clean store versus the clone target
543 the subrepos on the source do not have a clean store versus the clone target
539 because they were never explicitly pushed to the source
544 because they were never explicitly pushed to the source
540
545
541 $ hg push ../tcc
546 $ hg push ../tcc
542 pushing to ../tcc
547 pushing to ../tcc
543 pushing subrepo s/ss to ../tcc/s/ss (glob)
548 pushing subrepo s/ss to ../tcc/s/ss (glob)
544 searching for changes
549 searching for changes
545 no changes found
550 no changes found
546 pushing subrepo s to ../tcc/s
551 pushing subrepo s to ../tcc/s
547 searching for changes
552 searching for changes
548 no changes found
553 no changes found
549 pushing subrepo t to ../tcc/t
554 pushing subrepo t to ../tcc/t
550 searching for changes
555 searching for changes
551 no changes found
556 no changes found
552 searching for changes
557 searching for changes
553 no changes found
558 no changes found
554 [1]
559 [1]
555
560
556 after push their stores become clean
561 after push their stores become clean
557
562
558 $ hg push ../tcc
563 $ hg push ../tcc
559 pushing to ../tcc
564 pushing to ../tcc
560 no changes made to subrepo s/ss since last push to ../tcc/s/ss (glob)
565 no changes made to subrepo s/ss since last push to ../tcc/s/ss (glob)
561 no changes made to subrepo s since last push to ../tcc/s
566 no changes made to subrepo s since last push to ../tcc/s
562 no changes made to subrepo t since last push to ../tcc/t
567 no changes made to subrepo t since last push to ../tcc/t
563 searching for changes
568 searching for changes
564 no changes found
569 no changes found
565 [1]
570 [1]
566
571
567 updating a subrepo to a different revision or changing
572 updating a subrepo to a different revision or changing
568 its working directory does not make its store dirty
573 its working directory does not make its store dirty
569
574
570 $ hg -R s update '.^'
575 $ hg -R s update '.^'
571 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
576 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
572 $ hg push
577 $ hg push
573 pushing to $TESTTMP/t (glob)
578 pushing to $TESTTMP/t (glob)
574 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
579 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
575 no changes made to subrepo s since last push to $TESTTMP/t/s
580 no changes made to subrepo s since last push to $TESTTMP/t/s
576 no changes made to subrepo t since last push to $TESTTMP/t/t
581 no changes made to subrepo t since last push to $TESTTMP/t/t
577 searching for changes
582 searching for changes
578 no changes found
583 no changes found
579 [1]
584 [1]
580 $ echo foo >> s/a
585 $ echo foo >> s/a
581 $ hg push
586 $ hg push
582 pushing to $TESTTMP/t (glob)
587 pushing to $TESTTMP/t (glob)
583 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
588 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
584 no changes made to subrepo s since last push to $TESTTMP/t/s
589 no changes made to subrepo s since last push to $TESTTMP/t/s
585 no changes made to subrepo t since last push to $TESTTMP/t/t
590 no changes made to subrepo t since last push to $TESTTMP/t/t
586 searching for changes
591 searching for changes
587 no changes found
592 no changes found
588 [1]
593 [1]
589 $ hg -R s update -C tip
594 $ hg -R s update -C tip
590 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
595 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
591
596
592 committing into a subrepo makes its store (but not its parent's store) dirty
597 committing into a subrepo makes its store (but not its parent's store) dirty
593
598
594 $ echo foo >> s/ss/a
599 $ echo foo >> s/ss/a
595 $ hg -R s/ss commit -m 'test dirty store detection'
600 $ hg -R s/ss commit -m 'test dirty store detection'
596 $ hg push
601 $ hg push
597 pushing to $TESTTMP/t (glob)
602 pushing to $TESTTMP/t (glob)
598 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
603 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
599 searching for changes
604 searching for changes
600 adding changesets
605 adding changesets
601 adding manifests
606 adding manifests
602 adding file changes
607 adding file changes
603 added 1 changesets with 1 changes to 1 files
608 added 1 changesets with 1 changes to 1 files
604 no changes made to subrepo s since last push to $TESTTMP/t/s
609 no changes made to subrepo s since last push to $TESTTMP/t/s
605 no changes made to subrepo t since last push to $TESTTMP/t/t
610 no changes made to subrepo t since last push to $TESTTMP/t/t
606 searching for changes
611 searching for changes
607 no changes found
612 no changes found
608 [1]
613 [1]
609
614
610 a subrepo store may be clean versus one repo but not versus another
615 a subrepo store may be clean versus one repo but not versus another
611
616
612 $ hg push
617 $ hg push
613 pushing to $TESTTMP/t (glob)
618 pushing to $TESTTMP/t (glob)
614 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
619 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
615 no changes made to subrepo s since last push to $TESTTMP/t/s
620 no changes made to subrepo s since last push to $TESTTMP/t/s
616 no changes made to subrepo t since last push to $TESTTMP/t/t
621 no changes made to subrepo t since last push to $TESTTMP/t/t
617 searching for changes
622 searching for changes
618 no changes found
623 no changes found
619 [1]
624 [1]
620 $ hg push ../tcc
625 $ hg push ../tcc
621 pushing to ../tcc
626 pushing to ../tcc
622 pushing subrepo s/ss to ../tcc/s/ss (glob)
627 pushing subrepo s/ss to ../tcc/s/ss (glob)
623 searching for changes
628 searching for changes
624 adding changesets
629 adding changesets
625 adding manifests
630 adding manifests
626 adding file changes
631 adding file changes
627 added 1 changesets with 1 changes to 1 files
632 added 1 changesets with 1 changes to 1 files
628 no changes made to subrepo s since last push to ../tcc/s
633 no changes made to subrepo s since last push to ../tcc/s
629 no changes made to subrepo t since last push to ../tcc/t
634 no changes made to subrepo t since last push to ../tcc/t
630 searching for changes
635 searching for changes
631 no changes found
636 no changes found
632 [1]
637 [1]
633
638
634 update
639 update
635
640
636 $ cd ../t
641 $ cd ../t
637 $ hg up -C # discard our earlier merge
642 $ hg up -C # discard our earlier merge
638 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
643 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
639 $ echo blah > t/t
644 $ echo blah > t/t
640 $ hg ci -m13
645 $ hg ci -m13
641 committing subrepository t
646 committing subrepository t
642
647
643 backout calls revert internally with minimal opts, which should not raise
648 backout calls revert internally with minimal opts, which should not raise
644 KeyError
649 KeyError
645
650
646 $ hg backout ".^"
651 $ hg backout ".^"
647 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
652 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
648 changeset c373c8102e68 backed out, don't forget to commit.
653 changeset c373c8102e68 backed out, don't forget to commit.
649
654
650 $ hg up -C # discard changes
655 $ hg up -C # discard changes
651 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
656 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
652
657
653 pull
658 pull
654
659
655 $ cd ../tc
660 $ cd ../tc
656 $ hg pull
661 $ hg pull
657 pulling from $TESTTMP/t (glob)
662 pulling from $TESTTMP/t (glob)
658 searching for changes
663 searching for changes
659 adding changesets
664 adding changesets
660 adding manifests
665 adding manifests
661 adding file changes
666 adding file changes
662 added 1 changesets with 1 changes to 1 files
667 added 1 changesets with 1 changes to 1 files
663 (run 'hg update' to get a working copy)
668 (run 'hg update' to get a working copy)
664
669
665 should pull t
670 should pull t
666
671
667 $ hg up
672 $ hg up
668 pulling subrepo t from $TESTTMP/t/t
673 pulling subrepo t from $TESTTMP/t/t
669 searching for changes
674 searching for changes
670 adding changesets
675 adding changesets
671 adding manifests
676 adding manifests
672 adding file changes
677 adding file changes
673 added 1 changesets with 1 changes to 1 files
678 added 1 changesets with 1 changes to 1 files
674 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
679 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
675 $ cat t/t
680 $ cat t/t
676 blah
681 blah
677
682
678 bogus subrepo path aborts
683 bogus subrepo path aborts
679
684
680 $ echo 'bogus=[boguspath' >> .hgsub
685 $ echo 'bogus=[boguspath' >> .hgsub
681 $ hg ci -m 'bogus subrepo path'
686 $ hg ci -m 'bogus subrepo path'
682 abort: missing ] in subrepo source
687 abort: missing ] in subrepo source
683 [255]
688 [255]
684
689
685 Issue1986: merge aborts when trying to merge a subrepo that
690 Issue1986: merge aborts when trying to merge a subrepo that
686 shouldn't need merging
691 shouldn't need merging
687
692
688 # subrepo layout
693 # subrepo layout
689 #
694 #
690 # o 5 br
695 # o 5 br
691 # /|
696 # /|
692 # o | 4 default
697 # o | 4 default
693 # | |
698 # | |
694 # | o 3 br
699 # | o 3 br
695 # |/|
700 # |/|
696 # o | 2 default
701 # o | 2 default
697 # | |
702 # | |
698 # | o 1 br
703 # | o 1 br
699 # |/
704 # |/
700 # o 0 default
705 # o 0 default
701
706
702 $ cd ..
707 $ cd ..
703 $ rm -rf sub
708 $ rm -rf sub
704 $ hg init main
709 $ hg init main
705 $ cd main
710 $ cd main
706 $ hg init s
711 $ hg init s
707 $ cd s
712 $ cd s
708 $ echo a > a
713 $ echo a > a
709 $ hg ci -Am1
714 $ hg ci -Am1
710 adding a
715 adding a
711 $ hg branch br
716 $ hg branch br
712 marked working directory as branch br
717 marked working directory as branch br
713 (branches are permanent and global, did you want a bookmark?)
718 (branches are permanent and global, did you want a bookmark?)
714 $ echo a >> a
719 $ echo a >> a
715 $ hg ci -m1
720 $ hg ci -m1
716 $ hg up default
721 $ hg up default
717 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
722 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
718 $ echo b > b
723 $ echo b > b
719 $ hg ci -Am1
724 $ hg ci -Am1
720 adding b
725 adding b
721 $ hg up br
726 $ hg up br
722 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
727 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
723 $ hg merge tip
728 $ hg merge tip
724 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
729 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
725 (branch merge, don't forget to commit)
730 (branch merge, don't forget to commit)
726 $ hg ci -m1
731 $ hg ci -m1
727 $ hg up 2
732 $ hg up 2
728 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
733 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
729 $ echo c > c
734 $ echo c > c
730 $ hg ci -Am1
735 $ hg ci -Am1
731 adding c
736 adding c
732 $ hg up 3
737 $ hg up 3
733 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
738 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
734 $ hg merge 4
739 $ hg merge 4
735 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
740 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
736 (branch merge, don't forget to commit)
741 (branch merge, don't forget to commit)
737 $ hg ci -m1
742 $ hg ci -m1
738
743
739 # main repo layout:
744 # main repo layout:
740 #
745 #
741 # * <-- try to merge default into br again
746 # * <-- try to merge default into br again
742 # .`|
747 # .`|
743 # . o 5 br --> substate = 5
748 # . o 5 br --> substate = 5
744 # . |
749 # . |
745 # o | 4 default --> substate = 4
750 # o | 4 default --> substate = 4
746 # | |
751 # | |
747 # | o 3 br --> substate = 2
752 # | o 3 br --> substate = 2
748 # |/|
753 # |/|
749 # o | 2 default --> substate = 2
754 # o | 2 default --> substate = 2
750 # | |
755 # | |
751 # | o 1 br --> substate = 3
756 # | o 1 br --> substate = 3
752 # |/
757 # |/
753 # o 0 default --> substate = 2
758 # o 0 default --> substate = 2
754
759
755 $ cd ..
760 $ cd ..
756 $ echo 's = s' > .hgsub
761 $ echo 's = s' > .hgsub
757 $ hg -R s up 2
762 $ hg -R s up 2
758 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
763 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
759 $ hg ci -Am1
764 $ hg ci -Am1
760 adding .hgsub
765 adding .hgsub
761 $ hg branch br
766 $ hg branch br
762 marked working directory as branch br
767 marked working directory as branch br
763 (branches are permanent and global, did you want a bookmark?)
768 (branches are permanent and global, did you want a bookmark?)
764 $ echo b > b
769 $ echo b > b
765 $ hg -R s up 3
770 $ hg -R s up 3
766 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
771 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
767 $ hg ci -Am1
772 $ hg ci -Am1
768 adding b
773 adding b
769 $ hg up default
774 $ hg up default
770 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
775 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
771 $ echo c > c
776 $ echo c > c
772 $ hg ci -Am1
777 $ hg ci -Am1
773 adding c
778 adding c
774 $ hg up 1
779 $ hg up 1
775 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
780 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
776 $ hg merge 2
781 $ hg merge 2
777 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
782 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
778 (branch merge, don't forget to commit)
783 (branch merge, don't forget to commit)
779 $ hg ci -m1
784 $ hg ci -m1
780 $ hg up 2
785 $ hg up 2
781 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
786 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
782 $ hg -R s up 4
787 $ hg -R s up 4
783 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
788 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
784 $ echo d > d
789 $ echo d > d
785 $ hg ci -Am1
790 $ hg ci -Am1
786 adding d
791 adding d
787 $ hg up 3
792 $ hg up 3
788 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
793 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
789 $ hg -R s up 5
794 $ hg -R s up 5
790 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
795 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
791 $ echo e > e
796 $ echo e > e
792 $ hg ci -Am1
797 $ hg ci -Am1
793 adding e
798 adding e
794
799
795 $ hg up 5
800 $ hg up 5
796 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
801 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
797 $ hg merge 4 # try to merge default into br again
802 $ hg merge 4 # try to merge default into br again
798 subrepository s diverged (local revision: f8f13b33206e, remote revision: a3f9062a4f88)
803 subrepository s diverged (local revision: f8f13b33206e, remote revision: a3f9062a4f88)
799 (M)erge, keep (l)ocal or keep (r)emote? m
804 (M)erge, keep (l)ocal or keep (r)emote? m
800 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
805 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
801 (branch merge, don't forget to commit)
806 (branch merge, don't forget to commit)
802 $ cd ..
807 $ cd ..
803
808
804 test subrepo delete from .hgsubstate
809 test subrepo delete from .hgsubstate
805
810
806 $ hg init testdelete
811 $ hg init testdelete
807 $ mkdir testdelete/nested testdelete/nested2
812 $ mkdir testdelete/nested testdelete/nested2
808 $ hg init testdelete/nested
813 $ hg init testdelete/nested
809 $ hg init testdelete/nested2
814 $ hg init testdelete/nested2
810 $ echo test > testdelete/nested/foo
815 $ echo test > testdelete/nested/foo
811 $ echo test > testdelete/nested2/foo
816 $ echo test > testdelete/nested2/foo
812 $ hg -R testdelete/nested add
817 $ hg -R testdelete/nested add
813 adding testdelete/nested/foo (glob)
818 adding testdelete/nested/foo (glob)
814 $ hg -R testdelete/nested2 add
819 $ hg -R testdelete/nested2 add
815 adding testdelete/nested2/foo (glob)
820 adding testdelete/nested2/foo (glob)
816 $ hg -R testdelete/nested ci -m test
821 $ hg -R testdelete/nested ci -m test
817 $ hg -R testdelete/nested2 ci -m test
822 $ hg -R testdelete/nested2 ci -m test
818 $ echo nested = nested > testdelete/.hgsub
823 $ echo nested = nested > testdelete/.hgsub
819 $ echo nested2 = nested2 >> testdelete/.hgsub
824 $ echo nested2 = nested2 >> testdelete/.hgsub
820 $ hg -R testdelete add
825 $ hg -R testdelete add
821 adding testdelete/.hgsub (glob)
826 adding testdelete/.hgsub (glob)
822 $ hg -R testdelete ci -m "nested 1 & 2 added"
827 $ hg -R testdelete ci -m "nested 1 & 2 added"
823 $ echo nested = nested > testdelete/.hgsub
828 $ echo nested = nested > testdelete/.hgsub
824 $ hg -R testdelete ci -m "nested 2 deleted"
829 $ hg -R testdelete ci -m "nested 2 deleted"
825 $ cat testdelete/.hgsubstate
830 $ cat testdelete/.hgsubstate
826 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
831 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
827 $ hg -R testdelete remove testdelete/.hgsub
832 $ hg -R testdelete remove testdelete/.hgsub
828 $ hg -R testdelete ci -m ".hgsub deleted"
833 $ hg -R testdelete ci -m ".hgsub deleted"
829 $ cat testdelete/.hgsubstate
834 $ cat testdelete/.hgsubstate
830 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
835 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
831
836
832 test repository cloning
837 test repository cloning
833
838
834 $ mkdir mercurial mercurial2
839 $ mkdir mercurial mercurial2
835 $ hg init nested_absolute
840 $ hg init nested_absolute
836 $ echo test > nested_absolute/foo
841 $ echo test > nested_absolute/foo
837 $ hg -R nested_absolute add
842 $ hg -R nested_absolute add
838 adding nested_absolute/foo (glob)
843 adding nested_absolute/foo (glob)
839 $ hg -R nested_absolute ci -mtest
844 $ hg -R nested_absolute ci -mtest
840 $ cd mercurial
845 $ cd mercurial
841 $ hg init nested_relative
846 $ hg init nested_relative
842 $ echo test2 > nested_relative/foo2
847 $ echo test2 > nested_relative/foo2
843 $ hg -R nested_relative add
848 $ hg -R nested_relative add
844 adding nested_relative/foo2 (glob)
849 adding nested_relative/foo2 (glob)
845 $ hg -R nested_relative ci -mtest2
850 $ hg -R nested_relative ci -mtest2
846 $ hg init main
851 $ hg init main
847 $ echo "nested_relative = ../nested_relative" > main/.hgsub
852 $ echo "nested_relative = ../nested_relative" > main/.hgsub
848 $ echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
853 $ echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
849 $ hg -R main add
854 $ hg -R main add
850 adding main/.hgsub (glob)
855 adding main/.hgsub (glob)
851 $ hg -R main ci -m "add subrepos"
856 $ hg -R main ci -m "add subrepos"
852 $ cd ..
857 $ cd ..
853 $ hg clone mercurial/main mercurial2/main
858 $ hg clone mercurial/main mercurial2/main
854 updating to branch default
859 updating to branch default
855 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
860 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
856 $ cat mercurial2/main/nested_absolute/.hg/hgrc \
861 $ cat mercurial2/main/nested_absolute/.hg/hgrc \
857 > mercurial2/main/nested_relative/.hg/hgrc
862 > mercurial2/main/nested_relative/.hg/hgrc
858 [paths]
863 [paths]
859 default = $TESTTMP/mercurial/nested_absolute
864 default = $TESTTMP/mercurial/nested_absolute
860 [paths]
865 [paths]
861 default = $TESTTMP/mercurial/nested_relative
866 default = $TESTTMP/mercurial/nested_relative
862 $ rm -rf mercurial mercurial2
867 $ rm -rf mercurial mercurial2
863
868
864 Issue1977: multirepo push should fail if subrepo push fails
869 Issue1977: multirepo push should fail if subrepo push fails
865
870
866 $ hg init repo
871 $ hg init repo
867 $ hg init repo/s
872 $ hg init repo/s
868 $ echo a > repo/s/a
873 $ echo a > repo/s/a
869 $ hg -R repo/s ci -Am0
874 $ hg -R repo/s ci -Am0
870 adding a
875 adding a
871 $ echo s = s > repo/.hgsub
876 $ echo s = s > repo/.hgsub
872 $ hg -R repo ci -Am1
877 $ hg -R repo ci -Am1
873 adding .hgsub
878 adding .hgsub
874 $ hg clone repo repo2
879 $ hg clone repo repo2
875 updating to branch default
880 updating to branch default
876 cloning subrepo s from $TESTTMP/repo/s
881 cloning subrepo s from $TESTTMP/repo/s
877 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
882 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
878 $ hg -q -R repo2 pull -u
883 $ hg -q -R repo2 pull -u
879 $ echo 1 > repo2/s/a
884 $ echo 1 > repo2/s/a
880 $ hg -R repo2/s ci -m2
885 $ hg -R repo2/s ci -m2
881 $ hg -q -R repo2/s push
886 $ hg -q -R repo2/s push
882 $ hg -R repo2/s up -C 0
887 $ hg -R repo2/s up -C 0
883 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
888 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
884 $ echo 2 > repo2/s/b
889 $ echo 2 > repo2/s/b
885 $ hg -R repo2/s ci -m3 -A
890 $ hg -R repo2/s ci -m3 -A
886 adding b
891 adding b
887 created new head
892 created new head
888 $ hg -R repo2 ci -m3
893 $ hg -R repo2 ci -m3
889 $ hg -q -R repo2 push
894 $ hg -q -R repo2 push
890 abort: push creates new remote head cc505f09a8b2! (in subrepo s)
895 abort: push creates new remote head cc505f09a8b2! (in subrepo s)
891 (merge or see "hg help push" for details about pushing new heads)
896 (merge or see "hg help push" for details about pushing new heads)
892 [255]
897 [255]
893 $ hg -R repo update
898 $ hg -R repo update
894 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
899 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
895
900
896 test if untracked file is not overwritten
901 test if untracked file is not overwritten
897
902
898 $ echo issue3276_ok > repo/s/b
903 $ echo issue3276_ok > repo/s/b
899 $ hg -R repo2 push -f -q
904 $ hg -R repo2 push -f -q
900 $ touch -t 200001010000 repo/.hgsubstate
905 $ touch -t 200001010000 repo/.hgsubstate
901 $ hg -R repo status --config debug.dirstate.delaywrite=2 repo/.hgsubstate
906 $ hg -R repo status --config debug.dirstate.delaywrite=2 repo/.hgsubstate
902 $ hg -R repo update
907 $ hg -R repo update
903 b: untracked file differs
908 b: untracked file differs
904 abort: untracked files in working directory differ from files in requested revision (in subrepo s)
909 abort: untracked files in working directory differ from files in requested revision (in subrepo s)
905 [255]
910 [255]
906
911
907 $ cat repo/s/b
912 $ cat repo/s/b
908 issue3276_ok
913 issue3276_ok
909 $ rm repo/s/b
914 $ rm repo/s/b
910 $ touch -t 200001010000 repo/.hgsubstate
915 $ touch -t 200001010000 repo/.hgsubstate
911 $ hg -R repo revert --all
916 $ hg -R repo revert --all
912 reverting repo/.hgsubstate (glob)
917 reverting repo/.hgsubstate (glob)
913 reverting subrepo s
918 reverting subrepo s
914 $ hg -R repo update
919 $ hg -R repo update
915 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
920 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
916 $ cat repo/s/b
921 $ cat repo/s/b
917 2
922 2
918 $ rm -rf repo2 repo
923 $ rm -rf repo2 repo
919
924
920
925
921 Issue1852 subrepos with relative paths always push/pull relative to default
926 Issue1852 subrepos with relative paths always push/pull relative to default
922
927
923 Prepare a repo with subrepo
928 Prepare a repo with subrepo
924
929
925 $ hg init issue1852a
930 $ hg init issue1852a
926 $ cd issue1852a
931 $ cd issue1852a
927 $ hg init sub/repo
932 $ hg init sub/repo
928 $ echo test > sub/repo/foo
933 $ echo test > sub/repo/foo
929 $ hg -R sub/repo add sub/repo/foo
934 $ hg -R sub/repo add sub/repo/foo
930 $ echo sub/repo = sub/repo > .hgsub
935 $ echo sub/repo = sub/repo > .hgsub
931 $ hg add .hgsub
936 $ hg add .hgsub
932 $ hg ci -mtest
937 $ hg ci -mtest
933 committing subrepository sub/repo (glob)
938 committing subrepository sub/repo (glob)
934 $ echo test >> sub/repo/foo
939 $ echo test >> sub/repo/foo
935 $ hg ci -mtest
940 $ hg ci -mtest
936 committing subrepository sub/repo (glob)
941 committing subrepository sub/repo (glob)
937 $ hg cat sub/repo/foo
942 $ hg cat sub/repo/foo
938 test
943 test
939 test
944 test
940 $ mkdir -p tmp/sub/repo
945 $ mkdir -p tmp/sub/repo
941 $ hg cat -r 0 --output tmp/%p_p sub/repo/foo
946 $ hg cat -r 0 --output tmp/%p_p sub/repo/foo
942 $ cat tmp/sub/repo/foo_p
947 $ cat tmp/sub/repo/foo_p
943 test
948 test
944 $ mv sub/repo sub_
949 $ mv sub/repo sub_
945 $ hg cat sub/repo/baz
950 $ hg cat sub/repo/baz
946 skipping missing subrepository: sub/repo
951 skipping missing subrepository: sub/repo
947 [1]
952 [1]
948 $ rm -rf sub/repo
953 $ rm -rf sub/repo
949 $ mv sub_ sub/repo
954 $ mv sub_ sub/repo
950 $ cd ..
955 $ cd ..
951
956
952 Create repo without default path, pull top repo, and see what happens on update
957 Create repo without default path, pull top repo, and see what happens on update
953
958
954 $ hg init issue1852b
959 $ hg init issue1852b
955 $ hg -R issue1852b pull issue1852a
960 $ hg -R issue1852b pull issue1852a
956 pulling from issue1852a
961 pulling from issue1852a
957 requesting all changes
962 requesting all changes
958 adding changesets
963 adding changesets
959 adding manifests
964 adding manifests
960 adding file changes
965 adding file changes
961 added 2 changesets with 3 changes to 2 files
966 added 2 changesets with 3 changes to 2 files
962 (run 'hg update' to get a working copy)
967 (run 'hg update' to get a working copy)
963 $ hg -R issue1852b update
968 $ hg -R issue1852b update
964 abort: default path for subrepository not found (in subrepo sub/repo) (glob)
969 abort: default path for subrepository not found (in subrepo sub/repo) (glob)
965 [255]
970 [255]
966
971
967 Ensure a full traceback, not just the SubrepoAbort part
972 Ensure a full traceback, not just the SubrepoAbort part
968
973
969 $ hg -R issue1852b update --traceback 2>&1 | grep 'raise util\.Abort'
974 $ hg -R issue1852b update --traceback 2>&1 | grep 'raise util\.Abort'
970 raise util.Abort(_("default path for subrepository not found"))
975 raise util.Abort(_("default path for subrepository not found"))
971
976
972 Pull -u now doesn't help
977 Pull -u now doesn't help
973
978
974 $ hg -R issue1852b pull -u issue1852a
979 $ hg -R issue1852b pull -u issue1852a
975 pulling from issue1852a
980 pulling from issue1852a
976 searching for changes
981 searching for changes
977 no changes found
982 no changes found
978
983
979 Try the same, but with pull -u
984 Try the same, but with pull -u
980
985
981 $ hg init issue1852c
986 $ hg init issue1852c
982 $ hg -R issue1852c pull -r0 -u issue1852a
987 $ hg -R issue1852c pull -r0 -u issue1852a
983 pulling from issue1852a
988 pulling from issue1852a
984 adding changesets
989 adding changesets
985 adding manifests
990 adding manifests
986 adding file changes
991 adding file changes
987 added 1 changesets with 2 changes to 2 files
992 added 1 changesets with 2 changes to 2 files
988 cloning subrepo sub/repo from issue1852a/sub/repo (glob)
993 cloning subrepo sub/repo from issue1852a/sub/repo (glob)
989 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
994 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
990
995
991 Try to push from the other side
996 Try to push from the other side
992
997
993 $ hg -R issue1852a push `pwd`/issue1852c
998 $ hg -R issue1852a push `pwd`/issue1852c
994 pushing to $TESTTMP/issue1852c (glob)
999 pushing to $TESTTMP/issue1852c (glob)
995 pushing subrepo sub/repo to $TESTTMP/issue1852c/sub/repo (glob)
1000 pushing subrepo sub/repo to $TESTTMP/issue1852c/sub/repo (glob)
996 searching for changes
1001 searching for changes
997 no changes found
1002 no changes found
998 searching for changes
1003 searching for changes
999 adding changesets
1004 adding changesets
1000 adding manifests
1005 adding manifests
1001 adding file changes
1006 adding file changes
1002 added 1 changesets with 1 changes to 1 files
1007 added 1 changesets with 1 changes to 1 files
1003
1008
1004 Incoming and outgoing should not use the default path:
1009 Incoming and outgoing should not use the default path:
1005
1010
1006 $ hg clone -q issue1852a issue1852d
1011 $ hg clone -q issue1852a issue1852d
1007 $ hg -R issue1852d outgoing --subrepos issue1852c
1012 $ hg -R issue1852d outgoing --subrepos issue1852c
1008 comparing with issue1852c
1013 comparing with issue1852c
1009 searching for changes
1014 searching for changes
1010 no changes found
1015 no changes found
1011 comparing with issue1852c/sub/repo
1016 comparing with issue1852c/sub/repo
1012 searching for changes
1017 searching for changes
1013 no changes found
1018 no changes found
1014 [1]
1019 [1]
1015 $ hg -R issue1852d incoming --subrepos issue1852c
1020 $ hg -R issue1852d incoming --subrepos issue1852c
1016 comparing with issue1852c
1021 comparing with issue1852c
1017 searching for changes
1022 searching for changes
1018 no changes found
1023 no changes found
1019 comparing with issue1852c/sub/repo
1024 comparing with issue1852c/sub/repo
1020 searching for changes
1025 searching for changes
1021 no changes found
1026 no changes found
1022 [1]
1027 [1]
1023
1028
1024 Check status of files when none of them belong to the first
1029 Check status of files when none of them belong to the first
1025 subrepository:
1030 subrepository:
1026
1031
1027 $ hg init subrepo-status
1032 $ hg init subrepo-status
1028 $ cd subrepo-status
1033 $ cd subrepo-status
1029 $ hg init subrepo-1
1034 $ hg init subrepo-1
1030 $ hg init subrepo-2
1035 $ hg init subrepo-2
1031 $ cd subrepo-2
1036 $ cd subrepo-2
1032 $ touch file
1037 $ touch file
1033 $ hg add file
1038 $ hg add file
1034 $ cd ..
1039 $ cd ..
1035 $ echo subrepo-1 = subrepo-1 > .hgsub
1040 $ echo subrepo-1 = subrepo-1 > .hgsub
1036 $ echo subrepo-2 = subrepo-2 >> .hgsub
1041 $ echo subrepo-2 = subrepo-2 >> .hgsub
1037 $ hg add .hgsub
1042 $ hg add .hgsub
1038 $ hg ci -m 'Added subrepos'
1043 $ hg ci -m 'Added subrepos'
1039 committing subrepository subrepo-2
1044 committing subrepository subrepo-2
1040 $ hg st subrepo-2/file
1045 $ hg st subrepo-2/file
1041
1046
1042 Check that share works with subrepo
1047 Check that share works with subrepo
1043 $ hg --config extensions.share= share . ../shared
1048 $ hg --config extensions.share= share . ../shared
1044 updating working directory
1049 updating working directory
1045 cloning subrepo subrepo-2 from $TESTTMP/subrepo-status/subrepo-2
1050 cloning subrepo subrepo-2 from $TESTTMP/subrepo-status/subrepo-2
1046 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1051 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1047 $ test -f ../shared/subrepo-1/.hg/sharedpath
1052 $ test -f ../shared/subrepo-1/.hg/sharedpath
1048 [1]
1053 [1]
1049 $ hg -R ../shared in
1054 $ hg -R ../shared in
1050 abort: repository default not found!
1055 abort: repository default not found!
1051 [255]
1056 [255]
1052 $ hg -R ../shared/subrepo-2 showconfig paths
1057 $ hg -R ../shared/subrepo-2 showconfig paths
1053 paths.default=$TESTTMP/subrepo-status/subrepo-2
1058 paths.default=$TESTTMP/subrepo-status/subrepo-2
1054 $ hg -R ../shared/subrepo-1 sum --remote
1059 $ hg -R ../shared/subrepo-1 sum --remote
1055 parent: -1:000000000000 tip (empty repository)
1060 parent: -1:000000000000 tip (empty repository)
1056 branch: default
1061 branch: default
1057 commit: (clean)
1062 commit: (clean)
1058 update: (current)
1063 update: (current)
1059 remote: (synced)
1064 remote: (synced)
1060
1065
1061 Check hg update --clean
1066 Check hg update --clean
1062 $ cd $TESTTMP/t
1067 $ cd $TESTTMP/t
1063 $ rm -r t/t.orig
1068 $ rm -r t/t.orig
1064 $ hg status -S --all
1069 $ hg status -S --all
1065 C .hgsub
1070 C .hgsub
1066 C .hgsubstate
1071 C .hgsubstate
1067 C a
1072 C a
1068 C s/.hgsub
1073 C s/.hgsub
1069 C s/.hgsubstate
1074 C s/.hgsubstate
1070 C s/a
1075 C s/a
1071 C s/ss/a
1076 C s/ss/a
1072 C t/t
1077 C t/t
1073 $ echo c1 > s/a
1078 $ echo c1 > s/a
1074 $ cd s
1079 $ cd s
1075 $ echo c1 > b
1080 $ echo c1 > b
1076 $ echo c1 > c
1081 $ echo c1 > c
1077 $ hg add b
1082 $ hg add b
1078 $ cd ..
1083 $ cd ..
1079 $ hg status -S
1084 $ hg status -S
1080 M s/a
1085 M s/a
1081 A s/b
1086 A s/b
1082 ? s/c
1087 ? s/c
1083 $ hg update -C
1088 $ hg update -C
1084 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1089 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1085 $ hg status -S
1090 $ hg status -S
1086 ? s/b
1091 ? s/b
1087 ? s/c
1092 ? s/c
1088
1093
1089 Sticky subrepositories, no changes
1094 Sticky subrepositories, no changes
1090 $ cd $TESTTMP/t
1095 $ cd $TESTTMP/t
1091 $ hg id
1096 $ hg id
1092 925c17564ef8 tip
1097 925c17564ef8 tip
1093 $ hg -R s id
1098 $ hg -R s id
1094 12a213df6fa9 tip
1099 12a213df6fa9 tip
1095 $ hg -R t id
1100 $ hg -R t id
1096 52c0adc0515a tip
1101 52c0adc0515a tip
1097 $ hg update 11
1102 $ hg update 11
1098 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1103 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1099 $ hg id
1104 $ hg id
1100 365661e5936a
1105 365661e5936a
1101 $ hg -R s id
1106 $ hg -R s id
1102 fc627a69481f
1107 fc627a69481f
1103 $ hg -R t id
1108 $ hg -R t id
1104 e95bcfa18a35
1109 e95bcfa18a35
1105
1110
1106 Sticky subrepositories, file changes
1111 Sticky subrepositories, file changes
1107 $ touch s/f1
1112 $ touch s/f1
1108 $ touch t/f1
1113 $ touch t/f1
1109 $ hg add -S s/f1
1114 $ hg add -S s/f1
1110 $ hg add -S t/f1
1115 $ hg add -S t/f1
1111 $ hg id
1116 $ hg id
1112 365661e5936a+
1117 365661e5936a+
1113 $ hg -R s id
1118 $ hg -R s id
1114 fc627a69481f+
1119 fc627a69481f+
1115 $ hg -R t id
1120 $ hg -R t id
1116 e95bcfa18a35+
1121 e95bcfa18a35+
1117 $ hg update tip
1122 $ hg update tip
1118 subrepository s diverged (local revision: fc627a69481f, remote revision: 12a213df6fa9)
1123 subrepository s diverged (local revision: fc627a69481f, remote revision: 12a213df6fa9)
1119 (M)erge, keep (l)ocal or keep (r)emote? m
1124 (M)erge, keep (l)ocal or keep (r)emote? m
1120 subrepository sources for s differ
1125 subrepository sources for s differ
1121 use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9)? l
1126 use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9)? l
1122 subrepository t diverged (local revision: e95bcfa18a35, remote revision: 52c0adc0515a)
1127 subrepository t diverged (local revision: e95bcfa18a35, remote revision: 52c0adc0515a)
1123 (M)erge, keep (l)ocal or keep (r)emote? m
1128 (M)erge, keep (l)ocal or keep (r)emote? m
1124 subrepository sources for t differ
1129 subrepository sources for t differ
1125 use (l)ocal source (e95bcfa18a35) or (r)emote source (52c0adc0515a)? l
1130 use (l)ocal source (e95bcfa18a35) or (r)emote source (52c0adc0515a)? l
1126 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1131 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1127 $ hg id
1132 $ hg id
1128 925c17564ef8+ tip
1133 925c17564ef8+ tip
1129 $ hg -R s id
1134 $ hg -R s id
1130 fc627a69481f+
1135 fc627a69481f+
1131 $ hg -R t id
1136 $ hg -R t id
1132 e95bcfa18a35+
1137 e95bcfa18a35+
1133 $ hg update --clean tip
1138 $ hg update --clean tip
1134 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1139 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1135
1140
1136 Sticky subrepository, revision updates
1141 Sticky subrepository, revision updates
1137 $ hg id
1142 $ hg id
1138 925c17564ef8 tip
1143 925c17564ef8 tip
1139 $ hg -R s id
1144 $ hg -R s id
1140 12a213df6fa9 tip
1145 12a213df6fa9 tip
1141 $ hg -R t id
1146 $ hg -R t id
1142 52c0adc0515a tip
1147 52c0adc0515a tip
1143 $ cd s
1148 $ cd s
1144 $ hg update -r -2
1149 $ hg update -r -2
1145 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1150 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1146 $ cd ../t
1151 $ cd ../t
1147 $ hg update -r 2
1152 $ hg update -r 2
1148 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1153 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1149 $ cd ..
1154 $ cd ..
1150 $ hg update 10
1155 $ hg update 10
1151 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1156 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1152 (M)erge, keep (l)ocal or keep (r)emote? m
1157 (M)erge, keep (l)ocal or keep (r)emote? m
1153 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 20a0db6fbf6c)
1158 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 20a0db6fbf6c)
1154 (M)erge, keep (l)ocal or keep (r)emote? m
1159 (M)erge, keep (l)ocal or keep (r)emote? m
1155 subrepository sources for t differ (in checked out version)
1160 subrepository sources for t differ (in checked out version)
1156 use (l)ocal source (7af322bc1198) or (r)emote source (20a0db6fbf6c)? l
1161 use (l)ocal source (7af322bc1198) or (r)emote source (20a0db6fbf6c)? l
1157 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1162 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1158 $ hg id
1163 $ hg id
1159 e45c8b14af55+
1164 e45c8b14af55+
1160 $ hg -R s id
1165 $ hg -R s id
1161 02dcf1d70411
1166 02dcf1d70411
1162 $ hg -R t id
1167 $ hg -R t id
1163 7af322bc1198
1168 7af322bc1198
1164
1169
1165 Sticky subrepository, file changes and revision updates
1170 Sticky subrepository, file changes and revision updates
1166 $ touch s/f1
1171 $ touch s/f1
1167 $ touch t/f1
1172 $ touch t/f1
1168 $ hg add -S s/f1
1173 $ hg add -S s/f1
1169 $ hg add -S t/f1
1174 $ hg add -S t/f1
1170 $ hg id
1175 $ hg id
1171 e45c8b14af55+
1176 e45c8b14af55+
1172 $ hg -R s id
1177 $ hg -R s id
1173 02dcf1d70411+
1178 02dcf1d70411+
1174 $ hg -R t id
1179 $ hg -R t id
1175 7af322bc1198+
1180 7af322bc1198+
1176 $ hg update tip
1181 $ hg update tip
1177 subrepository s diverged (local revision: 12a213df6fa9, remote revision: 12a213df6fa9)
1182 subrepository s diverged (local revision: 12a213df6fa9, remote revision: 12a213df6fa9)
1178 (M)erge, keep (l)ocal or keep (r)emote? m
1183 (M)erge, keep (l)ocal or keep (r)emote? m
1179 subrepository sources for s differ
1184 subrepository sources for s differ
1180 use (l)ocal source (02dcf1d70411) or (r)emote source (12a213df6fa9)? l
1185 use (l)ocal source (02dcf1d70411) or (r)emote source (12a213df6fa9)? l
1181 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 52c0adc0515a)
1186 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 52c0adc0515a)
1182 (M)erge, keep (l)ocal or keep (r)emote? m
1187 (M)erge, keep (l)ocal or keep (r)emote? m
1183 subrepository sources for t differ
1188 subrepository sources for t differ
1184 use (l)ocal source (7af322bc1198) or (r)emote source (52c0adc0515a)? l
1189 use (l)ocal source (7af322bc1198) or (r)emote source (52c0adc0515a)? l
1185 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1190 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1186 $ hg id
1191 $ hg id
1187 925c17564ef8+ tip
1192 925c17564ef8+ tip
1188 $ hg -R s id
1193 $ hg -R s id
1189 02dcf1d70411+
1194 02dcf1d70411+
1190 $ hg -R t id
1195 $ hg -R t id
1191 7af322bc1198+
1196 7af322bc1198+
1192
1197
1193 Sticky repository, update --clean
1198 Sticky repository, update --clean
1194 $ hg update --clean tip
1199 $ hg update --clean tip
1195 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1200 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1196 $ hg id
1201 $ hg id
1197 925c17564ef8 tip
1202 925c17564ef8 tip
1198 $ hg -R s id
1203 $ hg -R s id
1199 12a213df6fa9 tip
1204 12a213df6fa9 tip
1200 $ hg -R t id
1205 $ hg -R t id
1201 52c0adc0515a tip
1206 52c0adc0515a tip
1202
1207
1203 Test subrepo already at intended revision:
1208 Test subrepo already at intended revision:
1204 $ cd s
1209 $ cd s
1205 $ hg update fc627a69481f
1210 $ hg update fc627a69481f
1206 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1211 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1207 $ cd ..
1212 $ cd ..
1208 $ hg update 11
1213 $ hg update 11
1209 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1214 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1210 (M)erge, keep (l)ocal or keep (r)emote? m
1215 (M)erge, keep (l)ocal or keep (r)emote? m
1211 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1216 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1212 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1217 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1213 $ hg id -n
1218 $ hg id -n
1214 11+
1219 11+
1215 $ hg -R s id
1220 $ hg -R s id
1216 fc627a69481f
1221 fc627a69481f
1217 $ hg -R t id
1222 $ hg -R t id
1218 e95bcfa18a35
1223 e95bcfa18a35
1219
1224
1220 Test that removing .hgsubstate doesn't break anything:
1225 Test that removing .hgsubstate doesn't break anything:
1221
1226
1222 $ hg rm -f .hgsubstate
1227 $ hg rm -f .hgsubstate
1223 $ hg ci -mrm
1228 $ hg ci -mrm
1224 nothing changed
1229 nothing changed
1225 [1]
1230 [1]
1226 $ hg log -vr tip
1231 $ hg log -vr tip
1227 changeset: 13:925c17564ef8
1232 changeset: 13:925c17564ef8
1228 tag: tip
1233 tag: tip
1229 user: test
1234 user: test
1230 date: Thu Jan 01 00:00:00 1970 +0000
1235 date: Thu Jan 01 00:00:00 1970 +0000
1231 files: .hgsubstate
1236 files: .hgsubstate
1232 description:
1237 description:
1233 13
1238 13
1234
1239
1235
1240
1236
1241
1237 Test that removing .hgsub removes .hgsubstate:
1242 Test that removing .hgsub removes .hgsubstate:
1238
1243
1239 $ hg rm .hgsub
1244 $ hg rm .hgsub
1240 $ hg ci -mrm2
1245 $ hg ci -mrm2
1241 created new head
1246 created new head
1242 $ hg log -vr tip
1247 $ hg log -vr tip
1243 changeset: 14:2400bccd50af
1248 changeset: 14:2400bccd50af
1244 tag: tip
1249 tag: tip
1245 parent: 11:365661e5936a
1250 parent: 11:365661e5936a
1246 user: test
1251 user: test
1247 date: Thu Jan 01 00:00:00 1970 +0000
1252 date: Thu Jan 01 00:00:00 1970 +0000
1248 files: .hgsub .hgsubstate
1253 files: .hgsub .hgsubstate
1249 description:
1254 description:
1250 rm2
1255 rm2
1251
1256
1252
1257
1253 Test issue3153: diff -S with deleted subrepos
1258 Test issue3153: diff -S with deleted subrepos
1254
1259
1255 $ hg diff --nodates -S -c .
1260 $ hg diff --nodates -S -c .
1256 diff -r 365661e5936a -r 2400bccd50af .hgsub
1261 diff -r 365661e5936a -r 2400bccd50af .hgsub
1257 --- a/.hgsub
1262 --- a/.hgsub
1258 +++ /dev/null
1263 +++ /dev/null
1259 @@ -1,2 +0,0 @@
1264 @@ -1,2 +0,0 @@
1260 -s = s
1265 -s = s
1261 -t = t
1266 -t = t
1262 diff -r 365661e5936a -r 2400bccd50af .hgsubstate
1267 diff -r 365661e5936a -r 2400bccd50af .hgsubstate
1263 --- a/.hgsubstate
1268 --- a/.hgsubstate
1264 +++ /dev/null
1269 +++ /dev/null
1265 @@ -1,2 +0,0 @@
1270 @@ -1,2 +0,0 @@
1266 -fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1271 -fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1267 -e95bcfa18a358dc4936da981ebf4147b4cad1362 t
1272 -e95bcfa18a358dc4936da981ebf4147b4cad1362 t
1268
1273
1269 Test behavior of add for explicit path in subrepo:
1274 Test behavior of add for explicit path in subrepo:
1270 $ cd ..
1275 $ cd ..
1271 $ hg init explicit
1276 $ hg init explicit
1272 $ cd explicit
1277 $ cd explicit
1273 $ echo s = s > .hgsub
1278 $ echo s = s > .hgsub
1274 $ hg add .hgsub
1279 $ hg add .hgsub
1275 $ hg init s
1280 $ hg init s
1276 $ hg ci -m0
1281 $ hg ci -m0
1277 Adding with an explicit path in a subrepo adds the file
1282 Adding with an explicit path in a subrepo adds the file
1278 $ echo c1 > f1
1283 $ echo c1 > f1
1279 $ echo c2 > s/f2
1284 $ echo c2 > s/f2
1280 $ hg st -S
1285 $ hg st -S
1281 ? f1
1286 ? f1
1282 ? s/f2
1287 ? s/f2
1283 $ hg add s/f2
1288 $ hg add s/f2
1284 $ hg st -S
1289 $ hg st -S
1285 A s/f2
1290 A s/f2
1286 ? f1
1291 ? f1
1287 $ hg ci -R s -m0
1292 $ hg ci -R s -m0
1288 $ hg ci -Am1
1293 $ hg ci -Am1
1289 adding f1
1294 adding f1
1290 Adding with an explicit path in a subrepo with -S has the same behavior
1295 Adding with an explicit path in a subrepo with -S has the same behavior
1291 $ echo c3 > f3
1296 $ echo c3 > f3
1292 $ echo c4 > s/f4
1297 $ echo c4 > s/f4
1293 $ hg st -S
1298 $ hg st -S
1294 ? f3
1299 ? f3
1295 ? s/f4
1300 ? s/f4
1296 $ hg add -S s/f4
1301 $ hg add -S s/f4
1297 $ hg st -S
1302 $ hg st -S
1298 A s/f4
1303 A s/f4
1299 ? f3
1304 ? f3
1300 $ hg ci -R s -m1
1305 $ hg ci -R s -m1
1301 $ hg ci -Ama2
1306 $ hg ci -Ama2
1302 adding f3
1307 adding f3
1303 Adding without a path or pattern silently ignores subrepos
1308 Adding without a path or pattern silently ignores subrepos
1304 $ echo c5 > f5
1309 $ echo c5 > f5
1305 $ echo c6 > s/f6
1310 $ echo c6 > s/f6
1306 $ echo c7 > s/f7
1311 $ echo c7 > s/f7
1307 $ hg st -S
1312 $ hg st -S
1308 ? f5
1313 ? f5
1309 ? s/f6
1314 ? s/f6
1310 ? s/f7
1315 ? s/f7
1311 $ hg add
1316 $ hg add
1312 adding f5
1317 adding f5
1313 $ hg st -S
1318 $ hg st -S
1314 A f5
1319 A f5
1315 ? s/f6
1320 ? s/f6
1316 ? s/f7
1321 ? s/f7
1317 $ hg ci -R s -Am2
1322 $ hg ci -R s -Am2
1318 adding f6
1323 adding f6
1319 adding f7
1324 adding f7
1320 $ hg ci -m3
1325 $ hg ci -m3
1321 Adding without a path or pattern with -S also adds files in subrepos
1326 Adding without a path or pattern with -S also adds files in subrepos
1322 $ echo c8 > f8
1327 $ echo c8 > f8
1323 $ echo c9 > s/f9
1328 $ echo c9 > s/f9
1324 $ echo c10 > s/f10
1329 $ echo c10 > s/f10
1325 $ hg st -S
1330 $ hg st -S
1326 ? f8
1331 ? f8
1327 ? s/f10
1332 ? s/f10
1328 ? s/f9
1333 ? s/f9
1329 $ hg add -S
1334 $ hg add -S
1330 adding f8
1335 adding f8
1331 adding s/f10 (glob)
1336 adding s/f10 (glob)
1332 adding s/f9 (glob)
1337 adding s/f9 (glob)
1333 $ hg st -S
1338 $ hg st -S
1334 A f8
1339 A f8
1335 A s/f10
1340 A s/f10
1336 A s/f9
1341 A s/f9
1337 $ hg ci -R s -m3
1342 $ hg ci -R s -m3
1338 $ hg ci -m4
1343 $ hg ci -m4
1339 Adding with a pattern silently ignores subrepos
1344 Adding with a pattern silently ignores subrepos
1340 $ echo c11 > fm11
1345 $ echo c11 > fm11
1341 $ echo c12 > fn12
1346 $ echo c12 > fn12
1342 $ echo c13 > s/fm13
1347 $ echo c13 > s/fm13
1343 $ echo c14 > s/fn14
1348 $ echo c14 > s/fn14
1344 $ hg st -S
1349 $ hg st -S
1345 ? fm11
1350 ? fm11
1346 ? fn12
1351 ? fn12
1347 ? s/fm13
1352 ? s/fm13
1348 ? s/fn14
1353 ? s/fn14
1349 $ hg add 'glob:**fm*'
1354 $ hg add 'glob:**fm*'
1350 adding fm11
1355 adding fm11
1351 $ hg st -S
1356 $ hg st -S
1352 A fm11
1357 A fm11
1353 ? fn12
1358 ? fn12
1354 ? s/fm13
1359 ? s/fm13
1355 ? s/fn14
1360 ? s/fn14
1356 $ hg ci -R s -Am4
1361 $ hg ci -R s -Am4
1357 adding fm13
1362 adding fm13
1358 adding fn14
1363 adding fn14
1359 $ hg ci -Am5
1364 $ hg ci -Am5
1360 adding fn12
1365 adding fn12
1361 Adding with a pattern with -S also adds matches in subrepos
1366 Adding with a pattern with -S also adds matches in subrepos
1362 $ echo c15 > fm15
1367 $ echo c15 > fm15
1363 $ echo c16 > fn16
1368 $ echo c16 > fn16
1364 $ echo c17 > s/fm17
1369 $ echo c17 > s/fm17
1365 $ echo c18 > s/fn18
1370 $ echo c18 > s/fn18
1366 $ hg st -S
1371 $ hg st -S
1367 ? fm15
1372 ? fm15
1368 ? fn16
1373 ? fn16
1369 ? s/fm17
1374 ? s/fm17
1370 ? s/fn18
1375 ? s/fn18
1371 $ hg add -S 'glob:**fm*'
1376 $ hg add -S 'glob:**fm*'
1372 adding fm15
1377 adding fm15
1373 adding s/fm17 (glob)
1378 adding s/fm17 (glob)
1374 $ hg st -S
1379 $ hg st -S
1375 A fm15
1380 A fm15
1376 A s/fm17
1381 A s/fm17
1377 ? fn16
1382 ? fn16
1378 ? s/fn18
1383 ? s/fn18
1379 $ hg ci -R s -Am5
1384 $ hg ci -R s -Am5
1380 adding fn18
1385 adding fn18
1381 $ hg ci -Am6
1386 $ hg ci -Am6
1382 adding fn16
1387 adding fn16
1383
1388
1384 Test behavior of forget for explicit path in subrepo:
1389 Test behavior of forget for explicit path in subrepo:
1385 Forgetting an explicit path in a subrepo untracks the file
1390 Forgetting an explicit path in a subrepo untracks the file
1386 $ echo c19 > s/f19
1391 $ echo c19 > s/f19
1387 $ hg add s/f19
1392 $ hg add s/f19
1388 $ hg st -S
1393 $ hg st -S
1389 A s/f19
1394 A s/f19
1390 $ hg forget s/f19
1395 $ hg forget s/f19
1391 $ hg st -S
1396 $ hg st -S
1392 ? s/f19
1397 ? s/f19
1393 $ rm s/f19
1398 $ rm s/f19
1394 $ cd ..
1399 $ cd ..
1395
1400
1396 Courtesy phases synchronisation to publishing server does not block the push
1401 Courtesy phases synchronisation to publishing server does not block the push
1397 (issue3781)
1402 (issue3781)
1398
1403
1399 $ cp -r main issue3781
1404 $ cp -r main issue3781
1400 $ cp -r main issue3781-dest
1405 $ cp -r main issue3781-dest
1401 $ cd issue3781-dest/s
1406 $ cd issue3781-dest/s
1402 $ hg phase tip # show we have draft changeset
1407 $ hg phase tip # show we have draft changeset
1403 5: draft
1408 5: draft
1404 $ chmod a-w .hg/store/phaseroots # prevent phase push
1409 $ chmod a-w .hg/store/phaseroots # prevent phase push
1405 $ cd ../../issue3781
1410 $ cd ../../issue3781
1406 $ cat >> .hg/hgrc << EOF
1411 $ cat >> .hg/hgrc << EOF
1407 > [paths]
1412 > [paths]
1408 > default=../issue3781-dest/
1413 > default=../issue3781-dest/
1409 > EOF
1414 > EOF
1410 $ hg push
1415 $ hg push
1411 pushing to $TESTTMP/issue3781-dest (glob)
1416 pushing to $TESTTMP/issue3781-dest (glob)
1412 pushing subrepo s to $TESTTMP/issue3781-dest/s
1417 pushing subrepo s to $TESTTMP/issue3781-dest/s
1413 searching for changes
1418 searching for changes
1414 no changes found
1419 no changes found
1415 searching for changes
1420 searching for changes
1416 no changes found
1421 no changes found
1417 [1]
1422 [1]
1418 $ cd ..
1423 $ cd ..
1419
1424
1420 Test phase choice for newly created commit with "phases.subrepochecks"
1425 Test phase choice for newly created commit with "phases.subrepochecks"
1421 configuration
1426 configuration
1422
1427
1423 $ cd t
1428 $ cd t
1424 $ hg update -q -r 12
1429 $ hg update -q -r 12
1425
1430
1426 $ cat >> s/ss/.hg/hgrc <<EOF
1431 $ cat >> s/ss/.hg/hgrc <<EOF
1427 > [phases]
1432 > [phases]
1428 > new-commit = secret
1433 > new-commit = secret
1429 > EOF
1434 > EOF
1430 $ cat >> s/.hg/hgrc <<EOF
1435 $ cat >> s/.hg/hgrc <<EOF
1431 > [phases]
1436 > [phases]
1432 > new-commit = draft
1437 > new-commit = draft
1433 > EOF
1438 > EOF
1434 $ echo phasecheck1 >> s/ss/a
1439 $ echo phasecheck1 >> s/ss/a
1435 $ hg -R s commit -S --config phases.checksubrepos=abort -m phasecheck1
1440 $ hg -R s commit -S --config phases.checksubrepos=abort -m phasecheck1
1436 committing subrepository ss
1441 committing subrepository ss
1437 transaction abort!
1442 transaction abort!
1438 rollback completed
1443 rollback completed
1439 abort: can't commit in draft phase conflicting secret from subrepository ss
1444 abort: can't commit in draft phase conflicting secret from subrepository ss
1440 [255]
1445 [255]
1441 $ echo phasecheck2 >> s/ss/a
1446 $ echo phasecheck2 >> s/ss/a
1442 $ hg -R s commit -S --config phases.checksubrepos=ignore -m phasecheck2
1447 $ hg -R s commit -S --config phases.checksubrepos=ignore -m phasecheck2
1443 committing subrepository ss
1448 committing subrepository ss
1444 $ hg -R s/ss phase tip
1449 $ hg -R s/ss phase tip
1445 3: secret
1450 3: secret
1446 $ hg -R s phase tip
1451 $ hg -R s phase tip
1447 6: draft
1452 6: draft
1448 $ echo phasecheck3 >> s/ss/a
1453 $ echo phasecheck3 >> s/ss/a
1449 $ hg -R s commit -S -m phasecheck3
1454 $ hg -R s commit -S -m phasecheck3
1450 committing subrepository ss
1455 committing subrepository ss
1451 warning: changes are committed in secret phase from subrepository ss
1456 warning: changes are committed in secret phase from subrepository ss
1452 $ hg -R s/ss phase tip
1457 $ hg -R s/ss phase tip
1453 4: secret
1458 4: secret
1454 $ hg -R s phase tip
1459 $ hg -R s phase tip
1455 7: secret
1460 7: secret
1456
1461
1457 $ cat >> t/.hg/hgrc <<EOF
1462 $ cat >> t/.hg/hgrc <<EOF
1458 > [phases]
1463 > [phases]
1459 > new-commit = draft
1464 > new-commit = draft
1460 > EOF
1465 > EOF
1461 $ cat >> .hg/hgrc <<EOF
1466 $ cat >> .hg/hgrc <<EOF
1462 > [phases]
1467 > [phases]
1463 > new-commit = public
1468 > new-commit = public
1464 > EOF
1469 > EOF
1465 $ echo phasecheck4 >> s/ss/a
1470 $ echo phasecheck4 >> s/ss/a
1466 $ echo phasecheck4 >> t/t
1471 $ echo phasecheck4 >> t/t
1467 $ hg commit -S -m phasecheck4
1472 $ hg commit -S -m phasecheck4
1468 committing subrepository s
1473 committing subrepository s
1469 committing subrepository s/ss (glob)
1474 committing subrepository s/ss (glob)
1470 warning: changes are committed in secret phase from subrepository ss
1475 warning: changes are committed in secret phase from subrepository ss
1471 committing subrepository t
1476 committing subrepository t
1472 warning: changes are committed in secret phase from subrepository s
1477 warning: changes are committed in secret phase from subrepository s
1473 created new head
1478 created new head
1474 $ hg -R s/ss phase tip
1479 $ hg -R s/ss phase tip
1475 5: secret
1480 5: secret
1476 $ hg -R s phase tip
1481 $ hg -R s phase tip
1477 8: secret
1482 8: secret
1478 $ hg -R t phase tip
1483 $ hg -R t phase tip
1479 6: draft
1484 6: draft
1480 $ hg phase tip
1485 $ hg phase tip
1481 15: secret
1486 15: secret
1482
1487
1483 $ cd ..
1488 $ cd ..
1484
1489
1485
1490
1486 Test that commit --secret works on both repo and subrepo (issue4182)
1491 Test that commit --secret works on both repo and subrepo (issue4182)
1487
1492
1488 $ cd main
1493 $ cd main
1489 $ echo secret >> b
1494 $ echo secret >> b
1490 $ echo secret >> s/b
1495 $ echo secret >> s/b
1491 $ hg commit --secret --subrepo -m "secret"
1496 $ hg commit --secret --subrepo -m "secret"
1492 committing subrepository s
1497 committing subrepository s
1493 $ hg phase -r .
1498 $ hg phase -r .
1494 6: secret
1499 6: secret
1495 $ cd s
1500 $ cd s
1496 $ hg phase -r .
1501 $ hg phase -r .
1497 6: secret
1502 6: secret
1498 $ cd ../../
1503 $ cd ../../
1499
1504
1500 Test "subrepos" template keyword
1505 Test "subrepos" template keyword
1501
1506
1502 $ cd t
1507 $ cd t
1503 $ hg update -q 15
1508 $ hg update -q 15
1504 $ cat > .hgsub <<EOF
1509 $ cat > .hgsub <<EOF
1505 > s = s
1510 > s = s
1506 > EOF
1511 > EOF
1507 $ hg commit -m "16"
1512 $ hg commit -m "16"
1508 warning: changes are committed in secret phase from subrepository s
1513 warning: changes are committed in secret phase from subrepository s
1509
1514
1510 (addition of ".hgsub" itself)
1515 (addition of ".hgsub" itself)
1511
1516
1512 $ hg diff --nodates -c 1 .hgsubstate
1517 $ hg diff --nodates -c 1 .hgsubstate
1513 diff -r f7b1eb17ad24 -r 7cf8cfea66e4 .hgsubstate
1518 diff -r f7b1eb17ad24 -r 7cf8cfea66e4 .hgsubstate
1514 --- /dev/null
1519 --- /dev/null
1515 +++ b/.hgsubstate
1520 +++ b/.hgsubstate
1516 @@ -0,0 +1,1 @@
1521 @@ -0,0 +1,1 @@
1517 +e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1522 +e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1518 $ hg log -r 1 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1523 $ hg log -r 1 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1519 f7b1eb17ad24 000000000000
1524 f7b1eb17ad24 000000000000
1520 s
1525 s
1521
1526
1522 (modification of existing entry)
1527 (modification of existing entry)
1523
1528
1524 $ hg diff --nodates -c 2 .hgsubstate
1529 $ hg diff --nodates -c 2 .hgsubstate
1525 diff -r 7cf8cfea66e4 -r df30734270ae .hgsubstate
1530 diff -r 7cf8cfea66e4 -r df30734270ae .hgsubstate
1526 --- a/.hgsubstate
1531 --- a/.hgsubstate
1527 +++ b/.hgsubstate
1532 +++ b/.hgsubstate
1528 @@ -1,1 +1,1 @@
1533 @@ -1,1 +1,1 @@
1529 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1534 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1530 +dc73e2e6d2675eb2e41e33c205f4bdab4ea5111d s
1535 +dc73e2e6d2675eb2e41e33c205f4bdab4ea5111d s
1531 $ hg log -r 2 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1536 $ hg log -r 2 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1532 7cf8cfea66e4 000000000000
1537 7cf8cfea66e4 000000000000
1533 s
1538 s
1534
1539
1535 (addition of entry)
1540 (addition of entry)
1536
1541
1537 $ hg diff --nodates -c 5 .hgsubstate
1542 $ hg diff --nodates -c 5 .hgsubstate
1538 diff -r 7cf8cfea66e4 -r 1f14a2e2d3ec .hgsubstate
1543 diff -r 7cf8cfea66e4 -r 1f14a2e2d3ec .hgsubstate
1539 --- a/.hgsubstate
1544 --- a/.hgsubstate
1540 +++ b/.hgsubstate
1545 +++ b/.hgsubstate
1541 @@ -1,1 +1,2 @@
1546 @@ -1,1 +1,2 @@
1542 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1547 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1543 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1548 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1544 $ hg log -r 5 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1549 $ hg log -r 5 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1545 7cf8cfea66e4 000000000000
1550 7cf8cfea66e4 000000000000
1546 t
1551 t
1547
1552
1548 (removal of existing entry)
1553 (removal of existing entry)
1549
1554
1550 $ hg diff --nodates -c 16 .hgsubstate
1555 $ hg diff --nodates -c 16 .hgsubstate
1551 diff -r 8bec38d2bd0b -r f2f70bc3d3c9 .hgsubstate
1556 diff -r 8bec38d2bd0b -r f2f70bc3d3c9 .hgsubstate
1552 --- a/.hgsubstate
1557 --- a/.hgsubstate
1553 +++ b/.hgsubstate
1558 +++ b/.hgsubstate
1554 @@ -1,2 +1,1 @@
1559 @@ -1,2 +1,1 @@
1555 0731af8ca9423976d3743119d0865097c07bdc1b s
1560 0731af8ca9423976d3743119d0865097c07bdc1b s
1556 -e202dc79b04c88a636ea8913d9182a1346d9b3dc t
1561 -e202dc79b04c88a636ea8913d9182a1346d9b3dc t
1557 $ hg log -r 16 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1562 $ hg log -r 16 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1558 8bec38d2bd0b 000000000000
1563 8bec38d2bd0b 000000000000
1559 t
1564 t
1560
1565
1561 (merging)
1566 (merging)
1562
1567
1563 $ hg diff --nodates -c 9 .hgsubstate
1568 $ hg diff --nodates -c 9 .hgsubstate
1564 diff -r f6affe3fbfaa -r f0d2028bf86d .hgsubstate
1569 diff -r f6affe3fbfaa -r f0d2028bf86d .hgsubstate
1565 --- a/.hgsubstate
1570 --- a/.hgsubstate
1566 +++ b/.hgsubstate
1571 +++ b/.hgsubstate
1567 @@ -1,1 +1,2 @@
1572 @@ -1,1 +1,2 @@
1568 fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1573 fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1569 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1574 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1570 $ hg log -r 9 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1575 $ hg log -r 9 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1571 f6affe3fbfaa 1f14a2e2d3ec
1576 f6affe3fbfaa 1f14a2e2d3ec
1572 t
1577 t
1573
1578
1574 (removal of ".hgsub" itself)
1579 (removal of ".hgsub" itself)
1575
1580
1576 $ hg diff --nodates -c 8 .hgsubstate
1581 $ hg diff --nodates -c 8 .hgsubstate
1577 diff -r f94576341bcf -r 96615c1dad2d .hgsubstate
1582 diff -r f94576341bcf -r 96615c1dad2d .hgsubstate
1578 --- a/.hgsubstate
1583 --- a/.hgsubstate
1579 +++ /dev/null
1584 +++ /dev/null
1580 @@ -1,2 +0,0 @@
1585 @@ -1,2 +0,0 @@
1581 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1586 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1582 -7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4 t
1587 -7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4 t
1583 $ hg log -r 8 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1588 $ hg log -r 8 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1584 f94576341bcf 000000000000
1589 f94576341bcf 000000000000
1585
1590
1586 Test that '[paths]' is configured correctly at subrepo creation
1591 Test that '[paths]' is configured correctly at subrepo creation
1587
1592
1588 $ cd $TESTTMP/tc
1593 $ cd $TESTTMP/tc
1589 $ cat > .hgsub <<EOF
1594 $ cat > .hgsub <<EOF
1590 > # to clear bogus subrepo path 'bogus=[boguspath'
1595 > # to clear bogus subrepo path 'bogus=[boguspath'
1591 > s = s
1596 > s = s
1592 > t = t
1597 > t = t
1593 > EOF
1598 > EOF
1594 $ hg update -q --clean null
1599 $ hg update -q --clean null
1595 $ rm -rf s t
1600 $ rm -rf s t
1596 $ cat >> .hg/hgrc <<EOF
1601 $ cat >> .hg/hgrc <<EOF
1597 > [paths]
1602 > [paths]
1598 > default-push = /foo/bar
1603 > default-push = /foo/bar
1599 > EOF
1604 > EOF
1600 $ hg update -q
1605 $ hg update -q
1601 $ cat s/.hg/hgrc
1606 $ cat s/.hg/hgrc
1602 [paths]
1607 [paths]
1603 default = $TESTTMP/t/s
1608 default = $TESTTMP/t/s
1604 default-push = /foo/bar/s
1609 default-push = /foo/bar/s
1605 $ cat s/ss/.hg/hgrc
1610 $ cat s/ss/.hg/hgrc
1606 [paths]
1611 [paths]
1607 default = $TESTTMP/t/s/ss
1612 default = $TESTTMP/t/s/ss
1608 default-push = /foo/bar/s/ss
1613 default-push = /foo/bar/s/ss
1609 $ cat t/.hg/hgrc
1614 $ cat t/.hg/hgrc
1610 [paths]
1615 [paths]
1611 default = $TESTTMP/t/t
1616 default = $TESTTMP/t/t
1612 default-push = /foo/bar/t
1617 default-push = /foo/bar/t
1613 $ cd ..
1618 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now