##// END OF EJS Templates
serve: add support for Mercurial subrepositories...
Matt Harbison -
r32005:2406dbba default
parent child Browse files
Show More
@@ -1,3474 +1,3483 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import itertools
11 import itertools
12 import os
12 import os
13 import re
13 import re
14 import tempfile
14 import tempfile
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import (
17 from .node import (
18 bin,
18 bin,
19 hex,
19 hex,
20 nullid,
20 nullid,
21 nullrev,
21 nullrev,
22 short,
22 short,
23 )
23 )
24
24
25 from . import (
25 from . import (
26 bookmarks,
26 bookmarks,
27 changelog,
27 changelog,
28 copies,
28 copies,
29 crecord as crecordmod,
29 crecord as crecordmod,
30 encoding,
30 encoding,
31 error,
31 error,
32 formatter,
32 formatter,
33 graphmod,
33 graphmod,
34 lock as lockmod,
34 lock as lockmod,
35 match as matchmod,
35 match as matchmod,
36 obsolete,
36 obsolete,
37 patch,
37 patch,
38 pathutil,
38 pathutil,
39 phases,
39 phases,
40 pycompat,
40 pycompat,
41 repair,
41 repair,
42 revlog,
42 revlog,
43 revset,
43 revset,
44 scmutil,
44 scmutil,
45 smartset,
45 smartset,
46 templatekw,
46 templatekw,
47 templater,
47 templater,
48 util,
48 util,
49 vfs as vfsmod,
49 vfs as vfsmod,
50 )
50 )
51 stringio = util.stringio
51 stringio = util.stringio
52
52
53 # special string such that everything below this line will be ingored in the
53 # special string such that everything below this line will be ingored in the
54 # editor text
54 # editor text
55 _linebelow = "^HG: ------------------------ >8 ------------------------$"
55 _linebelow = "^HG: ------------------------ >8 ------------------------$"
56
56
57 def ishunk(x):
57 def ishunk(x):
58 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
58 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
59 return isinstance(x, hunkclasses)
59 return isinstance(x, hunkclasses)
60
60
61 def newandmodified(chunks, originalchunks):
61 def newandmodified(chunks, originalchunks):
62 newlyaddedandmodifiedfiles = set()
62 newlyaddedandmodifiedfiles = set()
63 for chunk in chunks:
63 for chunk in chunks:
64 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
64 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
65 originalchunks:
65 originalchunks:
66 newlyaddedandmodifiedfiles.add(chunk.header.filename())
66 newlyaddedandmodifiedfiles.add(chunk.header.filename())
67 return newlyaddedandmodifiedfiles
67 return newlyaddedandmodifiedfiles
68
68
69 def parsealiases(cmd):
69 def parsealiases(cmd):
70 return cmd.lstrip("^").split("|")
70 return cmd.lstrip("^").split("|")
71
71
72 def setupwrapcolorwrite(ui):
72 def setupwrapcolorwrite(ui):
73 # wrap ui.write so diff output can be labeled/colorized
73 # wrap ui.write so diff output can be labeled/colorized
74 def wrapwrite(orig, *args, **kw):
74 def wrapwrite(orig, *args, **kw):
75 label = kw.pop('label', '')
75 label = kw.pop('label', '')
76 for chunk, l in patch.difflabel(lambda: args):
76 for chunk, l in patch.difflabel(lambda: args):
77 orig(chunk, label=label + l)
77 orig(chunk, label=label + l)
78
78
79 oldwrite = ui.write
79 oldwrite = ui.write
80 def wrap(*args, **kwargs):
80 def wrap(*args, **kwargs):
81 return wrapwrite(oldwrite, *args, **kwargs)
81 return wrapwrite(oldwrite, *args, **kwargs)
82 setattr(ui, 'write', wrap)
82 setattr(ui, 'write', wrap)
83 return oldwrite
83 return oldwrite
84
84
85 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
85 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
86 if usecurses:
86 if usecurses:
87 if testfile:
87 if testfile:
88 recordfn = crecordmod.testdecorator(testfile,
88 recordfn = crecordmod.testdecorator(testfile,
89 crecordmod.testchunkselector)
89 crecordmod.testchunkselector)
90 else:
90 else:
91 recordfn = crecordmod.chunkselector
91 recordfn = crecordmod.chunkselector
92
92
93 return crecordmod.filterpatch(ui, originalhunks, recordfn, operation)
93 return crecordmod.filterpatch(ui, originalhunks, recordfn, operation)
94
94
95 else:
95 else:
96 return patch.filterpatch(ui, originalhunks, operation)
96 return patch.filterpatch(ui, originalhunks, operation)
97
97
98 def recordfilter(ui, originalhunks, operation=None):
98 def recordfilter(ui, originalhunks, operation=None):
99 """ Prompts the user to filter the originalhunks and return a list of
99 """ Prompts the user to filter the originalhunks and return a list of
100 selected hunks.
100 selected hunks.
101 *operation* is used for to build ui messages to indicate the user what
101 *operation* is used for to build ui messages to indicate the user what
102 kind of filtering they are doing: reverting, committing, shelving, etc.
102 kind of filtering they are doing: reverting, committing, shelving, etc.
103 (see patch.filterpatch).
103 (see patch.filterpatch).
104 """
104 """
105 usecurses = crecordmod.checkcurses(ui)
105 usecurses = crecordmod.checkcurses(ui)
106 testfile = ui.config('experimental', 'crecordtest', None)
106 testfile = ui.config('experimental', 'crecordtest', None)
107 oldwrite = setupwrapcolorwrite(ui)
107 oldwrite = setupwrapcolorwrite(ui)
108 try:
108 try:
109 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
109 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
110 testfile, operation)
110 testfile, operation)
111 finally:
111 finally:
112 ui.write = oldwrite
112 ui.write = oldwrite
113 return newchunks, newopts
113 return newchunks, newopts
114
114
115 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
115 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
116 filterfn, *pats, **opts):
116 filterfn, *pats, **opts):
117 from . import merge as mergemod
117 from . import merge as mergemod
118 if not ui.interactive():
118 if not ui.interactive():
119 if cmdsuggest:
119 if cmdsuggest:
120 msg = _('running non-interactively, use %s instead') % cmdsuggest
120 msg = _('running non-interactively, use %s instead') % cmdsuggest
121 else:
121 else:
122 msg = _('running non-interactively')
122 msg = _('running non-interactively')
123 raise error.Abort(msg)
123 raise error.Abort(msg)
124
124
125 # make sure username is set before going interactive
125 # make sure username is set before going interactive
126 if not opts.get('user'):
126 if not opts.get('user'):
127 ui.username() # raise exception, username not provided
127 ui.username() # raise exception, username not provided
128
128
129 def recordfunc(ui, repo, message, match, opts):
129 def recordfunc(ui, repo, message, match, opts):
130 """This is generic record driver.
130 """This is generic record driver.
131
131
132 Its job is to interactively filter local changes, and
132 Its job is to interactively filter local changes, and
133 accordingly prepare working directory into a state in which the
133 accordingly prepare working directory into a state in which the
134 job can be delegated to a non-interactive commit command such as
134 job can be delegated to a non-interactive commit command such as
135 'commit' or 'qrefresh'.
135 'commit' or 'qrefresh'.
136
136
137 After the actual job is done by non-interactive command, the
137 After the actual job is done by non-interactive command, the
138 working directory is restored to its original state.
138 working directory is restored to its original state.
139
139
140 In the end we'll record interesting changes, and everything else
140 In the end we'll record interesting changes, and everything else
141 will be left in place, so the user can continue working.
141 will be left in place, so the user can continue working.
142 """
142 """
143
143
144 checkunfinished(repo, commit=True)
144 checkunfinished(repo, commit=True)
145 wctx = repo[None]
145 wctx = repo[None]
146 merge = len(wctx.parents()) > 1
146 merge = len(wctx.parents()) > 1
147 if merge:
147 if merge:
148 raise error.Abort(_('cannot partially commit a merge '
148 raise error.Abort(_('cannot partially commit a merge '
149 '(use "hg commit" instead)'))
149 '(use "hg commit" instead)'))
150
150
151 def fail(f, msg):
151 def fail(f, msg):
152 raise error.Abort('%s: %s' % (f, msg))
152 raise error.Abort('%s: %s' % (f, msg))
153
153
154 force = opts.get('force')
154 force = opts.get('force')
155 if not force:
155 if not force:
156 vdirs = []
156 vdirs = []
157 match.explicitdir = vdirs.append
157 match.explicitdir = vdirs.append
158 match.bad = fail
158 match.bad = fail
159
159
160 status = repo.status(match=match)
160 status = repo.status(match=match)
161 if not force:
161 if not force:
162 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
162 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
163 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
163 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
164 diffopts.nodates = True
164 diffopts.nodates = True
165 diffopts.git = True
165 diffopts.git = True
166 diffopts.showfunc = True
166 diffopts.showfunc = True
167 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
167 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
168 originalchunks = patch.parsepatch(originaldiff)
168 originalchunks = patch.parsepatch(originaldiff)
169
169
170 # 1. filter patch, since we are intending to apply subset of it
170 # 1. filter patch, since we are intending to apply subset of it
171 try:
171 try:
172 chunks, newopts = filterfn(ui, originalchunks)
172 chunks, newopts = filterfn(ui, originalchunks)
173 except patch.PatchError as err:
173 except patch.PatchError as err:
174 raise error.Abort(_('error parsing patch: %s') % err)
174 raise error.Abort(_('error parsing patch: %s') % err)
175 opts.update(newopts)
175 opts.update(newopts)
176
176
177 # We need to keep a backup of files that have been newly added and
177 # We need to keep a backup of files that have been newly added and
178 # modified during the recording process because there is a previous
178 # modified during the recording process because there is a previous
179 # version without the edit in the workdir
179 # version without the edit in the workdir
180 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
180 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
181 contenders = set()
181 contenders = set()
182 for h in chunks:
182 for h in chunks:
183 try:
183 try:
184 contenders.update(set(h.files()))
184 contenders.update(set(h.files()))
185 except AttributeError:
185 except AttributeError:
186 pass
186 pass
187
187
188 changed = status.modified + status.added + status.removed
188 changed = status.modified + status.added + status.removed
189 newfiles = [f for f in changed if f in contenders]
189 newfiles = [f for f in changed if f in contenders]
190 if not newfiles:
190 if not newfiles:
191 ui.status(_('no changes to record\n'))
191 ui.status(_('no changes to record\n'))
192 return 0
192 return 0
193
193
194 modified = set(status.modified)
194 modified = set(status.modified)
195
195
196 # 2. backup changed files, so we can restore them in the end
196 # 2. backup changed files, so we can restore them in the end
197
197
198 if backupall:
198 if backupall:
199 tobackup = changed
199 tobackup = changed
200 else:
200 else:
201 tobackup = [f for f in newfiles if f in modified or f in \
201 tobackup = [f for f in newfiles if f in modified or f in \
202 newlyaddedandmodifiedfiles]
202 newlyaddedandmodifiedfiles]
203 backups = {}
203 backups = {}
204 if tobackup:
204 if tobackup:
205 backupdir = repo.vfs.join('record-backups')
205 backupdir = repo.vfs.join('record-backups')
206 try:
206 try:
207 os.mkdir(backupdir)
207 os.mkdir(backupdir)
208 except OSError as err:
208 except OSError as err:
209 if err.errno != errno.EEXIST:
209 if err.errno != errno.EEXIST:
210 raise
210 raise
211 try:
211 try:
212 # backup continues
212 # backup continues
213 for f in tobackup:
213 for f in tobackup:
214 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
214 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
215 dir=backupdir)
215 dir=backupdir)
216 os.close(fd)
216 os.close(fd)
217 ui.debug('backup %r as %r\n' % (f, tmpname))
217 ui.debug('backup %r as %r\n' % (f, tmpname))
218 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
218 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
219 backups[f] = tmpname
219 backups[f] = tmpname
220
220
221 fp = stringio()
221 fp = stringio()
222 for c in chunks:
222 for c in chunks:
223 fname = c.filename()
223 fname = c.filename()
224 if fname in backups:
224 if fname in backups:
225 c.write(fp)
225 c.write(fp)
226 dopatch = fp.tell()
226 dopatch = fp.tell()
227 fp.seek(0)
227 fp.seek(0)
228
228
229 # 2.5 optionally review / modify patch in text editor
229 # 2.5 optionally review / modify patch in text editor
230 if opts.get('review', False):
230 if opts.get('review', False):
231 patchtext = (crecordmod.diffhelptext
231 patchtext = (crecordmod.diffhelptext
232 + crecordmod.patchhelptext
232 + crecordmod.patchhelptext
233 + fp.read())
233 + fp.read())
234 reviewedpatch = ui.edit(patchtext, "",
234 reviewedpatch = ui.edit(patchtext, "",
235 extra={"suffix": ".diff"},
235 extra={"suffix": ".diff"},
236 repopath=repo.path)
236 repopath=repo.path)
237 fp.truncate(0)
237 fp.truncate(0)
238 fp.write(reviewedpatch)
238 fp.write(reviewedpatch)
239 fp.seek(0)
239 fp.seek(0)
240
240
241 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
241 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
242 # 3a. apply filtered patch to clean repo (clean)
242 # 3a. apply filtered patch to clean repo (clean)
243 if backups:
243 if backups:
244 # Equivalent to hg.revert
244 # Equivalent to hg.revert
245 m = scmutil.matchfiles(repo, backups.keys())
245 m = scmutil.matchfiles(repo, backups.keys())
246 mergemod.update(repo, repo.dirstate.p1(),
246 mergemod.update(repo, repo.dirstate.p1(),
247 False, True, matcher=m)
247 False, True, matcher=m)
248
248
249 # 3b. (apply)
249 # 3b. (apply)
250 if dopatch:
250 if dopatch:
251 try:
251 try:
252 ui.debug('applying patch\n')
252 ui.debug('applying patch\n')
253 ui.debug(fp.getvalue())
253 ui.debug(fp.getvalue())
254 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
254 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
255 except patch.PatchError as err:
255 except patch.PatchError as err:
256 raise error.Abort(str(err))
256 raise error.Abort(str(err))
257 del fp
257 del fp
258
258
259 # 4. We prepared working directory according to filtered
259 # 4. We prepared working directory according to filtered
260 # patch. Now is the time to delegate the job to
260 # patch. Now is the time to delegate the job to
261 # commit/qrefresh or the like!
261 # commit/qrefresh or the like!
262
262
263 # Make all of the pathnames absolute.
263 # Make all of the pathnames absolute.
264 newfiles = [repo.wjoin(nf) for nf in newfiles]
264 newfiles = [repo.wjoin(nf) for nf in newfiles]
265 return commitfunc(ui, repo, *newfiles, **opts)
265 return commitfunc(ui, repo, *newfiles, **opts)
266 finally:
266 finally:
267 # 5. finally restore backed-up files
267 # 5. finally restore backed-up files
268 try:
268 try:
269 dirstate = repo.dirstate
269 dirstate = repo.dirstate
270 for realname, tmpname in backups.iteritems():
270 for realname, tmpname in backups.iteritems():
271 ui.debug('restoring %r to %r\n' % (tmpname, realname))
271 ui.debug('restoring %r to %r\n' % (tmpname, realname))
272
272
273 if dirstate[realname] == 'n':
273 if dirstate[realname] == 'n':
274 # without normallookup, restoring timestamp
274 # without normallookup, restoring timestamp
275 # may cause partially committed files
275 # may cause partially committed files
276 # to be treated as unmodified
276 # to be treated as unmodified
277 dirstate.normallookup(realname)
277 dirstate.normallookup(realname)
278
278
279 # copystat=True here and above are a hack to trick any
279 # copystat=True here and above are a hack to trick any
280 # editors that have f open that we haven't modified them.
280 # editors that have f open that we haven't modified them.
281 #
281 #
282 # Also note that this racy as an editor could notice the
282 # Also note that this racy as an editor could notice the
283 # file's mtime before we've finished writing it.
283 # file's mtime before we've finished writing it.
284 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
284 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
285 os.unlink(tmpname)
285 os.unlink(tmpname)
286 if tobackup:
286 if tobackup:
287 os.rmdir(backupdir)
287 os.rmdir(backupdir)
288 except OSError:
288 except OSError:
289 pass
289 pass
290
290
291 def recordinwlock(ui, repo, message, match, opts):
291 def recordinwlock(ui, repo, message, match, opts):
292 with repo.wlock():
292 with repo.wlock():
293 return recordfunc(ui, repo, message, match, opts)
293 return recordfunc(ui, repo, message, match, opts)
294
294
295 return commit(ui, repo, recordinwlock, pats, opts)
295 return commit(ui, repo, recordinwlock, pats, opts)
296
296
297 def findpossible(cmd, table, strict=False):
297 def findpossible(cmd, table, strict=False):
298 """
298 """
299 Return cmd -> (aliases, command table entry)
299 Return cmd -> (aliases, command table entry)
300 for each matching command.
300 for each matching command.
301 Return debug commands (or their aliases) only if no normal command matches.
301 Return debug commands (or their aliases) only if no normal command matches.
302 """
302 """
303 choice = {}
303 choice = {}
304 debugchoice = {}
304 debugchoice = {}
305
305
306 if cmd in table:
306 if cmd in table:
307 # short-circuit exact matches, "log" alias beats "^log|history"
307 # short-circuit exact matches, "log" alias beats "^log|history"
308 keys = [cmd]
308 keys = [cmd]
309 else:
309 else:
310 keys = table.keys()
310 keys = table.keys()
311
311
312 allcmds = []
312 allcmds = []
313 for e in keys:
313 for e in keys:
314 aliases = parsealiases(e)
314 aliases = parsealiases(e)
315 allcmds.extend(aliases)
315 allcmds.extend(aliases)
316 found = None
316 found = None
317 if cmd in aliases:
317 if cmd in aliases:
318 found = cmd
318 found = cmd
319 elif not strict:
319 elif not strict:
320 for a in aliases:
320 for a in aliases:
321 if a.startswith(cmd):
321 if a.startswith(cmd):
322 found = a
322 found = a
323 break
323 break
324 if found is not None:
324 if found is not None:
325 if aliases[0].startswith("debug") or found.startswith("debug"):
325 if aliases[0].startswith("debug") or found.startswith("debug"):
326 debugchoice[found] = (aliases, table[e])
326 debugchoice[found] = (aliases, table[e])
327 else:
327 else:
328 choice[found] = (aliases, table[e])
328 choice[found] = (aliases, table[e])
329
329
330 if not choice and debugchoice:
330 if not choice and debugchoice:
331 choice = debugchoice
331 choice = debugchoice
332
332
333 return choice, allcmds
333 return choice, allcmds
334
334
335 def findcmd(cmd, table, strict=True):
335 def findcmd(cmd, table, strict=True):
336 """Return (aliases, command table entry) for command string."""
336 """Return (aliases, command table entry) for command string."""
337 choice, allcmds = findpossible(cmd, table, strict)
337 choice, allcmds = findpossible(cmd, table, strict)
338
338
339 if cmd in choice:
339 if cmd in choice:
340 return choice[cmd]
340 return choice[cmd]
341
341
342 if len(choice) > 1:
342 if len(choice) > 1:
343 clist = choice.keys()
343 clist = choice.keys()
344 clist.sort()
344 clist.sort()
345 raise error.AmbiguousCommand(cmd, clist)
345 raise error.AmbiguousCommand(cmd, clist)
346
346
347 if choice:
347 if choice:
348 return choice.values()[0]
348 return choice.values()[0]
349
349
350 raise error.UnknownCommand(cmd, allcmds)
350 raise error.UnknownCommand(cmd, allcmds)
351
351
352 def findrepo(p):
352 def findrepo(p):
353 while not os.path.isdir(os.path.join(p, ".hg")):
353 while not os.path.isdir(os.path.join(p, ".hg")):
354 oldp, p = p, os.path.dirname(p)
354 oldp, p = p, os.path.dirname(p)
355 if p == oldp:
355 if p == oldp:
356 return None
356 return None
357
357
358 return p
358 return p
359
359
360 def bailifchanged(repo, merge=True, hint=None):
360 def bailifchanged(repo, merge=True, hint=None):
361 """ enforce the precondition that working directory must be clean.
361 """ enforce the precondition that working directory must be clean.
362
362
363 'merge' can be set to false if a pending uncommitted merge should be
363 'merge' can be set to false if a pending uncommitted merge should be
364 ignored (such as when 'update --check' runs).
364 ignored (such as when 'update --check' runs).
365
365
366 'hint' is the usual hint given to Abort exception.
366 'hint' is the usual hint given to Abort exception.
367 """
367 """
368
368
369 if merge and repo.dirstate.p2() != nullid:
369 if merge and repo.dirstate.p2() != nullid:
370 raise error.Abort(_('outstanding uncommitted merge'), hint=hint)
370 raise error.Abort(_('outstanding uncommitted merge'), hint=hint)
371 modified, added, removed, deleted = repo.status()[:4]
371 modified, added, removed, deleted = repo.status()[:4]
372 if modified or added or removed or deleted:
372 if modified or added or removed or deleted:
373 raise error.Abort(_('uncommitted changes'), hint=hint)
373 raise error.Abort(_('uncommitted changes'), hint=hint)
374 ctx = repo[None]
374 ctx = repo[None]
375 for s in sorted(ctx.substate):
375 for s in sorted(ctx.substate):
376 ctx.sub(s).bailifchanged(hint=hint)
376 ctx.sub(s).bailifchanged(hint=hint)
377
377
378 def logmessage(ui, opts):
378 def logmessage(ui, opts):
379 """ get the log message according to -m and -l option """
379 """ get the log message according to -m and -l option """
380 message = opts.get('message')
380 message = opts.get('message')
381 logfile = opts.get('logfile')
381 logfile = opts.get('logfile')
382
382
383 if message and logfile:
383 if message and logfile:
384 raise error.Abort(_('options --message and --logfile are mutually '
384 raise error.Abort(_('options --message and --logfile are mutually '
385 'exclusive'))
385 'exclusive'))
386 if not message and logfile:
386 if not message and logfile:
387 try:
387 try:
388 if logfile == '-':
388 if logfile == '-':
389 message = ui.fin.read()
389 message = ui.fin.read()
390 else:
390 else:
391 message = '\n'.join(util.readfile(logfile).splitlines())
391 message = '\n'.join(util.readfile(logfile).splitlines())
392 except IOError as inst:
392 except IOError as inst:
393 raise error.Abort(_("can't read commit message '%s': %s") %
393 raise error.Abort(_("can't read commit message '%s': %s") %
394 (logfile, inst.strerror))
394 (logfile, inst.strerror))
395 return message
395 return message
396
396
397 def mergeeditform(ctxorbool, baseformname):
397 def mergeeditform(ctxorbool, baseformname):
398 """return appropriate editform name (referencing a committemplate)
398 """return appropriate editform name (referencing a committemplate)
399
399
400 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
400 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
401 merging is committed.
401 merging is committed.
402
402
403 This returns baseformname with '.merge' appended if it is a merge,
403 This returns baseformname with '.merge' appended if it is a merge,
404 otherwise '.normal' is appended.
404 otherwise '.normal' is appended.
405 """
405 """
406 if isinstance(ctxorbool, bool):
406 if isinstance(ctxorbool, bool):
407 if ctxorbool:
407 if ctxorbool:
408 return baseformname + ".merge"
408 return baseformname + ".merge"
409 elif 1 < len(ctxorbool.parents()):
409 elif 1 < len(ctxorbool.parents()):
410 return baseformname + ".merge"
410 return baseformname + ".merge"
411
411
412 return baseformname + ".normal"
412 return baseformname + ".normal"
413
413
414 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
414 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
415 editform='', **opts):
415 editform='', **opts):
416 """get appropriate commit message editor according to '--edit' option
416 """get appropriate commit message editor according to '--edit' option
417
417
418 'finishdesc' is a function to be called with edited commit message
418 'finishdesc' is a function to be called with edited commit message
419 (= 'description' of the new changeset) just after editing, but
419 (= 'description' of the new changeset) just after editing, but
420 before checking empty-ness. It should return actual text to be
420 before checking empty-ness. It should return actual text to be
421 stored into history. This allows to change description before
421 stored into history. This allows to change description before
422 storing.
422 storing.
423
423
424 'extramsg' is a extra message to be shown in the editor instead of
424 'extramsg' is a extra message to be shown in the editor instead of
425 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
425 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
426 is automatically added.
426 is automatically added.
427
427
428 'editform' is a dot-separated list of names, to distinguish
428 'editform' is a dot-separated list of names, to distinguish
429 the purpose of commit text editing.
429 the purpose of commit text editing.
430
430
431 'getcommiteditor' returns 'commitforceeditor' regardless of
431 'getcommiteditor' returns 'commitforceeditor' regardless of
432 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
432 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
433 they are specific for usage in MQ.
433 they are specific for usage in MQ.
434 """
434 """
435 if edit or finishdesc or extramsg:
435 if edit or finishdesc or extramsg:
436 return lambda r, c, s: commitforceeditor(r, c, s,
436 return lambda r, c, s: commitforceeditor(r, c, s,
437 finishdesc=finishdesc,
437 finishdesc=finishdesc,
438 extramsg=extramsg,
438 extramsg=extramsg,
439 editform=editform)
439 editform=editform)
440 elif editform:
440 elif editform:
441 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
441 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
442 else:
442 else:
443 return commiteditor
443 return commiteditor
444
444
445 def loglimit(opts):
445 def loglimit(opts):
446 """get the log limit according to option -l/--limit"""
446 """get the log limit according to option -l/--limit"""
447 limit = opts.get('limit')
447 limit = opts.get('limit')
448 if limit:
448 if limit:
449 try:
449 try:
450 limit = int(limit)
450 limit = int(limit)
451 except ValueError:
451 except ValueError:
452 raise error.Abort(_('limit must be a positive integer'))
452 raise error.Abort(_('limit must be a positive integer'))
453 if limit <= 0:
453 if limit <= 0:
454 raise error.Abort(_('limit must be positive'))
454 raise error.Abort(_('limit must be positive'))
455 else:
455 else:
456 limit = None
456 limit = None
457 return limit
457 return limit
458
458
459 def makefilename(repo, pat, node, desc=None,
459 def makefilename(repo, pat, node, desc=None,
460 total=None, seqno=None, revwidth=None, pathname=None):
460 total=None, seqno=None, revwidth=None, pathname=None):
461 node_expander = {
461 node_expander = {
462 'H': lambda: hex(node),
462 'H': lambda: hex(node),
463 'R': lambda: str(repo.changelog.rev(node)),
463 'R': lambda: str(repo.changelog.rev(node)),
464 'h': lambda: short(node),
464 'h': lambda: short(node),
465 'm': lambda: re.sub('[^\w]', '_', str(desc))
465 'm': lambda: re.sub('[^\w]', '_', str(desc))
466 }
466 }
467 expander = {
467 expander = {
468 '%': lambda: '%',
468 '%': lambda: '%',
469 'b': lambda: os.path.basename(repo.root),
469 'b': lambda: os.path.basename(repo.root),
470 }
470 }
471
471
472 try:
472 try:
473 if node:
473 if node:
474 expander.update(node_expander)
474 expander.update(node_expander)
475 if node:
475 if node:
476 expander['r'] = (lambda:
476 expander['r'] = (lambda:
477 str(repo.changelog.rev(node)).zfill(revwidth or 0))
477 str(repo.changelog.rev(node)).zfill(revwidth or 0))
478 if total is not None:
478 if total is not None:
479 expander['N'] = lambda: str(total)
479 expander['N'] = lambda: str(total)
480 if seqno is not None:
480 if seqno is not None:
481 expander['n'] = lambda: str(seqno)
481 expander['n'] = lambda: str(seqno)
482 if total is not None and seqno is not None:
482 if total is not None and seqno is not None:
483 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
483 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
484 if pathname is not None:
484 if pathname is not None:
485 expander['s'] = lambda: os.path.basename(pathname)
485 expander['s'] = lambda: os.path.basename(pathname)
486 expander['d'] = lambda: os.path.dirname(pathname) or '.'
486 expander['d'] = lambda: os.path.dirname(pathname) or '.'
487 expander['p'] = lambda: pathname
487 expander['p'] = lambda: pathname
488
488
489 newname = []
489 newname = []
490 patlen = len(pat)
490 patlen = len(pat)
491 i = 0
491 i = 0
492 while i < patlen:
492 while i < patlen:
493 c = pat[i]
493 c = pat[i]
494 if c == '%':
494 if c == '%':
495 i += 1
495 i += 1
496 c = pat[i]
496 c = pat[i]
497 c = expander[c]()
497 c = expander[c]()
498 newname.append(c)
498 newname.append(c)
499 i += 1
499 i += 1
500 return ''.join(newname)
500 return ''.join(newname)
501 except KeyError as inst:
501 except KeyError as inst:
502 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
502 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
503 inst.args[0])
503 inst.args[0])
504
504
505 class _unclosablefile(object):
505 class _unclosablefile(object):
506 def __init__(self, fp):
506 def __init__(self, fp):
507 self._fp = fp
507 self._fp = fp
508
508
509 def close(self):
509 def close(self):
510 pass
510 pass
511
511
512 def __iter__(self):
512 def __iter__(self):
513 return iter(self._fp)
513 return iter(self._fp)
514
514
515 def __getattr__(self, attr):
515 def __getattr__(self, attr):
516 return getattr(self._fp, attr)
516 return getattr(self._fp, attr)
517
517
518 def __enter__(self):
518 def __enter__(self):
519 return self
519 return self
520
520
521 def __exit__(self, exc_type, exc_value, exc_tb):
521 def __exit__(self, exc_type, exc_value, exc_tb):
522 pass
522 pass
523
523
524 def makefileobj(repo, pat, node=None, desc=None, total=None,
524 def makefileobj(repo, pat, node=None, desc=None, total=None,
525 seqno=None, revwidth=None, mode='wb', modemap=None,
525 seqno=None, revwidth=None, mode='wb', modemap=None,
526 pathname=None):
526 pathname=None):
527
527
528 writable = mode not in ('r', 'rb')
528 writable = mode not in ('r', 'rb')
529
529
530 if not pat or pat == '-':
530 if not pat or pat == '-':
531 if writable:
531 if writable:
532 fp = repo.ui.fout
532 fp = repo.ui.fout
533 else:
533 else:
534 fp = repo.ui.fin
534 fp = repo.ui.fin
535 return _unclosablefile(fp)
535 return _unclosablefile(fp)
536 if util.safehasattr(pat, 'write') and writable:
536 if util.safehasattr(pat, 'write') and writable:
537 return pat
537 return pat
538 if util.safehasattr(pat, 'read') and 'r' in mode:
538 if util.safehasattr(pat, 'read') and 'r' in mode:
539 return pat
539 return pat
540 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
540 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
541 if modemap is not None:
541 if modemap is not None:
542 mode = modemap.get(fn, mode)
542 mode = modemap.get(fn, mode)
543 if mode == 'wb':
543 if mode == 'wb':
544 modemap[fn] = 'ab'
544 modemap[fn] = 'ab'
545 return open(fn, mode)
545 return open(fn, mode)
546
546
547 def openrevlog(repo, cmd, file_, opts):
547 def openrevlog(repo, cmd, file_, opts):
548 """opens the changelog, manifest, a filelog or a given revlog"""
548 """opens the changelog, manifest, a filelog or a given revlog"""
549 cl = opts['changelog']
549 cl = opts['changelog']
550 mf = opts['manifest']
550 mf = opts['manifest']
551 dir = opts['dir']
551 dir = opts['dir']
552 msg = None
552 msg = None
553 if cl and mf:
553 if cl and mf:
554 msg = _('cannot specify --changelog and --manifest at the same time')
554 msg = _('cannot specify --changelog and --manifest at the same time')
555 elif cl and dir:
555 elif cl and dir:
556 msg = _('cannot specify --changelog and --dir at the same time')
556 msg = _('cannot specify --changelog and --dir at the same time')
557 elif cl or mf or dir:
557 elif cl or mf or dir:
558 if file_:
558 if file_:
559 msg = _('cannot specify filename with --changelog or --manifest')
559 msg = _('cannot specify filename with --changelog or --manifest')
560 elif not repo:
560 elif not repo:
561 msg = _('cannot specify --changelog or --manifest or --dir '
561 msg = _('cannot specify --changelog or --manifest or --dir '
562 'without a repository')
562 'without a repository')
563 if msg:
563 if msg:
564 raise error.Abort(msg)
564 raise error.Abort(msg)
565
565
566 r = None
566 r = None
567 if repo:
567 if repo:
568 if cl:
568 if cl:
569 r = repo.unfiltered().changelog
569 r = repo.unfiltered().changelog
570 elif dir:
570 elif dir:
571 if 'treemanifest' not in repo.requirements:
571 if 'treemanifest' not in repo.requirements:
572 raise error.Abort(_("--dir can only be used on repos with "
572 raise error.Abort(_("--dir can only be used on repos with "
573 "treemanifest enabled"))
573 "treemanifest enabled"))
574 dirlog = repo.manifestlog._revlog.dirlog(dir)
574 dirlog = repo.manifestlog._revlog.dirlog(dir)
575 if len(dirlog):
575 if len(dirlog):
576 r = dirlog
576 r = dirlog
577 elif mf:
577 elif mf:
578 r = repo.manifestlog._revlog
578 r = repo.manifestlog._revlog
579 elif file_:
579 elif file_:
580 filelog = repo.file(file_)
580 filelog = repo.file(file_)
581 if len(filelog):
581 if len(filelog):
582 r = filelog
582 r = filelog
583 if not r:
583 if not r:
584 if not file_:
584 if not file_:
585 raise error.CommandError(cmd, _('invalid arguments'))
585 raise error.CommandError(cmd, _('invalid arguments'))
586 if not os.path.isfile(file_):
586 if not os.path.isfile(file_):
587 raise error.Abort(_("revlog '%s' not found") % file_)
587 raise error.Abort(_("revlog '%s' not found") % file_)
588 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
588 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
589 file_[:-2] + ".i")
589 file_[:-2] + ".i")
590 return r
590 return r
591
591
592 def copy(ui, repo, pats, opts, rename=False):
592 def copy(ui, repo, pats, opts, rename=False):
593 # called with the repo lock held
593 # called with the repo lock held
594 #
594 #
595 # hgsep => pathname that uses "/" to separate directories
595 # hgsep => pathname that uses "/" to separate directories
596 # ossep => pathname that uses os.sep to separate directories
596 # ossep => pathname that uses os.sep to separate directories
597 cwd = repo.getcwd()
597 cwd = repo.getcwd()
598 targets = {}
598 targets = {}
599 after = opts.get("after")
599 after = opts.get("after")
600 dryrun = opts.get("dry_run")
600 dryrun = opts.get("dry_run")
601 wctx = repo[None]
601 wctx = repo[None]
602
602
603 def walkpat(pat):
603 def walkpat(pat):
604 srcs = []
604 srcs = []
605 if after:
605 if after:
606 badstates = '?'
606 badstates = '?'
607 else:
607 else:
608 badstates = '?r'
608 badstates = '?r'
609 m = scmutil.match(repo[None], [pat], opts, globbed=True)
609 m = scmutil.match(repo[None], [pat], opts, globbed=True)
610 for abs in repo.walk(m):
610 for abs in repo.walk(m):
611 state = repo.dirstate[abs]
611 state = repo.dirstate[abs]
612 rel = m.rel(abs)
612 rel = m.rel(abs)
613 exact = m.exact(abs)
613 exact = m.exact(abs)
614 if state in badstates:
614 if state in badstates:
615 if exact and state == '?':
615 if exact and state == '?':
616 ui.warn(_('%s: not copying - file is not managed\n') % rel)
616 ui.warn(_('%s: not copying - file is not managed\n') % rel)
617 if exact and state == 'r':
617 if exact and state == 'r':
618 ui.warn(_('%s: not copying - file has been marked for'
618 ui.warn(_('%s: not copying - file has been marked for'
619 ' remove\n') % rel)
619 ' remove\n') % rel)
620 continue
620 continue
621 # abs: hgsep
621 # abs: hgsep
622 # rel: ossep
622 # rel: ossep
623 srcs.append((abs, rel, exact))
623 srcs.append((abs, rel, exact))
624 return srcs
624 return srcs
625
625
626 # abssrc: hgsep
626 # abssrc: hgsep
627 # relsrc: ossep
627 # relsrc: ossep
628 # otarget: ossep
628 # otarget: ossep
629 def copyfile(abssrc, relsrc, otarget, exact):
629 def copyfile(abssrc, relsrc, otarget, exact):
630 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
630 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
631 if '/' in abstarget:
631 if '/' in abstarget:
632 # We cannot normalize abstarget itself, this would prevent
632 # We cannot normalize abstarget itself, this would prevent
633 # case only renames, like a => A.
633 # case only renames, like a => A.
634 abspath, absname = abstarget.rsplit('/', 1)
634 abspath, absname = abstarget.rsplit('/', 1)
635 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
635 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
636 reltarget = repo.pathto(abstarget, cwd)
636 reltarget = repo.pathto(abstarget, cwd)
637 target = repo.wjoin(abstarget)
637 target = repo.wjoin(abstarget)
638 src = repo.wjoin(abssrc)
638 src = repo.wjoin(abssrc)
639 state = repo.dirstate[abstarget]
639 state = repo.dirstate[abstarget]
640
640
641 scmutil.checkportable(ui, abstarget)
641 scmutil.checkportable(ui, abstarget)
642
642
643 # check for collisions
643 # check for collisions
644 prevsrc = targets.get(abstarget)
644 prevsrc = targets.get(abstarget)
645 if prevsrc is not None:
645 if prevsrc is not None:
646 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
646 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
647 (reltarget, repo.pathto(abssrc, cwd),
647 (reltarget, repo.pathto(abssrc, cwd),
648 repo.pathto(prevsrc, cwd)))
648 repo.pathto(prevsrc, cwd)))
649 return
649 return
650
650
651 # check for overwrites
651 # check for overwrites
652 exists = os.path.lexists(target)
652 exists = os.path.lexists(target)
653 samefile = False
653 samefile = False
654 if exists and abssrc != abstarget:
654 if exists and abssrc != abstarget:
655 if (repo.dirstate.normalize(abssrc) ==
655 if (repo.dirstate.normalize(abssrc) ==
656 repo.dirstate.normalize(abstarget)):
656 repo.dirstate.normalize(abstarget)):
657 if not rename:
657 if not rename:
658 ui.warn(_("%s: can't copy - same file\n") % reltarget)
658 ui.warn(_("%s: can't copy - same file\n") % reltarget)
659 return
659 return
660 exists = False
660 exists = False
661 samefile = True
661 samefile = True
662
662
663 if not after and exists or after and state in 'mn':
663 if not after and exists or after and state in 'mn':
664 if not opts['force']:
664 if not opts['force']:
665 if state in 'mn':
665 if state in 'mn':
666 msg = _('%s: not overwriting - file already committed\n')
666 msg = _('%s: not overwriting - file already committed\n')
667 if after:
667 if after:
668 flags = '--after --force'
668 flags = '--after --force'
669 else:
669 else:
670 flags = '--force'
670 flags = '--force'
671 if rename:
671 if rename:
672 hint = _('(hg rename %s to replace the file by '
672 hint = _('(hg rename %s to replace the file by '
673 'recording a rename)\n') % flags
673 'recording a rename)\n') % flags
674 else:
674 else:
675 hint = _('(hg copy %s to replace the file by '
675 hint = _('(hg copy %s to replace the file by '
676 'recording a copy)\n') % flags
676 'recording a copy)\n') % flags
677 else:
677 else:
678 msg = _('%s: not overwriting - file exists\n')
678 msg = _('%s: not overwriting - file exists\n')
679 if rename:
679 if rename:
680 hint = _('(hg rename --after to record the rename)\n')
680 hint = _('(hg rename --after to record the rename)\n')
681 else:
681 else:
682 hint = _('(hg copy --after to record the copy)\n')
682 hint = _('(hg copy --after to record the copy)\n')
683 ui.warn(msg % reltarget)
683 ui.warn(msg % reltarget)
684 ui.warn(hint)
684 ui.warn(hint)
685 return
685 return
686
686
687 if after:
687 if after:
688 if not exists:
688 if not exists:
689 if rename:
689 if rename:
690 ui.warn(_('%s: not recording move - %s does not exist\n') %
690 ui.warn(_('%s: not recording move - %s does not exist\n') %
691 (relsrc, reltarget))
691 (relsrc, reltarget))
692 else:
692 else:
693 ui.warn(_('%s: not recording copy - %s does not exist\n') %
693 ui.warn(_('%s: not recording copy - %s does not exist\n') %
694 (relsrc, reltarget))
694 (relsrc, reltarget))
695 return
695 return
696 elif not dryrun:
696 elif not dryrun:
697 try:
697 try:
698 if exists:
698 if exists:
699 os.unlink(target)
699 os.unlink(target)
700 targetdir = os.path.dirname(target) or '.'
700 targetdir = os.path.dirname(target) or '.'
701 if not os.path.isdir(targetdir):
701 if not os.path.isdir(targetdir):
702 os.makedirs(targetdir)
702 os.makedirs(targetdir)
703 if samefile:
703 if samefile:
704 tmp = target + "~hgrename"
704 tmp = target + "~hgrename"
705 os.rename(src, tmp)
705 os.rename(src, tmp)
706 os.rename(tmp, target)
706 os.rename(tmp, target)
707 else:
707 else:
708 util.copyfile(src, target)
708 util.copyfile(src, target)
709 srcexists = True
709 srcexists = True
710 except IOError as inst:
710 except IOError as inst:
711 if inst.errno == errno.ENOENT:
711 if inst.errno == errno.ENOENT:
712 ui.warn(_('%s: deleted in working directory\n') % relsrc)
712 ui.warn(_('%s: deleted in working directory\n') % relsrc)
713 srcexists = False
713 srcexists = False
714 else:
714 else:
715 ui.warn(_('%s: cannot copy - %s\n') %
715 ui.warn(_('%s: cannot copy - %s\n') %
716 (relsrc, inst.strerror))
716 (relsrc, inst.strerror))
717 return True # report a failure
717 return True # report a failure
718
718
719 if ui.verbose or not exact:
719 if ui.verbose or not exact:
720 if rename:
720 if rename:
721 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
721 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
722 else:
722 else:
723 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
723 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
724
724
725 targets[abstarget] = abssrc
725 targets[abstarget] = abssrc
726
726
727 # fix up dirstate
727 # fix up dirstate
728 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
728 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
729 dryrun=dryrun, cwd=cwd)
729 dryrun=dryrun, cwd=cwd)
730 if rename and not dryrun:
730 if rename and not dryrun:
731 if not after and srcexists and not samefile:
731 if not after and srcexists and not samefile:
732 repo.wvfs.unlinkpath(abssrc)
732 repo.wvfs.unlinkpath(abssrc)
733 wctx.forget([abssrc])
733 wctx.forget([abssrc])
734
734
735 # pat: ossep
735 # pat: ossep
736 # dest ossep
736 # dest ossep
737 # srcs: list of (hgsep, hgsep, ossep, bool)
737 # srcs: list of (hgsep, hgsep, ossep, bool)
738 # return: function that takes hgsep and returns ossep
738 # return: function that takes hgsep and returns ossep
739 def targetpathfn(pat, dest, srcs):
739 def targetpathfn(pat, dest, srcs):
740 if os.path.isdir(pat):
740 if os.path.isdir(pat):
741 abspfx = pathutil.canonpath(repo.root, cwd, pat)
741 abspfx = pathutil.canonpath(repo.root, cwd, pat)
742 abspfx = util.localpath(abspfx)
742 abspfx = util.localpath(abspfx)
743 if destdirexists:
743 if destdirexists:
744 striplen = len(os.path.split(abspfx)[0])
744 striplen = len(os.path.split(abspfx)[0])
745 else:
745 else:
746 striplen = len(abspfx)
746 striplen = len(abspfx)
747 if striplen:
747 if striplen:
748 striplen += len(pycompat.ossep)
748 striplen += len(pycompat.ossep)
749 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
749 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
750 elif destdirexists:
750 elif destdirexists:
751 res = lambda p: os.path.join(dest,
751 res = lambda p: os.path.join(dest,
752 os.path.basename(util.localpath(p)))
752 os.path.basename(util.localpath(p)))
753 else:
753 else:
754 res = lambda p: dest
754 res = lambda p: dest
755 return res
755 return res
756
756
757 # pat: ossep
757 # pat: ossep
758 # dest ossep
758 # dest ossep
759 # srcs: list of (hgsep, hgsep, ossep, bool)
759 # srcs: list of (hgsep, hgsep, ossep, bool)
760 # return: function that takes hgsep and returns ossep
760 # return: function that takes hgsep and returns ossep
761 def targetpathafterfn(pat, dest, srcs):
761 def targetpathafterfn(pat, dest, srcs):
762 if matchmod.patkind(pat):
762 if matchmod.patkind(pat):
763 # a mercurial pattern
763 # a mercurial pattern
764 res = lambda p: os.path.join(dest,
764 res = lambda p: os.path.join(dest,
765 os.path.basename(util.localpath(p)))
765 os.path.basename(util.localpath(p)))
766 else:
766 else:
767 abspfx = pathutil.canonpath(repo.root, cwd, pat)
767 abspfx = pathutil.canonpath(repo.root, cwd, pat)
768 if len(abspfx) < len(srcs[0][0]):
768 if len(abspfx) < len(srcs[0][0]):
769 # A directory. Either the target path contains the last
769 # A directory. Either the target path contains the last
770 # component of the source path or it does not.
770 # component of the source path or it does not.
771 def evalpath(striplen):
771 def evalpath(striplen):
772 score = 0
772 score = 0
773 for s in srcs:
773 for s in srcs:
774 t = os.path.join(dest, util.localpath(s[0])[striplen:])
774 t = os.path.join(dest, util.localpath(s[0])[striplen:])
775 if os.path.lexists(t):
775 if os.path.lexists(t):
776 score += 1
776 score += 1
777 return score
777 return score
778
778
779 abspfx = util.localpath(abspfx)
779 abspfx = util.localpath(abspfx)
780 striplen = len(abspfx)
780 striplen = len(abspfx)
781 if striplen:
781 if striplen:
782 striplen += len(pycompat.ossep)
782 striplen += len(pycompat.ossep)
783 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
783 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
784 score = evalpath(striplen)
784 score = evalpath(striplen)
785 striplen1 = len(os.path.split(abspfx)[0])
785 striplen1 = len(os.path.split(abspfx)[0])
786 if striplen1:
786 if striplen1:
787 striplen1 += len(pycompat.ossep)
787 striplen1 += len(pycompat.ossep)
788 if evalpath(striplen1) > score:
788 if evalpath(striplen1) > score:
789 striplen = striplen1
789 striplen = striplen1
790 res = lambda p: os.path.join(dest,
790 res = lambda p: os.path.join(dest,
791 util.localpath(p)[striplen:])
791 util.localpath(p)[striplen:])
792 else:
792 else:
793 # a file
793 # a file
794 if destdirexists:
794 if destdirexists:
795 res = lambda p: os.path.join(dest,
795 res = lambda p: os.path.join(dest,
796 os.path.basename(util.localpath(p)))
796 os.path.basename(util.localpath(p)))
797 else:
797 else:
798 res = lambda p: dest
798 res = lambda p: dest
799 return res
799 return res
800
800
801 pats = scmutil.expandpats(pats)
801 pats = scmutil.expandpats(pats)
802 if not pats:
802 if not pats:
803 raise error.Abort(_('no source or destination specified'))
803 raise error.Abort(_('no source or destination specified'))
804 if len(pats) == 1:
804 if len(pats) == 1:
805 raise error.Abort(_('no destination specified'))
805 raise error.Abort(_('no destination specified'))
806 dest = pats.pop()
806 dest = pats.pop()
807 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
807 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
808 if not destdirexists:
808 if not destdirexists:
809 if len(pats) > 1 or matchmod.patkind(pats[0]):
809 if len(pats) > 1 or matchmod.patkind(pats[0]):
810 raise error.Abort(_('with multiple sources, destination must be an '
810 raise error.Abort(_('with multiple sources, destination must be an '
811 'existing directory'))
811 'existing directory'))
812 if util.endswithsep(dest):
812 if util.endswithsep(dest):
813 raise error.Abort(_('destination %s is not a directory') % dest)
813 raise error.Abort(_('destination %s is not a directory') % dest)
814
814
815 tfn = targetpathfn
815 tfn = targetpathfn
816 if after:
816 if after:
817 tfn = targetpathafterfn
817 tfn = targetpathafterfn
818 copylist = []
818 copylist = []
819 for pat in pats:
819 for pat in pats:
820 srcs = walkpat(pat)
820 srcs = walkpat(pat)
821 if not srcs:
821 if not srcs:
822 continue
822 continue
823 copylist.append((tfn(pat, dest, srcs), srcs))
823 copylist.append((tfn(pat, dest, srcs), srcs))
824 if not copylist:
824 if not copylist:
825 raise error.Abort(_('no files to copy'))
825 raise error.Abort(_('no files to copy'))
826
826
827 errors = 0
827 errors = 0
828 for targetpath, srcs in copylist:
828 for targetpath, srcs in copylist:
829 for abssrc, relsrc, exact in srcs:
829 for abssrc, relsrc, exact in srcs:
830 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
830 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
831 errors += 1
831 errors += 1
832
832
833 if errors:
833 if errors:
834 ui.warn(_('(consider using --after)\n'))
834 ui.warn(_('(consider using --after)\n'))
835
835
836 return errors != 0
836 return errors != 0
837
837
838 ## facility to let extension process additional data into an import patch
838 ## facility to let extension process additional data into an import patch
839 # list of identifier to be executed in order
839 # list of identifier to be executed in order
840 extrapreimport = [] # run before commit
840 extrapreimport = [] # run before commit
841 extrapostimport = [] # run after commit
841 extrapostimport = [] # run after commit
842 # mapping from identifier to actual import function
842 # mapping from identifier to actual import function
843 #
843 #
844 # 'preimport' are run before the commit is made and are provided the following
844 # 'preimport' are run before the commit is made and are provided the following
845 # arguments:
845 # arguments:
846 # - repo: the localrepository instance,
846 # - repo: the localrepository instance,
847 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
847 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
848 # - extra: the future extra dictionary of the changeset, please mutate it,
848 # - extra: the future extra dictionary of the changeset, please mutate it,
849 # - opts: the import options.
849 # - opts: the import options.
850 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
850 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
851 # mutation of in memory commit and more. Feel free to rework the code to get
851 # mutation of in memory commit and more. Feel free to rework the code to get
852 # there.
852 # there.
853 extrapreimportmap = {}
853 extrapreimportmap = {}
854 # 'postimport' are run after the commit is made and are provided the following
854 # 'postimport' are run after the commit is made and are provided the following
855 # argument:
855 # argument:
856 # - ctx: the changectx created by import.
856 # - ctx: the changectx created by import.
857 extrapostimportmap = {}
857 extrapostimportmap = {}
858
858
859 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
859 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
860 """Utility function used by commands.import to import a single patch
860 """Utility function used by commands.import to import a single patch
861
861
862 This function is explicitly defined here to help the evolve extension to
862 This function is explicitly defined here to help the evolve extension to
863 wrap this part of the import logic.
863 wrap this part of the import logic.
864
864
865 The API is currently a bit ugly because it a simple code translation from
865 The API is currently a bit ugly because it a simple code translation from
866 the import command. Feel free to make it better.
866 the import command. Feel free to make it better.
867
867
868 :hunk: a patch (as a binary string)
868 :hunk: a patch (as a binary string)
869 :parents: nodes that will be parent of the created commit
869 :parents: nodes that will be parent of the created commit
870 :opts: the full dict of option passed to the import command
870 :opts: the full dict of option passed to the import command
871 :msgs: list to save commit message to.
871 :msgs: list to save commit message to.
872 (used in case we need to save it when failing)
872 (used in case we need to save it when failing)
873 :updatefunc: a function that update a repo to a given node
873 :updatefunc: a function that update a repo to a given node
874 updatefunc(<repo>, <node>)
874 updatefunc(<repo>, <node>)
875 """
875 """
876 # avoid cycle context -> subrepo -> cmdutil
876 # avoid cycle context -> subrepo -> cmdutil
877 from . import context
877 from . import context
878 extractdata = patch.extract(ui, hunk)
878 extractdata = patch.extract(ui, hunk)
879 tmpname = extractdata.get('filename')
879 tmpname = extractdata.get('filename')
880 message = extractdata.get('message')
880 message = extractdata.get('message')
881 user = opts.get('user') or extractdata.get('user')
881 user = opts.get('user') or extractdata.get('user')
882 date = opts.get('date') or extractdata.get('date')
882 date = opts.get('date') or extractdata.get('date')
883 branch = extractdata.get('branch')
883 branch = extractdata.get('branch')
884 nodeid = extractdata.get('nodeid')
884 nodeid = extractdata.get('nodeid')
885 p1 = extractdata.get('p1')
885 p1 = extractdata.get('p1')
886 p2 = extractdata.get('p2')
886 p2 = extractdata.get('p2')
887
887
888 nocommit = opts.get('no_commit')
888 nocommit = opts.get('no_commit')
889 importbranch = opts.get('import_branch')
889 importbranch = opts.get('import_branch')
890 update = not opts.get('bypass')
890 update = not opts.get('bypass')
891 strip = opts["strip"]
891 strip = opts["strip"]
892 prefix = opts["prefix"]
892 prefix = opts["prefix"]
893 sim = float(opts.get('similarity') or 0)
893 sim = float(opts.get('similarity') or 0)
894 if not tmpname:
894 if not tmpname:
895 return (None, None, False)
895 return (None, None, False)
896
896
897 rejects = False
897 rejects = False
898
898
899 try:
899 try:
900 cmdline_message = logmessage(ui, opts)
900 cmdline_message = logmessage(ui, opts)
901 if cmdline_message:
901 if cmdline_message:
902 # pickup the cmdline msg
902 # pickup the cmdline msg
903 message = cmdline_message
903 message = cmdline_message
904 elif message:
904 elif message:
905 # pickup the patch msg
905 # pickup the patch msg
906 message = message.strip()
906 message = message.strip()
907 else:
907 else:
908 # launch the editor
908 # launch the editor
909 message = None
909 message = None
910 ui.debug('message:\n%s\n' % message)
910 ui.debug('message:\n%s\n' % message)
911
911
912 if len(parents) == 1:
912 if len(parents) == 1:
913 parents.append(repo[nullid])
913 parents.append(repo[nullid])
914 if opts.get('exact'):
914 if opts.get('exact'):
915 if not nodeid or not p1:
915 if not nodeid or not p1:
916 raise error.Abort(_('not a Mercurial patch'))
916 raise error.Abort(_('not a Mercurial patch'))
917 p1 = repo[p1]
917 p1 = repo[p1]
918 p2 = repo[p2 or nullid]
918 p2 = repo[p2 or nullid]
919 elif p2:
919 elif p2:
920 try:
920 try:
921 p1 = repo[p1]
921 p1 = repo[p1]
922 p2 = repo[p2]
922 p2 = repo[p2]
923 # Without any options, consider p2 only if the
923 # Without any options, consider p2 only if the
924 # patch is being applied on top of the recorded
924 # patch is being applied on top of the recorded
925 # first parent.
925 # first parent.
926 if p1 != parents[0]:
926 if p1 != parents[0]:
927 p1 = parents[0]
927 p1 = parents[0]
928 p2 = repo[nullid]
928 p2 = repo[nullid]
929 except error.RepoError:
929 except error.RepoError:
930 p1, p2 = parents
930 p1, p2 = parents
931 if p2.node() == nullid:
931 if p2.node() == nullid:
932 ui.warn(_("warning: import the patch as a normal revision\n"
932 ui.warn(_("warning: import the patch as a normal revision\n"
933 "(use --exact to import the patch as a merge)\n"))
933 "(use --exact to import the patch as a merge)\n"))
934 else:
934 else:
935 p1, p2 = parents
935 p1, p2 = parents
936
936
937 n = None
937 n = None
938 if update:
938 if update:
939 if p1 != parents[0]:
939 if p1 != parents[0]:
940 updatefunc(repo, p1.node())
940 updatefunc(repo, p1.node())
941 if p2 != parents[1]:
941 if p2 != parents[1]:
942 repo.setparents(p1.node(), p2.node())
942 repo.setparents(p1.node(), p2.node())
943
943
944 if opts.get('exact') or importbranch:
944 if opts.get('exact') or importbranch:
945 repo.dirstate.setbranch(branch or 'default')
945 repo.dirstate.setbranch(branch or 'default')
946
946
947 partial = opts.get('partial', False)
947 partial = opts.get('partial', False)
948 files = set()
948 files = set()
949 try:
949 try:
950 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
950 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
951 files=files, eolmode=None, similarity=sim / 100.0)
951 files=files, eolmode=None, similarity=sim / 100.0)
952 except patch.PatchError as e:
952 except patch.PatchError as e:
953 if not partial:
953 if not partial:
954 raise error.Abort(str(e))
954 raise error.Abort(str(e))
955 if partial:
955 if partial:
956 rejects = True
956 rejects = True
957
957
958 files = list(files)
958 files = list(files)
959 if nocommit:
959 if nocommit:
960 if message:
960 if message:
961 msgs.append(message)
961 msgs.append(message)
962 else:
962 else:
963 if opts.get('exact') or p2:
963 if opts.get('exact') or p2:
964 # If you got here, you either use --force and know what
964 # If you got here, you either use --force and know what
965 # you are doing or used --exact or a merge patch while
965 # you are doing or used --exact or a merge patch while
966 # being updated to its first parent.
966 # being updated to its first parent.
967 m = None
967 m = None
968 else:
968 else:
969 m = scmutil.matchfiles(repo, files or [])
969 m = scmutil.matchfiles(repo, files or [])
970 editform = mergeeditform(repo[None], 'import.normal')
970 editform = mergeeditform(repo[None], 'import.normal')
971 if opts.get('exact'):
971 if opts.get('exact'):
972 editor = None
972 editor = None
973 else:
973 else:
974 editor = getcommiteditor(editform=editform, **opts)
974 editor = getcommiteditor(editform=editform, **opts)
975 extra = {}
975 extra = {}
976 for idfunc in extrapreimport:
976 for idfunc in extrapreimport:
977 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
977 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
978 overrides = {}
978 overrides = {}
979 if partial:
979 if partial:
980 overrides[('ui', 'allowemptycommit')] = True
980 overrides[('ui', 'allowemptycommit')] = True
981 with repo.ui.configoverride(overrides, 'import'):
981 with repo.ui.configoverride(overrides, 'import'):
982 n = repo.commit(message, user,
982 n = repo.commit(message, user,
983 date, match=m,
983 date, match=m,
984 editor=editor, extra=extra)
984 editor=editor, extra=extra)
985 for idfunc in extrapostimport:
985 for idfunc in extrapostimport:
986 extrapostimportmap[idfunc](repo[n])
986 extrapostimportmap[idfunc](repo[n])
987 else:
987 else:
988 if opts.get('exact') or importbranch:
988 if opts.get('exact') or importbranch:
989 branch = branch or 'default'
989 branch = branch or 'default'
990 else:
990 else:
991 branch = p1.branch()
991 branch = p1.branch()
992 store = patch.filestore()
992 store = patch.filestore()
993 try:
993 try:
994 files = set()
994 files = set()
995 try:
995 try:
996 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
996 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
997 files, eolmode=None)
997 files, eolmode=None)
998 except patch.PatchError as e:
998 except patch.PatchError as e:
999 raise error.Abort(str(e))
999 raise error.Abort(str(e))
1000 if opts.get('exact'):
1000 if opts.get('exact'):
1001 editor = None
1001 editor = None
1002 else:
1002 else:
1003 editor = getcommiteditor(editform='import.bypass')
1003 editor = getcommiteditor(editform='import.bypass')
1004 memctx = context.makememctx(repo, (p1.node(), p2.node()),
1004 memctx = context.makememctx(repo, (p1.node(), p2.node()),
1005 message,
1005 message,
1006 user,
1006 user,
1007 date,
1007 date,
1008 branch, files, store,
1008 branch, files, store,
1009 editor=editor)
1009 editor=editor)
1010 n = memctx.commit()
1010 n = memctx.commit()
1011 finally:
1011 finally:
1012 store.close()
1012 store.close()
1013 if opts.get('exact') and nocommit:
1013 if opts.get('exact') and nocommit:
1014 # --exact with --no-commit is still useful in that it does merge
1014 # --exact with --no-commit is still useful in that it does merge
1015 # and branch bits
1015 # and branch bits
1016 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1016 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1017 elif opts.get('exact') and hex(n) != nodeid:
1017 elif opts.get('exact') and hex(n) != nodeid:
1018 raise error.Abort(_('patch is damaged or loses information'))
1018 raise error.Abort(_('patch is damaged or loses information'))
1019 msg = _('applied to working directory')
1019 msg = _('applied to working directory')
1020 if n:
1020 if n:
1021 # i18n: refers to a short changeset id
1021 # i18n: refers to a short changeset id
1022 msg = _('created %s') % short(n)
1022 msg = _('created %s') % short(n)
1023 return (msg, n, rejects)
1023 return (msg, n, rejects)
1024 finally:
1024 finally:
1025 os.unlink(tmpname)
1025 os.unlink(tmpname)
1026
1026
1027 # facility to let extensions include additional data in an exported patch
1027 # facility to let extensions include additional data in an exported patch
1028 # list of identifiers to be executed in order
1028 # list of identifiers to be executed in order
1029 extraexport = []
1029 extraexport = []
1030 # mapping from identifier to actual export function
1030 # mapping from identifier to actual export function
1031 # function as to return a string to be added to the header or None
1031 # function as to return a string to be added to the header or None
1032 # it is given two arguments (sequencenumber, changectx)
1032 # it is given two arguments (sequencenumber, changectx)
1033 extraexportmap = {}
1033 extraexportmap = {}
1034
1034
1035 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1035 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1036 opts=None, match=None):
1036 opts=None, match=None):
1037 '''export changesets as hg patches.'''
1037 '''export changesets as hg patches.'''
1038
1038
1039 total = len(revs)
1039 total = len(revs)
1040 revwidth = max([len(str(rev)) for rev in revs])
1040 revwidth = max([len(str(rev)) for rev in revs])
1041 filemode = {}
1041 filemode = {}
1042
1042
1043 def single(rev, seqno, fp):
1043 def single(rev, seqno, fp):
1044 ctx = repo[rev]
1044 ctx = repo[rev]
1045 node = ctx.node()
1045 node = ctx.node()
1046 parents = [p.node() for p in ctx.parents() if p]
1046 parents = [p.node() for p in ctx.parents() if p]
1047 branch = ctx.branch()
1047 branch = ctx.branch()
1048 if switch_parent:
1048 if switch_parent:
1049 parents.reverse()
1049 parents.reverse()
1050
1050
1051 if parents:
1051 if parents:
1052 prev = parents[0]
1052 prev = parents[0]
1053 else:
1053 else:
1054 prev = nullid
1054 prev = nullid
1055
1055
1056 shouldclose = False
1056 shouldclose = False
1057 if not fp and len(template) > 0:
1057 if not fp and len(template) > 0:
1058 desc_lines = ctx.description().rstrip().split('\n')
1058 desc_lines = ctx.description().rstrip().split('\n')
1059 desc = desc_lines[0] #Commit always has a first line.
1059 desc = desc_lines[0] #Commit always has a first line.
1060 fp = makefileobj(repo, template, node, desc=desc, total=total,
1060 fp = makefileobj(repo, template, node, desc=desc, total=total,
1061 seqno=seqno, revwidth=revwidth, mode='wb',
1061 seqno=seqno, revwidth=revwidth, mode='wb',
1062 modemap=filemode)
1062 modemap=filemode)
1063 shouldclose = True
1063 shouldclose = True
1064 if fp and not getattr(fp, 'name', '<unnamed>').startswith('<'):
1064 if fp and not getattr(fp, 'name', '<unnamed>').startswith('<'):
1065 repo.ui.note("%s\n" % fp.name)
1065 repo.ui.note("%s\n" % fp.name)
1066
1066
1067 if not fp:
1067 if not fp:
1068 write = repo.ui.write
1068 write = repo.ui.write
1069 else:
1069 else:
1070 def write(s, **kw):
1070 def write(s, **kw):
1071 fp.write(s)
1071 fp.write(s)
1072
1072
1073 write("# HG changeset patch\n")
1073 write("# HG changeset patch\n")
1074 write("# User %s\n" % ctx.user())
1074 write("# User %s\n" % ctx.user())
1075 write("# Date %d %d\n" % ctx.date())
1075 write("# Date %d %d\n" % ctx.date())
1076 write("# %s\n" % util.datestr(ctx.date()))
1076 write("# %s\n" % util.datestr(ctx.date()))
1077 if branch and branch != 'default':
1077 if branch and branch != 'default':
1078 write("# Branch %s\n" % branch)
1078 write("# Branch %s\n" % branch)
1079 write("# Node ID %s\n" % hex(node))
1079 write("# Node ID %s\n" % hex(node))
1080 write("# Parent %s\n" % hex(prev))
1080 write("# Parent %s\n" % hex(prev))
1081 if len(parents) > 1:
1081 if len(parents) > 1:
1082 write("# Parent %s\n" % hex(parents[1]))
1082 write("# Parent %s\n" % hex(parents[1]))
1083
1083
1084 for headerid in extraexport:
1084 for headerid in extraexport:
1085 header = extraexportmap[headerid](seqno, ctx)
1085 header = extraexportmap[headerid](seqno, ctx)
1086 if header is not None:
1086 if header is not None:
1087 write('# %s\n' % header)
1087 write('# %s\n' % header)
1088 write(ctx.description().rstrip())
1088 write(ctx.description().rstrip())
1089 write("\n\n")
1089 write("\n\n")
1090
1090
1091 for chunk, label in patch.diffui(repo, prev, node, match, opts=opts):
1091 for chunk, label in patch.diffui(repo, prev, node, match, opts=opts):
1092 write(chunk, label=label)
1092 write(chunk, label=label)
1093
1093
1094 if shouldclose:
1094 if shouldclose:
1095 fp.close()
1095 fp.close()
1096
1096
1097 for seqno, rev in enumerate(revs):
1097 for seqno, rev in enumerate(revs):
1098 single(rev, seqno + 1, fp)
1098 single(rev, seqno + 1, fp)
1099
1099
1100 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1100 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1101 changes=None, stat=False, fp=None, prefix='',
1101 changes=None, stat=False, fp=None, prefix='',
1102 root='', listsubrepos=False):
1102 root='', listsubrepos=False):
1103 '''show diff or diffstat.'''
1103 '''show diff or diffstat.'''
1104 if fp is None:
1104 if fp is None:
1105 write = ui.write
1105 write = ui.write
1106 else:
1106 else:
1107 def write(s, **kw):
1107 def write(s, **kw):
1108 fp.write(s)
1108 fp.write(s)
1109
1109
1110 if root:
1110 if root:
1111 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1111 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1112 else:
1112 else:
1113 relroot = ''
1113 relroot = ''
1114 if relroot != '':
1114 if relroot != '':
1115 # XXX relative roots currently don't work if the root is within a
1115 # XXX relative roots currently don't work if the root is within a
1116 # subrepo
1116 # subrepo
1117 uirelroot = match.uipath(relroot)
1117 uirelroot = match.uipath(relroot)
1118 relroot += '/'
1118 relroot += '/'
1119 for matchroot in match.files():
1119 for matchroot in match.files():
1120 if not matchroot.startswith(relroot):
1120 if not matchroot.startswith(relroot):
1121 ui.warn(_('warning: %s not inside relative root %s\n') % (
1121 ui.warn(_('warning: %s not inside relative root %s\n') % (
1122 match.uipath(matchroot), uirelroot))
1122 match.uipath(matchroot), uirelroot))
1123
1123
1124 if stat:
1124 if stat:
1125 diffopts = diffopts.copy(context=0)
1125 diffopts = diffopts.copy(context=0)
1126 width = 80
1126 width = 80
1127 if not ui.plain():
1127 if not ui.plain():
1128 width = ui.termwidth()
1128 width = ui.termwidth()
1129 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1129 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1130 prefix=prefix, relroot=relroot)
1130 prefix=prefix, relroot=relroot)
1131 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1131 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1132 width=width):
1132 width=width):
1133 write(chunk, label=label)
1133 write(chunk, label=label)
1134 else:
1134 else:
1135 for chunk, label in patch.diffui(repo, node1, node2, match,
1135 for chunk, label in patch.diffui(repo, node1, node2, match,
1136 changes, diffopts, prefix=prefix,
1136 changes, diffopts, prefix=prefix,
1137 relroot=relroot):
1137 relroot=relroot):
1138 write(chunk, label=label)
1138 write(chunk, label=label)
1139
1139
1140 if listsubrepos:
1140 if listsubrepos:
1141 ctx1 = repo[node1]
1141 ctx1 = repo[node1]
1142 ctx2 = repo[node2]
1142 ctx2 = repo[node2]
1143 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1143 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1144 tempnode2 = node2
1144 tempnode2 = node2
1145 try:
1145 try:
1146 if node2 is not None:
1146 if node2 is not None:
1147 tempnode2 = ctx2.substate[subpath][1]
1147 tempnode2 = ctx2.substate[subpath][1]
1148 except KeyError:
1148 except KeyError:
1149 # A subrepo that existed in node1 was deleted between node1 and
1149 # A subrepo that existed in node1 was deleted between node1 and
1150 # node2 (inclusive). Thus, ctx2's substate won't contain that
1150 # node2 (inclusive). Thus, ctx2's substate won't contain that
1151 # subpath. The best we can do is to ignore it.
1151 # subpath. The best we can do is to ignore it.
1152 tempnode2 = None
1152 tempnode2 = None
1153 submatch = matchmod.subdirmatcher(subpath, match)
1153 submatch = matchmod.subdirmatcher(subpath, match)
1154 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1154 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1155 stat=stat, fp=fp, prefix=prefix)
1155 stat=stat, fp=fp, prefix=prefix)
1156
1156
1157 def _changesetlabels(ctx):
1157 def _changesetlabels(ctx):
1158 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()]
1158 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()]
1159 if ctx.obsolete():
1159 if ctx.obsolete():
1160 labels.append('changeset.obsolete')
1160 labels.append('changeset.obsolete')
1161 if ctx.troubled():
1161 if ctx.troubled():
1162 labels.append('changeset.troubled')
1162 labels.append('changeset.troubled')
1163 for trouble in ctx.troubles():
1163 for trouble in ctx.troubles():
1164 labels.append('trouble.%s' % trouble)
1164 labels.append('trouble.%s' % trouble)
1165 return ' '.join(labels)
1165 return ' '.join(labels)
1166
1166
1167 class changeset_printer(object):
1167 class changeset_printer(object):
1168 '''show changeset information when templating not requested.'''
1168 '''show changeset information when templating not requested.'''
1169
1169
1170 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1170 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1171 self.ui = ui
1171 self.ui = ui
1172 self.repo = repo
1172 self.repo = repo
1173 self.buffered = buffered
1173 self.buffered = buffered
1174 self.matchfn = matchfn
1174 self.matchfn = matchfn
1175 self.diffopts = diffopts
1175 self.diffopts = diffopts
1176 self.header = {}
1176 self.header = {}
1177 self.hunk = {}
1177 self.hunk = {}
1178 self.lastheader = None
1178 self.lastheader = None
1179 self.footer = None
1179 self.footer = None
1180
1180
1181 def flush(self, ctx):
1181 def flush(self, ctx):
1182 rev = ctx.rev()
1182 rev = ctx.rev()
1183 if rev in self.header:
1183 if rev in self.header:
1184 h = self.header[rev]
1184 h = self.header[rev]
1185 if h != self.lastheader:
1185 if h != self.lastheader:
1186 self.lastheader = h
1186 self.lastheader = h
1187 self.ui.write(h)
1187 self.ui.write(h)
1188 del self.header[rev]
1188 del self.header[rev]
1189 if rev in self.hunk:
1189 if rev in self.hunk:
1190 self.ui.write(self.hunk[rev])
1190 self.ui.write(self.hunk[rev])
1191 del self.hunk[rev]
1191 del self.hunk[rev]
1192 return 1
1192 return 1
1193 return 0
1193 return 0
1194
1194
1195 def close(self):
1195 def close(self):
1196 if self.footer:
1196 if self.footer:
1197 self.ui.write(self.footer)
1197 self.ui.write(self.footer)
1198
1198
1199 def show(self, ctx, copies=None, matchfn=None, **props):
1199 def show(self, ctx, copies=None, matchfn=None, **props):
1200 if self.buffered:
1200 if self.buffered:
1201 self.ui.pushbuffer(labeled=True)
1201 self.ui.pushbuffer(labeled=True)
1202 self._show(ctx, copies, matchfn, props)
1202 self._show(ctx, copies, matchfn, props)
1203 self.hunk[ctx.rev()] = self.ui.popbuffer()
1203 self.hunk[ctx.rev()] = self.ui.popbuffer()
1204 else:
1204 else:
1205 self._show(ctx, copies, matchfn, props)
1205 self._show(ctx, copies, matchfn, props)
1206
1206
1207 def _show(self, ctx, copies, matchfn, props):
1207 def _show(self, ctx, copies, matchfn, props):
1208 '''show a single changeset or file revision'''
1208 '''show a single changeset or file revision'''
1209 changenode = ctx.node()
1209 changenode = ctx.node()
1210 rev = ctx.rev()
1210 rev = ctx.rev()
1211 if self.ui.debugflag:
1211 if self.ui.debugflag:
1212 hexfunc = hex
1212 hexfunc = hex
1213 else:
1213 else:
1214 hexfunc = short
1214 hexfunc = short
1215 # as of now, wctx.node() and wctx.rev() return None, but we want to
1215 # as of now, wctx.node() and wctx.rev() return None, but we want to
1216 # show the same values as {node} and {rev} templatekw
1216 # show the same values as {node} and {rev} templatekw
1217 revnode = (scmutil.intrev(rev), hexfunc(bin(ctx.hex())))
1217 revnode = (scmutil.intrev(rev), hexfunc(bin(ctx.hex())))
1218
1218
1219 if self.ui.quiet:
1219 if self.ui.quiet:
1220 self.ui.write("%d:%s\n" % revnode, label='log.node')
1220 self.ui.write("%d:%s\n" % revnode, label='log.node')
1221 return
1221 return
1222
1222
1223 date = util.datestr(ctx.date())
1223 date = util.datestr(ctx.date())
1224
1224
1225 # i18n: column positioning for "hg log"
1225 # i18n: column positioning for "hg log"
1226 self.ui.write(_("changeset: %d:%s\n") % revnode,
1226 self.ui.write(_("changeset: %d:%s\n") % revnode,
1227 label=_changesetlabels(ctx))
1227 label=_changesetlabels(ctx))
1228
1228
1229 # branches are shown first before any other names due to backwards
1229 # branches are shown first before any other names due to backwards
1230 # compatibility
1230 # compatibility
1231 branch = ctx.branch()
1231 branch = ctx.branch()
1232 # don't show the default branch name
1232 # don't show the default branch name
1233 if branch != 'default':
1233 if branch != 'default':
1234 # i18n: column positioning for "hg log"
1234 # i18n: column positioning for "hg log"
1235 self.ui.write(_("branch: %s\n") % branch,
1235 self.ui.write(_("branch: %s\n") % branch,
1236 label='log.branch')
1236 label='log.branch')
1237
1237
1238 for nsname, ns in self.repo.names.iteritems():
1238 for nsname, ns in self.repo.names.iteritems():
1239 # branches has special logic already handled above, so here we just
1239 # branches has special logic already handled above, so here we just
1240 # skip it
1240 # skip it
1241 if nsname == 'branches':
1241 if nsname == 'branches':
1242 continue
1242 continue
1243 # we will use the templatename as the color name since those two
1243 # we will use the templatename as the color name since those two
1244 # should be the same
1244 # should be the same
1245 for name in ns.names(self.repo, changenode):
1245 for name in ns.names(self.repo, changenode):
1246 self.ui.write(ns.logfmt % name,
1246 self.ui.write(ns.logfmt % name,
1247 label='log.%s' % ns.colorname)
1247 label='log.%s' % ns.colorname)
1248 if self.ui.debugflag:
1248 if self.ui.debugflag:
1249 # i18n: column positioning for "hg log"
1249 # i18n: column positioning for "hg log"
1250 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1250 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1251 label='log.phase')
1251 label='log.phase')
1252 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1252 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1253 label = 'log.parent changeset.%s' % pctx.phasestr()
1253 label = 'log.parent changeset.%s' % pctx.phasestr()
1254 # i18n: column positioning for "hg log"
1254 # i18n: column positioning for "hg log"
1255 self.ui.write(_("parent: %d:%s\n")
1255 self.ui.write(_("parent: %d:%s\n")
1256 % (pctx.rev(), hexfunc(pctx.node())),
1256 % (pctx.rev(), hexfunc(pctx.node())),
1257 label=label)
1257 label=label)
1258
1258
1259 if self.ui.debugflag and rev is not None:
1259 if self.ui.debugflag and rev is not None:
1260 mnode = ctx.manifestnode()
1260 mnode = ctx.manifestnode()
1261 # i18n: column positioning for "hg log"
1261 # i18n: column positioning for "hg log"
1262 self.ui.write(_("manifest: %d:%s\n") %
1262 self.ui.write(_("manifest: %d:%s\n") %
1263 (self.repo.manifestlog._revlog.rev(mnode),
1263 (self.repo.manifestlog._revlog.rev(mnode),
1264 hex(mnode)),
1264 hex(mnode)),
1265 label='ui.debug log.manifest')
1265 label='ui.debug log.manifest')
1266 # i18n: column positioning for "hg log"
1266 # i18n: column positioning for "hg log"
1267 self.ui.write(_("user: %s\n") % ctx.user(),
1267 self.ui.write(_("user: %s\n") % ctx.user(),
1268 label='log.user')
1268 label='log.user')
1269 # i18n: column positioning for "hg log"
1269 # i18n: column positioning for "hg log"
1270 self.ui.write(_("date: %s\n") % date,
1270 self.ui.write(_("date: %s\n") % date,
1271 label='log.date')
1271 label='log.date')
1272
1272
1273 if ctx.troubled():
1273 if ctx.troubled():
1274 # i18n: column positioning for "hg log"
1274 # i18n: column positioning for "hg log"
1275 self.ui.write(_("trouble: %s\n") % ', '.join(ctx.troubles()),
1275 self.ui.write(_("trouble: %s\n") % ', '.join(ctx.troubles()),
1276 label='log.trouble')
1276 label='log.trouble')
1277
1277
1278 if self.ui.debugflag:
1278 if self.ui.debugflag:
1279 files = ctx.p1().status(ctx)[:3]
1279 files = ctx.p1().status(ctx)[:3]
1280 for key, value in zip([# i18n: column positioning for "hg log"
1280 for key, value in zip([# i18n: column positioning for "hg log"
1281 _("files:"),
1281 _("files:"),
1282 # i18n: column positioning for "hg log"
1282 # i18n: column positioning for "hg log"
1283 _("files+:"),
1283 _("files+:"),
1284 # i18n: column positioning for "hg log"
1284 # i18n: column positioning for "hg log"
1285 _("files-:")], files):
1285 _("files-:")], files):
1286 if value:
1286 if value:
1287 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1287 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1288 label='ui.debug log.files')
1288 label='ui.debug log.files')
1289 elif ctx.files() and self.ui.verbose:
1289 elif ctx.files() and self.ui.verbose:
1290 # i18n: column positioning for "hg log"
1290 # i18n: column positioning for "hg log"
1291 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1291 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1292 label='ui.note log.files')
1292 label='ui.note log.files')
1293 if copies and self.ui.verbose:
1293 if copies and self.ui.verbose:
1294 copies = ['%s (%s)' % c for c in copies]
1294 copies = ['%s (%s)' % c for c in copies]
1295 # i18n: column positioning for "hg log"
1295 # i18n: column positioning for "hg log"
1296 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1296 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1297 label='ui.note log.copies')
1297 label='ui.note log.copies')
1298
1298
1299 extra = ctx.extra()
1299 extra = ctx.extra()
1300 if extra and self.ui.debugflag:
1300 if extra and self.ui.debugflag:
1301 for key, value in sorted(extra.items()):
1301 for key, value in sorted(extra.items()):
1302 # i18n: column positioning for "hg log"
1302 # i18n: column positioning for "hg log"
1303 self.ui.write(_("extra: %s=%s\n")
1303 self.ui.write(_("extra: %s=%s\n")
1304 % (key, util.escapestr(value)),
1304 % (key, util.escapestr(value)),
1305 label='ui.debug log.extra')
1305 label='ui.debug log.extra')
1306
1306
1307 description = ctx.description().strip()
1307 description = ctx.description().strip()
1308 if description:
1308 if description:
1309 if self.ui.verbose:
1309 if self.ui.verbose:
1310 self.ui.write(_("description:\n"),
1310 self.ui.write(_("description:\n"),
1311 label='ui.note log.description')
1311 label='ui.note log.description')
1312 self.ui.write(description,
1312 self.ui.write(description,
1313 label='ui.note log.description')
1313 label='ui.note log.description')
1314 self.ui.write("\n\n")
1314 self.ui.write("\n\n")
1315 else:
1315 else:
1316 # i18n: column positioning for "hg log"
1316 # i18n: column positioning for "hg log"
1317 self.ui.write(_("summary: %s\n") %
1317 self.ui.write(_("summary: %s\n") %
1318 description.splitlines()[0],
1318 description.splitlines()[0],
1319 label='log.summary')
1319 label='log.summary')
1320 self.ui.write("\n")
1320 self.ui.write("\n")
1321
1321
1322 self.showpatch(ctx, matchfn)
1322 self.showpatch(ctx, matchfn)
1323
1323
1324 def showpatch(self, ctx, matchfn):
1324 def showpatch(self, ctx, matchfn):
1325 if not matchfn:
1325 if not matchfn:
1326 matchfn = self.matchfn
1326 matchfn = self.matchfn
1327 if matchfn:
1327 if matchfn:
1328 stat = self.diffopts.get('stat')
1328 stat = self.diffopts.get('stat')
1329 diff = self.diffopts.get('patch')
1329 diff = self.diffopts.get('patch')
1330 diffopts = patch.diffallopts(self.ui, self.diffopts)
1330 diffopts = patch.diffallopts(self.ui, self.diffopts)
1331 node = ctx.node()
1331 node = ctx.node()
1332 prev = ctx.p1().node()
1332 prev = ctx.p1().node()
1333 if stat:
1333 if stat:
1334 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1334 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1335 match=matchfn, stat=True)
1335 match=matchfn, stat=True)
1336 if diff:
1336 if diff:
1337 if stat:
1337 if stat:
1338 self.ui.write("\n")
1338 self.ui.write("\n")
1339 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1339 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1340 match=matchfn, stat=False)
1340 match=matchfn, stat=False)
1341 self.ui.write("\n")
1341 self.ui.write("\n")
1342
1342
1343 class jsonchangeset(changeset_printer):
1343 class jsonchangeset(changeset_printer):
1344 '''format changeset information.'''
1344 '''format changeset information.'''
1345
1345
1346 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1346 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1347 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1347 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1348 self.cache = {}
1348 self.cache = {}
1349 self._first = True
1349 self._first = True
1350
1350
1351 def close(self):
1351 def close(self):
1352 if not self._first:
1352 if not self._first:
1353 self.ui.write("\n]\n")
1353 self.ui.write("\n]\n")
1354 else:
1354 else:
1355 self.ui.write("[]\n")
1355 self.ui.write("[]\n")
1356
1356
1357 def _show(self, ctx, copies, matchfn, props):
1357 def _show(self, ctx, copies, matchfn, props):
1358 '''show a single changeset or file revision'''
1358 '''show a single changeset or file revision'''
1359 rev = ctx.rev()
1359 rev = ctx.rev()
1360 if rev is None:
1360 if rev is None:
1361 jrev = jnode = 'null'
1361 jrev = jnode = 'null'
1362 else:
1362 else:
1363 jrev = str(rev)
1363 jrev = str(rev)
1364 jnode = '"%s"' % hex(ctx.node())
1364 jnode = '"%s"' % hex(ctx.node())
1365 j = encoding.jsonescape
1365 j = encoding.jsonescape
1366
1366
1367 if self._first:
1367 if self._first:
1368 self.ui.write("[\n {")
1368 self.ui.write("[\n {")
1369 self._first = False
1369 self._first = False
1370 else:
1370 else:
1371 self.ui.write(",\n {")
1371 self.ui.write(",\n {")
1372
1372
1373 if self.ui.quiet:
1373 if self.ui.quiet:
1374 self.ui.write(('\n "rev": %s') % jrev)
1374 self.ui.write(('\n "rev": %s') % jrev)
1375 self.ui.write((',\n "node": %s') % jnode)
1375 self.ui.write((',\n "node": %s') % jnode)
1376 self.ui.write('\n }')
1376 self.ui.write('\n }')
1377 return
1377 return
1378
1378
1379 self.ui.write(('\n "rev": %s') % jrev)
1379 self.ui.write(('\n "rev": %s') % jrev)
1380 self.ui.write((',\n "node": %s') % jnode)
1380 self.ui.write((',\n "node": %s') % jnode)
1381 self.ui.write((',\n "branch": "%s"') % j(ctx.branch()))
1381 self.ui.write((',\n "branch": "%s"') % j(ctx.branch()))
1382 self.ui.write((',\n "phase": "%s"') % ctx.phasestr())
1382 self.ui.write((',\n "phase": "%s"') % ctx.phasestr())
1383 self.ui.write((',\n "user": "%s"') % j(ctx.user()))
1383 self.ui.write((',\n "user": "%s"') % j(ctx.user()))
1384 self.ui.write((',\n "date": [%d, %d]') % ctx.date())
1384 self.ui.write((',\n "date": [%d, %d]') % ctx.date())
1385 self.ui.write((',\n "desc": "%s"') % j(ctx.description()))
1385 self.ui.write((',\n "desc": "%s"') % j(ctx.description()))
1386
1386
1387 self.ui.write((',\n "bookmarks": [%s]') %
1387 self.ui.write((',\n "bookmarks": [%s]') %
1388 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1388 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1389 self.ui.write((',\n "tags": [%s]') %
1389 self.ui.write((',\n "tags": [%s]') %
1390 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1390 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1391 self.ui.write((',\n "parents": [%s]') %
1391 self.ui.write((',\n "parents": [%s]') %
1392 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1392 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1393
1393
1394 if self.ui.debugflag:
1394 if self.ui.debugflag:
1395 if rev is None:
1395 if rev is None:
1396 jmanifestnode = 'null'
1396 jmanifestnode = 'null'
1397 else:
1397 else:
1398 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1398 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1399 self.ui.write((',\n "manifest": %s') % jmanifestnode)
1399 self.ui.write((',\n "manifest": %s') % jmanifestnode)
1400
1400
1401 self.ui.write((',\n "extra": {%s}') %
1401 self.ui.write((',\n "extra": {%s}') %
1402 ", ".join('"%s": "%s"' % (j(k), j(v))
1402 ", ".join('"%s": "%s"' % (j(k), j(v))
1403 for k, v in ctx.extra().items()))
1403 for k, v in ctx.extra().items()))
1404
1404
1405 files = ctx.p1().status(ctx)
1405 files = ctx.p1().status(ctx)
1406 self.ui.write((',\n "modified": [%s]') %
1406 self.ui.write((',\n "modified": [%s]') %
1407 ", ".join('"%s"' % j(f) for f in files[0]))
1407 ", ".join('"%s"' % j(f) for f in files[0]))
1408 self.ui.write((',\n "added": [%s]') %
1408 self.ui.write((',\n "added": [%s]') %
1409 ", ".join('"%s"' % j(f) for f in files[1]))
1409 ", ".join('"%s"' % j(f) for f in files[1]))
1410 self.ui.write((',\n "removed": [%s]') %
1410 self.ui.write((',\n "removed": [%s]') %
1411 ", ".join('"%s"' % j(f) for f in files[2]))
1411 ", ".join('"%s"' % j(f) for f in files[2]))
1412
1412
1413 elif self.ui.verbose:
1413 elif self.ui.verbose:
1414 self.ui.write((',\n "files": [%s]') %
1414 self.ui.write((',\n "files": [%s]') %
1415 ", ".join('"%s"' % j(f) for f in ctx.files()))
1415 ", ".join('"%s"' % j(f) for f in ctx.files()))
1416
1416
1417 if copies:
1417 if copies:
1418 self.ui.write((',\n "copies": {%s}') %
1418 self.ui.write((',\n "copies": {%s}') %
1419 ", ".join('"%s": "%s"' % (j(k), j(v))
1419 ", ".join('"%s": "%s"' % (j(k), j(v))
1420 for k, v in copies))
1420 for k, v in copies))
1421
1421
1422 matchfn = self.matchfn
1422 matchfn = self.matchfn
1423 if matchfn:
1423 if matchfn:
1424 stat = self.diffopts.get('stat')
1424 stat = self.diffopts.get('stat')
1425 diff = self.diffopts.get('patch')
1425 diff = self.diffopts.get('patch')
1426 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1426 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1427 node, prev = ctx.node(), ctx.p1().node()
1427 node, prev = ctx.node(), ctx.p1().node()
1428 if stat:
1428 if stat:
1429 self.ui.pushbuffer()
1429 self.ui.pushbuffer()
1430 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1430 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1431 match=matchfn, stat=True)
1431 match=matchfn, stat=True)
1432 self.ui.write((',\n "diffstat": "%s"')
1432 self.ui.write((',\n "diffstat": "%s"')
1433 % j(self.ui.popbuffer()))
1433 % j(self.ui.popbuffer()))
1434 if diff:
1434 if diff:
1435 self.ui.pushbuffer()
1435 self.ui.pushbuffer()
1436 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1436 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1437 match=matchfn, stat=False)
1437 match=matchfn, stat=False)
1438 self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer()))
1438 self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer()))
1439
1439
1440 self.ui.write("\n }")
1440 self.ui.write("\n }")
1441
1441
1442 class changeset_templater(changeset_printer):
1442 class changeset_templater(changeset_printer):
1443 '''format changeset information.'''
1443 '''format changeset information.'''
1444
1444
1445 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1445 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1446 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1446 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1447 assert not (tmpl and mapfile)
1447 assert not (tmpl and mapfile)
1448 defaulttempl = templatekw.defaulttempl
1448 defaulttempl = templatekw.defaulttempl
1449 if mapfile:
1449 if mapfile:
1450 self.t = templater.templater.frommapfile(mapfile,
1450 self.t = templater.templater.frommapfile(mapfile,
1451 cache=defaulttempl)
1451 cache=defaulttempl)
1452 else:
1452 else:
1453 self.t = formatter.maketemplater(ui, 'changeset', tmpl,
1453 self.t = formatter.maketemplater(ui, 'changeset', tmpl,
1454 cache=defaulttempl)
1454 cache=defaulttempl)
1455
1455
1456 self._counter = itertools.count()
1456 self._counter = itertools.count()
1457 self.cache = {}
1457 self.cache = {}
1458
1458
1459 # find correct templates for current mode
1459 # find correct templates for current mode
1460 tmplmodes = [
1460 tmplmodes = [
1461 (True, None),
1461 (True, None),
1462 (self.ui.verbose, 'verbose'),
1462 (self.ui.verbose, 'verbose'),
1463 (self.ui.quiet, 'quiet'),
1463 (self.ui.quiet, 'quiet'),
1464 (self.ui.debugflag, 'debug'),
1464 (self.ui.debugflag, 'debug'),
1465 ]
1465 ]
1466
1466
1467 self._parts = {'header': '', 'footer': '', 'changeset': 'changeset',
1467 self._parts = {'header': '', 'footer': '', 'changeset': 'changeset',
1468 'docheader': '', 'docfooter': ''}
1468 'docheader': '', 'docfooter': ''}
1469 for mode, postfix in tmplmodes:
1469 for mode, postfix in tmplmodes:
1470 for t in self._parts:
1470 for t in self._parts:
1471 cur = t
1471 cur = t
1472 if postfix:
1472 if postfix:
1473 cur += "_" + postfix
1473 cur += "_" + postfix
1474 if mode and cur in self.t:
1474 if mode and cur in self.t:
1475 self._parts[t] = cur
1475 self._parts[t] = cur
1476
1476
1477 if self._parts['docheader']:
1477 if self._parts['docheader']:
1478 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1478 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1479
1479
1480 def close(self):
1480 def close(self):
1481 if self._parts['docfooter']:
1481 if self._parts['docfooter']:
1482 if not self.footer:
1482 if not self.footer:
1483 self.footer = ""
1483 self.footer = ""
1484 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1484 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1485 return super(changeset_templater, self).close()
1485 return super(changeset_templater, self).close()
1486
1486
1487 def _show(self, ctx, copies, matchfn, props):
1487 def _show(self, ctx, copies, matchfn, props):
1488 '''show a single changeset or file revision'''
1488 '''show a single changeset or file revision'''
1489 props = props.copy()
1489 props = props.copy()
1490 props.update(templatekw.keywords)
1490 props.update(templatekw.keywords)
1491 props['templ'] = self.t
1491 props['templ'] = self.t
1492 props['ctx'] = ctx
1492 props['ctx'] = ctx
1493 props['repo'] = self.repo
1493 props['repo'] = self.repo
1494 props['ui'] = self.repo.ui
1494 props['ui'] = self.repo.ui
1495 props['index'] = next(self._counter)
1495 props['index'] = next(self._counter)
1496 props['revcache'] = {'copies': copies}
1496 props['revcache'] = {'copies': copies}
1497 props['cache'] = self.cache
1497 props['cache'] = self.cache
1498
1498
1499 # write header
1499 # write header
1500 if self._parts['header']:
1500 if self._parts['header']:
1501 h = templater.stringify(self.t(self._parts['header'], **props))
1501 h = templater.stringify(self.t(self._parts['header'], **props))
1502 if self.buffered:
1502 if self.buffered:
1503 self.header[ctx.rev()] = h
1503 self.header[ctx.rev()] = h
1504 else:
1504 else:
1505 if self.lastheader != h:
1505 if self.lastheader != h:
1506 self.lastheader = h
1506 self.lastheader = h
1507 self.ui.write(h)
1507 self.ui.write(h)
1508
1508
1509 # write changeset metadata, then patch if requested
1509 # write changeset metadata, then patch if requested
1510 key = self._parts['changeset']
1510 key = self._parts['changeset']
1511 self.ui.write(templater.stringify(self.t(key, **props)))
1511 self.ui.write(templater.stringify(self.t(key, **props)))
1512 self.showpatch(ctx, matchfn)
1512 self.showpatch(ctx, matchfn)
1513
1513
1514 if self._parts['footer']:
1514 if self._parts['footer']:
1515 if not self.footer:
1515 if not self.footer:
1516 self.footer = templater.stringify(
1516 self.footer = templater.stringify(
1517 self.t(self._parts['footer'], **props))
1517 self.t(self._parts['footer'], **props))
1518
1518
1519 def gettemplate(ui, tmpl, style):
1519 def gettemplate(ui, tmpl, style):
1520 """
1520 """
1521 Find the template matching the given template spec or style.
1521 Find the template matching the given template spec or style.
1522 """
1522 """
1523
1523
1524 # ui settings
1524 # ui settings
1525 if not tmpl and not style: # template are stronger than style
1525 if not tmpl and not style: # template are stronger than style
1526 tmpl = ui.config('ui', 'logtemplate')
1526 tmpl = ui.config('ui', 'logtemplate')
1527 if tmpl:
1527 if tmpl:
1528 return templater.unquotestring(tmpl), None
1528 return templater.unquotestring(tmpl), None
1529 else:
1529 else:
1530 style = util.expandpath(ui.config('ui', 'style', ''))
1530 style = util.expandpath(ui.config('ui', 'style', ''))
1531
1531
1532 if not tmpl and style:
1532 if not tmpl and style:
1533 mapfile = style
1533 mapfile = style
1534 if not os.path.split(mapfile)[0]:
1534 if not os.path.split(mapfile)[0]:
1535 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1535 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1536 or templater.templatepath(mapfile))
1536 or templater.templatepath(mapfile))
1537 if mapname:
1537 if mapname:
1538 mapfile = mapname
1538 mapfile = mapname
1539 return None, mapfile
1539 return None, mapfile
1540
1540
1541 if not tmpl:
1541 if not tmpl:
1542 return None, None
1542 return None, None
1543
1543
1544 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1544 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1545
1545
1546 def show_changeset(ui, repo, opts, buffered=False):
1546 def show_changeset(ui, repo, opts, buffered=False):
1547 """show one changeset using template or regular display.
1547 """show one changeset using template or regular display.
1548
1548
1549 Display format will be the first non-empty hit of:
1549 Display format will be the first non-empty hit of:
1550 1. option 'template'
1550 1. option 'template'
1551 2. option 'style'
1551 2. option 'style'
1552 3. [ui] setting 'logtemplate'
1552 3. [ui] setting 'logtemplate'
1553 4. [ui] setting 'style'
1553 4. [ui] setting 'style'
1554 If all of these values are either the unset or the empty string,
1554 If all of these values are either the unset or the empty string,
1555 regular display via changeset_printer() is done.
1555 regular display via changeset_printer() is done.
1556 """
1556 """
1557 # options
1557 # options
1558 matchfn = None
1558 matchfn = None
1559 if opts.get('patch') or opts.get('stat'):
1559 if opts.get('patch') or opts.get('stat'):
1560 matchfn = scmutil.matchall(repo)
1560 matchfn = scmutil.matchall(repo)
1561
1561
1562 if opts.get('template') == 'json':
1562 if opts.get('template') == 'json':
1563 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1563 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1564
1564
1565 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1565 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1566
1566
1567 if not tmpl and not mapfile:
1567 if not tmpl and not mapfile:
1568 return changeset_printer(ui, repo, matchfn, opts, buffered)
1568 return changeset_printer(ui, repo, matchfn, opts, buffered)
1569
1569
1570 return changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile, buffered)
1570 return changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile, buffered)
1571
1571
1572 def showmarker(fm, marker, index=None):
1572 def showmarker(fm, marker, index=None):
1573 """utility function to display obsolescence marker in a readable way
1573 """utility function to display obsolescence marker in a readable way
1574
1574
1575 To be used by debug function."""
1575 To be used by debug function."""
1576 if index is not None:
1576 if index is not None:
1577 fm.write('index', '%i ', index)
1577 fm.write('index', '%i ', index)
1578 fm.write('precnode', '%s ', hex(marker.precnode()))
1578 fm.write('precnode', '%s ', hex(marker.precnode()))
1579 succs = marker.succnodes()
1579 succs = marker.succnodes()
1580 fm.condwrite(succs, 'succnodes', '%s ',
1580 fm.condwrite(succs, 'succnodes', '%s ',
1581 fm.formatlist(map(hex, succs), name='node'))
1581 fm.formatlist(map(hex, succs), name='node'))
1582 fm.write('flag', '%X ', marker.flags())
1582 fm.write('flag', '%X ', marker.flags())
1583 parents = marker.parentnodes()
1583 parents = marker.parentnodes()
1584 if parents is not None:
1584 if parents is not None:
1585 fm.write('parentnodes', '{%s} ',
1585 fm.write('parentnodes', '{%s} ',
1586 fm.formatlist(map(hex, parents), name='node', sep=', '))
1586 fm.formatlist(map(hex, parents), name='node', sep=', '))
1587 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
1587 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
1588 meta = marker.metadata().copy()
1588 meta = marker.metadata().copy()
1589 meta.pop('date', None)
1589 meta.pop('date', None)
1590 fm.write('metadata', '{%s}', fm.formatdict(meta, fmt='%r: %r', sep=', '))
1590 fm.write('metadata', '{%s}', fm.formatdict(meta, fmt='%r: %r', sep=', '))
1591 fm.plain('\n')
1591 fm.plain('\n')
1592
1592
1593 def finddate(ui, repo, date):
1593 def finddate(ui, repo, date):
1594 """Find the tipmost changeset that matches the given date spec"""
1594 """Find the tipmost changeset that matches the given date spec"""
1595
1595
1596 df = util.matchdate(date)
1596 df = util.matchdate(date)
1597 m = scmutil.matchall(repo)
1597 m = scmutil.matchall(repo)
1598 results = {}
1598 results = {}
1599
1599
1600 def prep(ctx, fns):
1600 def prep(ctx, fns):
1601 d = ctx.date()
1601 d = ctx.date()
1602 if df(d[0]):
1602 if df(d[0]):
1603 results[ctx.rev()] = d
1603 results[ctx.rev()] = d
1604
1604
1605 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1605 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1606 rev = ctx.rev()
1606 rev = ctx.rev()
1607 if rev in results:
1607 if rev in results:
1608 ui.status(_("found revision %s from %s\n") %
1608 ui.status(_("found revision %s from %s\n") %
1609 (rev, util.datestr(results[rev])))
1609 (rev, util.datestr(results[rev])))
1610 return str(rev)
1610 return str(rev)
1611
1611
1612 raise error.Abort(_("revision matching date not found"))
1612 raise error.Abort(_("revision matching date not found"))
1613
1613
1614 def increasingwindows(windowsize=8, sizelimit=512):
1614 def increasingwindows(windowsize=8, sizelimit=512):
1615 while True:
1615 while True:
1616 yield windowsize
1616 yield windowsize
1617 if windowsize < sizelimit:
1617 if windowsize < sizelimit:
1618 windowsize *= 2
1618 windowsize *= 2
1619
1619
1620 class FileWalkError(Exception):
1620 class FileWalkError(Exception):
1621 pass
1621 pass
1622
1622
1623 def walkfilerevs(repo, match, follow, revs, fncache):
1623 def walkfilerevs(repo, match, follow, revs, fncache):
1624 '''Walks the file history for the matched files.
1624 '''Walks the file history for the matched files.
1625
1625
1626 Returns the changeset revs that are involved in the file history.
1626 Returns the changeset revs that are involved in the file history.
1627
1627
1628 Throws FileWalkError if the file history can't be walked using
1628 Throws FileWalkError if the file history can't be walked using
1629 filelogs alone.
1629 filelogs alone.
1630 '''
1630 '''
1631 wanted = set()
1631 wanted = set()
1632 copies = []
1632 copies = []
1633 minrev, maxrev = min(revs), max(revs)
1633 minrev, maxrev = min(revs), max(revs)
1634 def filerevgen(filelog, last):
1634 def filerevgen(filelog, last):
1635 """
1635 """
1636 Only files, no patterns. Check the history of each file.
1636 Only files, no patterns. Check the history of each file.
1637
1637
1638 Examines filelog entries within minrev, maxrev linkrev range
1638 Examines filelog entries within minrev, maxrev linkrev range
1639 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1639 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1640 tuples in backwards order
1640 tuples in backwards order
1641 """
1641 """
1642 cl_count = len(repo)
1642 cl_count = len(repo)
1643 revs = []
1643 revs = []
1644 for j in xrange(0, last + 1):
1644 for j in xrange(0, last + 1):
1645 linkrev = filelog.linkrev(j)
1645 linkrev = filelog.linkrev(j)
1646 if linkrev < minrev:
1646 if linkrev < minrev:
1647 continue
1647 continue
1648 # only yield rev for which we have the changelog, it can
1648 # only yield rev for which we have the changelog, it can
1649 # happen while doing "hg log" during a pull or commit
1649 # happen while doing "hg log" during a pull or commit
1650 if linkrev >= cl_count:
1650 if linkrev >= cl_count:
1651 break
1651 break
1652
1652
1653 parentlinkrevs = []
1653 parentlinkrevs = []
1654 for p in filelog.parentrevs(j):
1654 for p in filelog.parentrevs(j):
1655 if p != nullrev:
1655 if p != nullrev:
1656 parentlinkrevs.append(filelog.linkrev(p))
1656 parentlinkrevs.append(filelog.linkrev(p))
1657 n = filelog.node(j)
1657 n = filelog.node(j)
1658 revs.append((linkrev, parentlinkrevs,
1658 revs.append((linkrev, parentlinkrevs,
1659 follow and filelog.renamed(n)))
1659 follow and filelog.renamed(n)))
1660
1660
1661 return reversed(revs)
1661 return reversed(revs)
1662 def iterfiles():
1662 def iterfiles():
1663 pctx = repo['.']
1663 pctx = repo['.']
1664 for filename in match.files():
1664 for filename in match.files():
1665 if follow:
1665 if follow:
1666 if filename not in pctx:
1666 if filename not in pctx:
1667 raise error.Abort(_('cannot follow file not in parent '
1667 raise error.Abort(_('cannot follow file not in parent '
1668 'revision: "%s"') % filename)
1668 'revision: "%s"') % filename)
1669 yield filename, pctx[filename].filenode()
1669 yield filename, pctx[filename].filenode()
1670 else:
1670 else:
1671 yield filename, None
1671 yield filename, None
1672 for filename_node in copies:
1672 for filename_node in copies:
1673 yield filename_node
1673 yield filename_node
1674
1674
1675 for file_, node in iterfiles():
1675 for file_, node in iterfiles():
1676 filelog = repo.file(file_)
1676 filelog = repo.file(file_)
1677 if not len(filelog):
1677 if not len(filelog):
1678 if node is None:
1678 if node is None:
1679 # A zero count may be a directory or deleted file, so
1679 # A zero count may be a directory or deleted file, so
1680 # try to find matching entries on the slow path.
1680 # try to find matching entries on the slow path.
1681 if follow:
1681 if follow:
1682 raise error.Abort(
1682 raise error.Abort(
1683 _('cannot follow nonexistent file: "%s"') % file_)
1683 _('cannot follow nonexistent file: "%s"') % file_)
1684 raise FileWalkError("Cannot walk via filelog")
1684 raise FileWalkError("Cannot walk via filelog")
1685 else:
1685 else:
1686 continue
1686 continue
1687
1687
1688 if node is None:
1688 if node is None:
1689 last = len(filelog) - 1
1689 last = len(filelog) - 1
1690 else:
1690 else:
1691 last = filelog.rev(node)
1691 last = filelog.rev(node)
1692
1692
1693 # keep track of all ancestors of the file
1693 # keep track of all ancestors of the file
1694 ancestors = set([filelog.linkrev(last)])
1694 ancestors = set([filelog.linkrev(last)])
1695
1695
1696 # iterate from latest to oldest revision
1696 # iterate from latest to oldest revision
1697 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1697 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1698 if not follow:
1698 if not follow:
1699 if rev > maxrev:
1699 if rev > maxrev:
1700 continue
1700 continue
1701 else:
1701 else:
1702 # Note that last might not be the first interesting
1702 # Note that last might not be the first interesting
1703 # rev to us:
1703 # rev to us:
1704 # if the file has been changed after maxrev, we'll
1704 # if the file has been changed after maxrev, we'll
1705 # have linkrev(last) > maxrev, and we still need
1705 # have linkrev(last) > maxrev, and we still need
1706 # to explore the file graph
1706 # to explore the file graph
1707 if rev not in ancestors:
1707 if rev not in ancestors:
1708 continue
1708 continue
1709 # XXX insert 1327 fix here
1709 # XXX insert 1327 fix here
1710 if flparentlinkrevs:
1710 if flparentlinkrevs:
1711 ancestors.update(flparentlinkrevs)
1711 ancestors.update(flparentlinkrevs)
1712
1712
1713 fncache.setdefault(rev, []).append(file_)
1713 fncache.setdefault(rev, []).append(file_)
1714 wanted.add(rev)
1714 wanted.add(rev)
1715 if copied:
1715 if copied:
1716 copies.append(copied)
1716 copies.append(copied)
1717
1717
1718 return wanted
1718 return wanted
1719
1719
1720 class _followfilter(object):
1720 class _followfilter(object):
1721 def __init__(self, repo, onlyfirst=False):
1721 def __init__(self, repo, onlyfirst=False):
1722 self.repo = repo
1722 self.repo = repo
1723 self.startrev = nullrev
1723 self.startrev = nullrev
1724 self.roots = set()
1724 self.roots = set()
1725 self.onlyfirst = onlyfirst
1725 self.onlyfirst = onlyfirst
1726
1726
1727 def match(self, rev):
1727 def match(self, rev):
1728 def realparents(rev):
1728 def realparents(rev):
1729 if self.onlyfirst:
1729 if self.onlyfirst:
1730 return self.repo.changelog.parentrevs(rev)[0:1]
1730 return self.repo.changelog.parentrevs(rev)[0:1]
1731 else:
1731 else:
1732 return filter(lambda x: x != nullrev,
1732 return filter(lambda x: x != nullrev,
1733 self.repo.changelog.parentrevs(rev))
1733 self.repo.changelog.parentrevs(rev))
1734
1734
1735 if self.startrev == nullrev:
1735 if self.startrev == nullrev:
1736 self.startrev = rev
1736 self.startrev = rev
1737 return True
1737 return True
1738
1738
1739 if rev > self.startrev:
1739 if rev > self.startrev:
1740 # forward: all descendants
1740 # forward: all descendants
1741 if not self.roots:
1741 if not self.roots:
1742 self.roots.add(self.startrev)
1742 self.roots.add(self.startrev)
1743 for parent in realparents(rev):
1743 for parent in realparents(rev):
1744 if parent in self.roots:
1744 if parent in self.roots:
1745 self.roots.add(rev)
1745 self.roots.add(rev)
1746 return True
1746 return True
1747 else:
1747 else:
1748 # backwards: all parents
1748 # backwards: all parents
1749 if not self.roots:
1749 if not self.roots:
1750 self.roots.update(realparents(self.startrev))
1750 self.roots.update(realparents(self.startrev))
1751 if rev in self.roots:
1751 if rev in self.roots:
1752 self.roots.remove(rev)
1752 self.roots.remove(rev)
1753 self.roots.update(realparents(rev))
1753 self.roots.update(realparents(rev))
1754 return True
1754 return True
1755
1755
1756 return False
1756 return False
1757
1757
1758 def walkchangerevs(repo, match, opts, prepare):
1758 def walkchangerevs(repo, match, opts, prepare):
1759 '''Iterate over files and the revs in which they changed.
1759 '''Iterate over files and the revs in which they changed.
1760
1760
1761 Callers most commonly need to iterate backwards over the history
1761 Callers most commonly need to iterate backwards over the history
1762 in which they are interested. Doing so has awful (quadratic-looking)
1762 in which they are interested. Doing so has awful (quadratic-looking)
1763 performance, so we use iterators in a "windowed" way.
1763 performance, so we use iterators in a "windowed" way.
1764
1764
1765 We walk a window of revisions in the desired order. Within the
1765 We walk a window of revisions in the desired order. Within the
1766 window, we first walk forwards to gather data, then in the desired
1766 window, we first walk forwards to gather data, then in the desired
1767 order (usually backwards) to display it.
1767 order (usually backwards) to display it.
1768
1768
1769 This function returns an iterator yielding contexts. Before
1769 This function returns an iterator yielding contexts. Before
1770 yielding each context, the iterator will first call the prepare
1770 yielding each context, the iterator will first call the prepare
1771 function on each context in the window in forward order.'''
1771 function on each context in the window in forward order.'''
1772
1772
1773 follow = opts.get('follow') or opts.get('follow_first')
1773 follow = opts.get('follow') or opts.get('follow_first')
1774 revs = _logrevs(repo, opts)
1774 revs = _logrevs(repo, opts)
1775 if not revs:
1775 if not revs:
1776 return []
1776 return []
1777 wanted = set()
1777 wanted = set()
1778 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1778 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1779 opts.get('removed'))
1779 opts.get('removed'))
1780 fncache = {}
1780 fncache = {}
1781 change = repo.changectx
1781 change = repo.changectx
1782
1782
1783 # First step is to fill wanted, the set of revisions that we want to yield.
1783 # First step is to fill wanted, the set of revisions that we want to yield.
1784 # When it does not induce extra cost, we also fill fncache for revisions in
1784 # When it does not induce extra cost, we also fill fncache for revisions in
1785 # wanted: a cache of filenames that were changed (ctx.files()) and that
1785 # wanted: a cache of filenames that were changed (ctx.files()) and that
1786 # match the file filtering conditions.
1786 # match the file filtering conditions.
1787
1787
1788 if match.always():
1788 if match.always():
1789 # No files, no patterns. Display all revs.
1789 # No files, no patterns. Display all revs.
1790 wanted = revs
1790 wanted = revs
1791 elif not slowpath:
1791 elif not slowpath:
1792 # We only have to read through the filelog to find wanted revisions
1792 # We only have to read through the filelog to find wanted revisions
1793
1793
1794 try:
1794 try:
1795 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1795 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1796 except FileWalkError:
1796 except FileWalkError:
1797 slowpath = True
1797 slowpath = True
1798
1798
1799 # We decided to fall back to the slowpath because at least one
1799 # We decided to fall back to the slowpath because at least one
1800 # of the paths was not a file. Check to see if at least one of them
1800 # of the paths was not a file. Check to see if at least one of them
1801 # existed in history, otherwise simply return
1801 # existed in history, otherwise simply return
1802 for path in match.files():
1802 for path in match.files():
1803 if path == '.' or path in repo.store:
1803 if path == '.' or path in repo.store:
1804 break
1804 break
1805 else:
1805 else:
1806 return []
1806 return []
1807
1807
1808 if slowpath:
1808 if slowpath:
1809 # We have to read the changelog to match filenames against
1809 # We have to read the changelog to match filenames against
1810 # changed files
1810 # changed files
1811
1811
1812 if follow:
1812 if follow:
1813 raise error.Abort(_('can only follow copies/renames for explicit '
1813 raise error.Abort(_('can only follow copies/renames for explicit '
1814 'filenames'))
1814 'filenames'))
1815
1815
1816 # The slow path checks files modified in every changeset.
1816 # The slow path checks files modified in every changeset.
1817 # This is really slow on large repos, so compute the set lazily.
1817 # This is really slow on large repos, so compute the set lazily.
1818 class lazywantedset(object):
1818 class lazywantedset(object):
1819 def __init__(self):
1819 def __init__(self):
1820 self.set = set()
1820 self.set = set()
1821 self.revs = set(revs)
1821 self.revs = set(revs)
1822
1822
1823 # No need to worry about locality here because it will be accessed
1823 # No need to worry about locality here because it will be accessed
1824 # in the same order as the increasing window below.
1824 # in the same order as the increasing window below.
1825 def __contains__(self, value):
1825 def __contains__(self, value):
1826 if value in self.set:
1826 if value in self.set:
1827 return True
1827 return True
1828 elif not value in self.revs:
1828 elif not value in self.revs:
1829 return False
1829 return False
1830 else:
1830 else:
1831 self.revs.discard(value)
1831 self.revs.discard(value)
1832 ctx = change(value)
1832 ctx = change(value)
1833 matches = filter(match, ctx.files())
1833 matches = filter(match, ctx.files())
1834 if matches:
1834 if matches:
1835 fncache[value] = matches
1835 fncache[value] = matches
1836 self.set.add(value)
1836 self.set.add(value)
1837 return True
1837 return True
1838 return False
1838 return False
1839
1839
1840 def discard(self, value):
1840 def discard(self, value):
1841 self.revs.discard(value)
1841 self.revs.discard(value)
1842 self.set.discard(value)
1842 self.set.discard(value)
1843
1843
1844 wanted = lazywantedset()
1844 wanted = lazywantedset()
1845
1845
1846 # it might be worthwhile to do this in the iterator if the rev range
1846 # it might be worthwhile to do this in the iterator if the rev range
1847 # is descending and the prune args are all within that range
1847 # is descending and the prune args are all within that range
1848 for rev in opts.get('prune', ()):
1848 for rev in opts.get('prune', ()):
1849 rev = repo[rev].rev()
1849 rev = repo[rev].rev()
1850 ff = _followfilter(repo)
1850 ff = _followfilter(repo)
1851 stop = min(revs[0], revs[-1])
1851 stop = min(revs[0], revs[-1])
1852 for x in xrange(rev, stop - 1, -1):
1852 for x in xrange(rev, stop - 1, -1):
1853 if ff.match(x):
1853 if ff.match(x):
1854 wanted = wanted - [x]
1854 wanted = wanted - [x]
1855
1855
1856 # Now that wanted is correctly initialized, we can iterate over the
1856 # Now that wanted is correctly initialized, we can iterate over the
1857 # revision range, yielding only revisions in wanted.
1857 # revision range, yielding only revisions in wanted.
1858 def iterate():
1858 def iterate():
1859 if follow and match.always():
1859 if follow and match.always():
1860 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1860 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1861 def want(rev):
1861 def want(rev):
1862 return ff.match(rev) and rev in wanted
1862 return ff.match(rev) and rev in wanted
1863 else:
1863 else:
1864 def want(rev):
1864 def want(rev):
1865 return rev in wanted
1865 return rev in wanted
1866
1866
1867 it = iter(revs)
1867 it = iter(revs)
1868 stopiteration = False
1868 stopiteration = False
1869 for windowsize in increasingwindows():
1869 for windowsize in increasingwindows():
1870 nrevs = []
1870 nrevs = []
1871 for i in xrange(windowsize):
1871 for i in xrange(windowsize):
1872 rev = next(it, None)
1872 rev = next(it, None)
1873 if rev is None:
1873 if rev is None:
1874 stopiteration = True
1874 stopiteration = True
1875 break
1875 break
1876 elif want(rev):
1876 elif want(rev):
1877 nrevs.append(rev)
1877 nrevs.append(rev)
1878 for rev in sorted(nrevs):
1878 for rev in sorted(nrevs):
1879 fns = fncache.get(rev)
1879 fns = fncache.get(rev)
1880 ctx = change(rev)
1880 ctx = change(rev)
1881 if not fns:
1881 if not fns:
1882 def fns_generator():
1882 def fns_generator():
1883 for f in ctx.files():
1883 for f in ctx.files():
1884 if match(f):
1884 if match(f):
1885 yield f
1885 yield f
1886 fns = fns_generator()
1886 fns = fns_generator()
1887 prepare(ctx, fns)
1887 prepare(ctx, fns)
1888 for rev in nrevs:
1888 for rev in nrevs:
1889 yield change(rev)
1889 yield change(rev)
1890
1890
1891 if stopiteration:
1891 if stopiteration:
1892 break
1892 break
1893
1893
1894 return iterate()
1894 return iterate()
1895
1895
1896 def _makefollowlogfilematcher(repo, files, followfirst):
1896 def _makefollowlogfilematcher(repo, files, followfirst):
1897 # When displaying a revision with --patch --follow FILE, we have
1897 # When displaying a revision with --patch --follow FILE, we have
1898 # to know which file of the revision must be diffed. With
1898 # to know which file of the revision must be diffed. With
1899 # --follow, we want the names of the ancestors of FILE in the
1899 # --follow, we want the names of the ancestors of FILE in the
1900 # revision, stored in "fcache". "fcache" is populated by
1900 # revision, stored in "fcache". "fcache" is populated by
1901 # reproducing the graph traversal already done by --follow revset
1901 # reproducing the graph traversal already done by --follow revset
1902 # and relating revs to file names (which is not "correct" but
1902 # and relating revs to file names (which is not "correct" but
1903 # good enough).
1903 # good enough).
1904 fcache = {}
1904 fcache = {}
1905 fcacheready = [False]
1905 fcacheready = [False]
1906 pctx = repo['.']
1906 pctx = repo['.']
1907
1907
1908 def populate():
1908 def populate():
1909 for fn in files:
1909 for fn in files:
1910 fctx = pctx[fn]
1910 fctx = pctx[fn]
1911 fcache.setdefault(fctx.introrev(), set()).add(fctx.path())
1911 fcache.setdefault(fctx.introrev(), set()).add(fctx.path())
1912 for c in fctx.ancestors(followfirst=followfirst):
1912 for c in fctx.ancestors(followfirst=followfirst):
1913 fcache.setdefault(c.rev(), set()).add(c.path())
1913 fcache.setdefault(c.rev(), set()).add(c.path())
1914
1914
1915 def filematcher(rev):
1915 def filematcher(rev):
1916 if not fcacheready[0]:
1916 if not fcacheready[0]:
1917 # Lazy initialization
1917 # Lazy initialization
1918 fcacheready[0] = True
1918 fcacheready[0] = True
1919 populate()
1919 populate()
1920 return scmutil.matchfiles(repo, fcache.get(rev, []))
1920 return scmutil.matchfiles(repo, fcache.get(rev, []))
1921
1921
1922 return filematcher
1922 return filematcher
1923
1923
1924 def _makenofollowlogfilematcher(repo, pats, opts):
1924 def _makenofollowlogfilematcher(repo, pats, opts):
1925 '''hook for extensions to override the filematcher for non-follow cases'''
1925 '''hook for extensions to override the filematcher for non-follow cases'''
1926 return None
1926 return None
1927
1927
1928 def _makelogrevset(repo, pats, opts, revs):
1928 def _makelogrevset(repo, pats, opts, revs):
1929 """Return (expr, filematcher) where expr is a revset string built
1929 """Return (expr, filematcher) where expr is a revset string built
1930 from log options and file patterns or None. If --stat or --patch
1930 from log options and file patterns or None. If --stat or --patch
1931 are not passed filematcher is None. Otherwise it is a callable
1931 are not passed filematcher is None. Otherwise it is a callable
1932 taking a revision number and returning a match objects filtering
1932 taking a revision number and returning a match objects filtering
1933 the files to be detailed when displaying the revision.
1933 the files to be detailed when displaying the revision.
1934 """
1934 """
1935 opt2revset = {
1935 opt2revset = {
1936 'no_merges': ('not merge()', None),
1936 'no_merges': ('not merge()', None),
1937 'only_merges': ('merge()', None),
1937 'only_merges': ('merge()', None),
1938 '_ancestors': ('ancestors(%(val)s)', None),
1938 '_ancestors': ('ancestors(%(val)s)', None),
1939 '_fancestors': ('_firstancestors(%(val)s)', None),
1939 '_fancestors': ('_firstancestors(%(val)s)', None),
1940 '_descendants': ('descendants(%(val)s)', None),
1940 '_descendants': ('descendants(%(val)s)', None),
1941 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1941 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1942 '_matchfiles': ('_matchfiles(%(val)s)', None),
1942 '_matchfiles': ('_matchfiles(%(val)s)', None),
1943 'date': ('date(%(val)r)', None),
1943 'date': ('date(%(val)r)', None),
1944 'branch': ('branch(%(val)r)', ' or '),
1944 'branch': ('branch(%(val)r)', ' or '),
1945 '_patslog': ('filelog(%(val)r)', ' or '),
1945 '_patslog': ('filelog(%(val)r)', ' or '),
1946 '_patsfollow': ('follow(%(val)r)', ' or '),
1946 '_patsfollow': ('follow(%(val)r)', ' or '),
1947 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1947 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1948 'keyword': ('keyword(%(val)r)', ' or '),
1948 'keyword': ('keyword(%(val)r)', ' or '),
1949 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1949 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1950 'user': ('user(%(val)r)', ' or '),
1950 'user': ('user(%(val)r)', ' or '),
1951 }
1951 }
1952
1952
1953 opts = dict(opts)
1953 opts = dict(opts)
1954 # follow or not follow?
1954 # follow or not follow?
1955 follow = opts.get('follow') or opts.get('follow_first')
1955 follow = opts.get('follow') or opts.get('follow_first')
1956 if opts.get('follow_first'):
1956 if opts.get('follow_first'):
1957 followfirst = 1
1957 followfirst = 1
1958 else:
1958 else:
1959 followfirst = 0
1959 followfirst = 0
1960 # --follow with FILE behavior depends on revs...
1960 # --follow with FILE behavior depends on revs...
1961 it = iter(revs)
1961 it = iter(revs)
1962 startrev = next(it)
1962 startrev = next(it)
1963 followdescendants = startrev < next(it, startrev)
1963 followdescendants = startrev < next(it, startrev)
1964
1964
1965 # branch and only_branch are really aliases and must be handled at
1965 # branch and only_branch are really aliases and must be handled at
1966 # the same time
1966 # the same time
1967 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1967 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1968 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1968 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1969 # pats/include/exclude are passed to match.match() directly in
1969 # pats/include/exclude are passed to match.match() directly in
1970 # _matchfiles() revset but walkchangerevs() builds its matcher with
1970 # _matchfiles() revset but walkchangerevs() builds its matcher with
1971 # scmutil.match(). The difference is input pats are globbed on
1971 # scmutil.match(). The difference is input pats are globbed on
1972 # platforms without shell expansion (windows).
1972 # platforms without shell expansion (windows).
1973 wctx = repo[None]
1973 wctx = repo[None]
1974 match, pats = scmutil.matchandpats(wctx, pats, opts)
1974 match, pats = scmutil.matchandpats(wctx, pats, opts)
1975 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1975 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1976 opts.get('removed'))
1976 opts.get('removed'))
1977 if not slowpath:
1977 if not slowpath:
1978 for f in match.files():
1978 for f in match.files():
1979 if follow and f not in wctx:
1979 if follow and f not in wctx:
1980 # If the file exists, it may be a directory, so let it
1980 # If the file exists, it may be a directory, so let it
1981 # take the slow path.
1981 # take the slow path.
1982 if os.path.exists(repo.wjoin(f)):
1982 if os.path.exists(repo.wjoin(f)):
1983 slowpath = True
1983 slowpath = True
1984 continue
1984 continue
1985 else:
1985 else:
1986 raise error.Abort(_('cannot follow file not in parent '
1986 raise error.Abort(_('cannot follow file not in parent '
1987 'revision: "%s"') % f)
1987 'revision: "%s"') % f)
1988 filelog = repo.file(f)
1988 filelog = repo.file(f)
1989 if not filelog:
1989 if not filelog:
1990 # A zero count may be a directory or deleted file, so
1990 # A zero count may be a directory or deleted file, so
1991 # try to find matching entries on the slow path.
1991 # try to find matching entries on the slow path.
1992 if follow:
1992 if follow:
1993 raise error.Abort(
1993 raise error.Abort(
1994 _('cannot follow nonexistent file: "%s"') % f)
1994 _('cannot follow nonexistent file: "%s"') % f)
1995 slowpath = True
1995 slowpath = True
1996
1996
1997 # We decided to fall back to the slowpath because at least one
1997 # We decided to fall back to the slowpath because at least one
1998 # of the paths was not a file. Check to see if at least one of them
1998 # of the paths was not a file. Check to see if at least one of them
1999 # existed in history - in that case, we'll continue down the
1999 # existed in history - in that case, we'll continue down the
2000 # slowpath; otherwise, we can turn off the slowpath
2000 # slowpath; otherwise, we can turn off the slowpath
2001 if slowpath:
2001 if slowpath:
2002 for path in match.files():
2002 for path in match.files():
2003 if path == '.' or path in repo.store:
2003 if path == '.' or path in repo.store:
2004 break
2004 break
2005 else:
2005 else:
2006 slowpath = False
2006 slowpath = False
2007
2007
2008 fpats = ('_patsfollow', '_patsfollowfirst')
2008 fpats = ('_patsfollow', '_patsfollowfirst')
2009 fnopats = (('_ancestors', '_fancestors'),
2009 fnopats = (('_ancestors', '_fancestors'),
2010 ('_descendants', '_fdescendants'))
2010 ('_descendants', '_fdescendants'))
2011 if slowpath:
2011 if slowpath:
2012 # See walkchangerevs() slow path.
2012 # See walkchangerevs() slow path.
2013 #
2013 #
2014 # pats/include/exclude cannot be represented as separate
2014 # pats/include/exclude cannot be represented as separate
2015 # revset expressions as their filtering logic applies at file
2015 # revset expressions as their filtering logic applies at file
2016 # level. For instance "-I a -X a" matches a revision touching
2016 # level. For instance "-I a -X a" matches a revision touching
2017 # "a" and "b" while "file(a) and not file(b)" does
2017 # "a" and "b" while "file(a) and not file(b)" does
2018 # not. Besides, filesets are evaluated against the working
2018 # not. Besides, filesets are evaluated against the working
2019 # directory.
2019 # directory.
2020 matchargs = ['r:', 'd:relpath']
2020 matchargs = ['r:', 'd:relpath']
2021 for p in pats:
2021 for p in pats:
2022 matchargs.append('p:' + p)
2022 matchargs.append('p:' + p)
2023 for p in opts.get('include', []):
2023 for p in opts.get('include', []):
2024 matchargs.append('i:' + p)
2024 matchargs.append('i:' + p)
2025 for p in opts.get('exclude', []):
2025 for p in opts.get('exclude', []):
2026 matchargs.append('x:' + p)
2026 matchargs.append('x:' + p)
2027 matchargs = ','.join(('%r' % p) for p in matchargs)
2027 matchargs = ','.join(('%r' % p) for p in matchargs)
2028 opts['_matchfiles'] = matchargs
2028 opts['_matchfiles'] = matchargs
2029 if follow:
2029 if follow:
2030 opts[fnopats[0][followfirst]] = '.'
2030 opts[fnopats[0][followfirst]] = '.'
2031 else:
2031 else:
2032 if follow:
2032 if follow:
2033 if pats:
2033 if pats:
2034 # follow() revset interprets its file argument as a
2034 # follow() revset interprets its file argument as a
2035 # manifest entry, so use match.files(), not pats.
2035 # manifest entry, so use match.files(), not pats.
2036 opts[fpats[followfirst]] = list(match.files())
2036 opts[fpats[followfirst]] = list(match.files())
2037 else:
2037 else:
2038 op = fnopats[followdescendants][followfirst]
2038 op = fnopats[followdescendants][followfirst]
2039 opts[op] = 'rev(%d)' % startrev
2039 opts[op] = 'rev(%d)' % startrev
2040 else:
2040 else:
2041 opts['_patslog'] = list(pats)
2041 opts['_patslog'] = list(pats)
2042
2042
2043 filematcher = None
2043 filematcher = None
2044 if opts.get('patch') or opts.get('stat'):
2044 if opts.get('patch') or opts.get('stat'):
2045 # When following files, track renames via a special matcher.
2045 # When following files, track renames via a special matcher.
2046 # If we're forced to take the slowpath it means we're following
2046 # If we're forced to take the slowpath it means we're following
2047 # at least one pattern/directory, so don't bother with rename tracking.
2047 # at least one pattern/directory, so don't bother with rename tracking.
2048 if follow and not match.always() and not slowpath:
2048 if follow and not match.always() and not slowpath:
2049 # _makefollowlogfilematcher expects its files argument to be
2049 # _makefollowlogfilematcher expects its files argument to be
2050 # relative to the repo root, so use match.files(), not pats.
2050 # relative to the repo root, so use match.files(), not pats.
2051 filematcher = _makefollowlogfilematcher(repo, match.files(),
2051 filematcher = _makefollowlogfilematcher(repo, match.files(),
2052 followfirst)
2052 followfirst)
2053 else:
2053 else:
2054 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2054 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2055 if filematcher is None:
2055 if filematcher is None:
2056 filematcher = lambda rev: match
2056 filematcher = lambda rev: match
2057
2057
2058 expr = []
2058 expr = []
2059 for op, val in sorted(opts.iteritems()):
2059 for op, val in sorted(opts.iteritems()):
2060 if not val:
2060 if not val:
2061 continue
2061 continue
2062 if op not in opt2revset:
2062 if op not in opt2revset:
2063 continue
2063 continue
2064 revop, andor = opt2revset[op]
2064 revop, andor = opt2revset[op]
2065 if '%(val)' not in revop:
2065 if '%(val)' not in revop:
2066 expr.append(revop)
2066 expr.append(revop)
2067 else:
2067 else:
2068 if not isinstance(val, list):
2068 if not isinstance(val, list):
2069 e = revop % {'val': val}
2069 e = revop % {'val': val}
2070 else:
2070 else:
2071 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2071 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2072 expr.append(e)
2072 expr.append(e)
2073
2073
2074 if expr:
2074 if expr:
2075 expr = '(' + ' and '.join(expr) + ')'
2075 expr = '(' + ' and '.join(expr) + ')'
2076 else:
2076 else:
2077 expr = None
2077 expr = None
2078 return expr, filematcher
2078 return expr, filematcher
2079
2079
2080 def _logrevs(repo, opts):
2080 def _logrevs(repo, opts):
2081 # Default --rev value depends on --follow but --follow behavior
2081 # Default --rev value depends on --follow but --follow behavior
2082 # depends on revisions resolved from --rev...
2082 # depends on revisions resolved from --rev...
2083 follow = opts.get('follow') or opts.get('follow_first')
2083 follow = opts.get('follow') or opts.get('follow_first')
2084 if opts.get('rev'):
2084 if opts.get('rev'):
2085 revs = scmutil.revrange(repo, opts['rev'])
2085 revs = scmutil.revrange(repo, opts['rev'])
2086 elif follow and repo.dirstate.p1() == nullid:
2086 elif follow and repo.dirstate.p1() == nullid:
2087 revs = smartset.baseset()
2087 revs = smartset.baseset()
2088 elif follow:
2088 elif follow:
2089 revs = repo.revs('reverse(:.)')
2089 revs = repo.revs('reverse(:.)')
2090 else:
2090 else:
2091 revs = smartset.spanset(repo)
2091 revs = smartset.spanset(repo)
2092 revs.reverse()
2092 revs.reverse()
2093 return revs
2093 return revs
2094
2094
2095 def getgraphlogrevs(repo, pats, opts):
2095 def getgraphlogrevs(repo, pats, opts):
2096 """Return (revs, expr, filematcher) where revs is an iterable of
2096 """Return (revs, expr, filematcher) where revs is an iterable of
2097 revision numbers, expr is a revset string built from log options
2097 revision numbers, expr is a revset string built from log options
2098 and file patterns or None, and used to filter 'revs'. If --stat or
2098 and file patterns or None, and used to filter 'revs'. If --stat or
2099 --patch are not passed filematcher is None. Otherwise it is a
2099 --patch are not passed filematcher is None. Otherwise it is a
2100 callable taking a revision number and returning a match objects
2100 callable taking a revision number and returning a match objects
2101 filtering the files to be detailed when displaying the revision.
2101 filtering the files to be detailed when displaying the revision.
2102 """
2102 """
2103 limit = loglimit(opts)
2103 limit = loglimit(opts)
2104 revs = _logrevs(repo, opts)
2104 revs = _logrevs(repo, opts)
2105 if not revs:
2105 if not revs:
2106 return smartset.baseset(), None, None
2106 return smartset.baseset(), None, None
2107 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2107 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2108 if opts.get('rev'):
2108 if opts.get('rev'):
2109 # User-specified revs might be unsorted, but don't sort before
2109 # User-specified revs might be unsorted, but don't sort before
2110 # _makelogrevset because it might depend on the order of revs
2110 # _makelogrevset because it might depend on the order of revs
2111 if not (revs.isdescending() or revs.istopo()):
2111 if not (revs.isdescending() or revs.istopo()):
2112 revs.sort(reverse=True)
2112 revs.sort(reverse=True)
2113 if expr:
2113 if expr:
2114 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2114 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2115 revs = matcher(repo, revs)
2115 revs = matcher(repo, revs)
2116 if limit is not None:
2116 if limit is not None:
2117 limitedrevs = []
2117 limitedrevs = []
2118 for idx, rev in enumerate(revs):
2118 for idx, rev in enumerate(revs):
2119 if idx >= limit:
2119 if idx >= limit:
2120 break
2120 break
2121 limitedrevs.append(rev)
2121 limitedrevs.append(rev)
2122 revs = smartset.baseset(limitedrevs)
2122 revs = smartset.baseset(limitedrevs)
2123
2123
2124 return revs, expr, filematcher
2124 return revs, expr, filematcher
2125
2125
2126 def getlogrevs(repo, pats, opts):
2126 def getlogrevs(repo, pats, opts):
2127 """Return (revs, expr, filematcher) where revs is an iterable of
2127 """Return (revs, expr, filematcher) where revs is an iterable of
2128 revision numbers, expr is a revset string built from log options
2128 revision numbers, expr is a revset string built from log options
2129 and file patterns or None, and used to filter 'revs'. If --stat or
2129 and file patterns or None, and used to filter 'revs'. If --stat or
2130 --patch are not passed filematcher is None. Otherwise it is a
2130 --patch are not passed filematcher is None. Otherwise it is a
2131 callable taking a revision number and returning a match objects
2131 callable taking a revision number and returning a match objects
2132 filtering the files to be detailed when displaying the revision.
2132 filtering the files to be detailed when displaying the revision.
2133 """
2133 """
2134 limit = loglimit(opts)
2134 limit = loglimit(opts)
2135 revs = _logrevs(repo, opts)
2135 revs = _logrevs(repo, opts)
2136 if not revs:
2136 if not revs:
2137 return smartset.baseset([]), None, None
2137 return smartset.baseset([]), None, None
2138 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2138 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2139 if expr:
2139 if expr:
2140 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2140 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2141 revs = matcher(repo, revs)
2141 revs = matcher(repo, revs)
2142 if limit is not None:
2142 if limit is not None:
2143 limitedrevs = []
2143 limitedrevs = []
2144 for idx, r in enumerate(revs):
2144 for idx, r in enumerate(revs):
2145 if limit <= idx:
2145 if limit <= idx:
2146 break
2146 break
2147 limitedrevs.append(r)
2147 limitedrevs.append(r)
2148 revs = smartset.baseset(limitedrevs)
2148 revs = smartset.baseset(limitedrevs)
2149
2149
2150 return revs, expr, filematcher
2150 return revs, expr, filematcher
2151
2151
2152 def _graphnodeformatter(ui, displayer):
2152 def _graphnodeformatter(ui, displayer):
2153 spec = ui.config('ui', 'graphnodetemplate')
2153 spec = ui.config('ui', 'graphnodetemplate')
2154 if not spec:
2154 if not spec:
2155 return templatekw.showgraphnode # fast path for "{graphnode}"
2155 return templatekw.showgraphnode # fast path for "{graphnode}"
2156
2156
2157 templ = formatter.gettemplater(ui, 'graphnode', spec)
2157 templ = formatter.gettemplater(ui, 'graphnode', spec)
2158 cache = {}
2158 cache = {}
2159 if isinstance(displayer, changeset_templater):
2159 if isinstance(displayer, changeset_templater):
2160 cache = displayer.cache # reuse cache of slow templates
2160 cache = displayer.cache # reuse cache of slow templates
2161 props = templatekw.keywords.copy()
2161 props = templatekw.keywords.copy()
2162 props['templ'] = templ
2162 props['templ'] = templ
2163 props['cache'] = cache
2163 props['cache'] = cache
2164 def formatnode(repo, ctx):
2164 def formatnode(repo, ctx):
2165 props['ctx'] = ctx
2165 props['ctx'] = ctx
2166 props['repo'] = repo
2166 props['repo'] = repo
2167 props['ui'] = repo.ui
2167 props['ui'] = repo.ui
2168 props['revcache'] = {}
2168 props['revcache'] = {}
2169 return templater.stringify(templ('graphnode', **props))
2169 return templater.stringify(templ('graphnode', **props))
2170 return formatnode
2170 return formatnode
2171
2171
2172 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2172 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2173 filematcher=None):
2173 filematcher=None):
2174 formatnode = _graphnodeformatter(ui, displayer)
2174 formatnode = _graphnodeformatter(ui, displayer)
2175 state = graphmod.asciistate()
2175 state = graphmod.asciistate()
2176 styles = state['styles']
2176 styles = state['styles']
2177
2177
2178 # only set graph styling if HGPLAIN is not set.
2178 # only set graph styling if HGPLAIN is not set.
2179 if ui.plain('graph'):
2179 if ui.plain('graph'):
2180 # set all edge styles to |, the default pre-3.8 behaviour
2180 # set all edge styles to |, the default pre-3.8 behaviour
2181 styles.update(dict.fromkeys(styles, '|'))
2181 styles.update(dict.fromkeys(styles, '|'))
2182 else:
2182 else:
2183 edgetypes = {
2183 edgetypes = {
2184 'parent': graphmod.PARENT,
2184 'parent': graphmod.PARENT,
2185 'grandparent': graphmod.GRANDPARENT,
2185 'grandparent': graphmod.GRANDPARENT,
2186 'missing': graphmod.MISSINGPARENT
2186 'missing': graphmod.MISSINGPARENT
2187 }
2187 }
2188 for name, key in edgetypes.items():
2188 for name, key in edgetypes.items():
2189 # experimental config: experimental.graphstyle.*
2189 # experimental config: experimental.graphstyle.*
2190 styles[key] = ui.config('experimental', 'graphstyle.%s' % name,
2190 styles[key] = ui.config('experimental', 'graphstyle.%s' % name,
2191 styles[key])
2191 styles[key])
2192 if not styles[key]:
2192 if not styles[key]:
2193 styles[key] = None
2193 styles[key] = None
2194
2194
2195 # experimental config: experimental.graphshorten
2195 # experimental config: experimental.graphshorten
2196 state['graphshorten'] = ui.configbool('experimental', 'graphshorten')
2196 state['graphshorten'] = ui.configbool('experimental', 'graphshorten')
2197
2197
2198 for rev, type, ctx, parents in dag:
2198 for rev, type, ctx, parents in dag:
2199 char = formatnode(repo, ctx)
2199 char = formatnode(repo, ctx)
2200 copies = None
2200 copies = None
2201 if getrenamed and ctx.rev():
2201 if getrenamed and ctx.rev():
2202 copies = []
2202 copies = []
2203 for fn in ctx.files():
2203 for fn in ctx.files():
2204 rename = getrenamed(fn, ctx.rev())
2204 rename = getrenamed(fn, ctx.rev())
2205 if rename:
2205 if rename:
2206 copies.append((fn, rename[0]))
2206 copies.append((fn, rename[0]))
2207 revmatchfn = None
2207 revmatchfn = None
2208 if filematcher is not None:
2208 if filematcher is not None:
2209 revmatchfn = filematcher(ctx.rev())
2209 revmatchfn = filematcher(ctx.rev())
2210 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2210 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2211 lines = displayer.hunk.pop(rev).split('\n')
2211 lines = displayer.hunk.pop(rev).split('\n')
2212 if not lines[-1]:
2212 if not lines[-1]:
2213 del lines[-1]
2213 del lines[-1]
2214 displayer.flush(ctx)
2214 displayer.flush(ctx)
2215 edges = edgefn(type, char, lines, state, rev, parents)
2215 edges = edgefn(type, char, lines, state, rev, parents)
2216 for type, char, lines, coldata in edges:
2216 for type, char, lines, coldata in edges:
2217 graphmod.ascii(ui, state, type, char, lines, coldata)
2217 graphmod.ascii(ui, state, type, char, lines, coldata)
2218 displayer.close()
2218 displayer.close()
2219
2219
2220 def graphlog(ui, repo, pats, opts):
2220 def graphlog(ui, repo, pats, opts):
2221 # Parameters are identical to log command ones
2221 # Parameters are identical to log command ones
2222 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2222 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2223 revdag = graphmod.dagwalker(repo, revs)
2223 revdag = graphmod.dagwalker(repo, revs)
2224
2224
2225 getrenamed = None
2225 getrenamed = None
2226 if opts.get('copies'):
2226 if opts.get('copies'):
2227 endrev = None
2227 endrev = None
2228 if opts.get('rev'):
2228 if opts.get('rev'):
2229 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2229 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2230 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2230 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2231
2231
2232 ui.pager('log')
2232 ui.pager('log')
2233 displayer = show_changeset(ui, repo, opts, buffered=True)
2233 displayer = show_changeset(ui, repo, opts, buffered=True)
2234 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2234 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2235 filematcher)
2235 filematcher)
2236
2236
2237 def checkunsupportedgraphflags(pats, opts):
2237 def checkunsupportedgraphflags(pats, opts):
2238 for op in ["newest_first"]:
2238 for op in ["newest_first"]:
2239 if op in opts and opts[op]:
2239 if op in opts and opts[op]:
2240 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2240 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2241 % op.replace("_", "-"))
2241 % op.replace("_", "-"))
2242
2242
2243 def graphrevs(repo, nodes, opts):
2243 def graphrevs(repo, nodes, opts):
2244 limit = loglimit(opts)
2244 limit = loglimit(opts)
2245 nodes.reverse()
2245 nodes.reverse()
2246 if limit is not None:
2246 if limit is not None:
2247 nodes = nodes[:limit]
2247 nodes = nodes[:limit]
2248 return graphmod.nodes(repo, nodes)
2248 return graphmod.nodes(repo, nodes)
2249
2249
2250 def add(ui, repo, match, prefix, explicitonly, **opts):
2250 def add(ui, repo, match, prefix, explicitonly, **opts):
2251 join = lambda f: os.path.join(prefix, f)
2251 join = lambda f: os.path.join(prefix, f)
2252 bad = []
2252 bad = []
2253
2253
2254 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2254 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2255 names = []
2255 names = []
2256 wctx = repo[None]
2256 wctx = repo[None]
2257 cca = None
2257 cca = None
2258 abort, warn = scmutil.checkportabilityalert(ui)
2258 abort, warn = scmutil.checkportabilityalert(ui)
2259 if abort or warn:
2259 if abort or warn:
2260 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2260 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2261
2261
2262 badmatch = matchmod.badmatch(match, badfn)
2262 badmatch = matchmod.badmatch(match, badfn)
2263 dirstate = repo.dirstate
2263 dirstate = repo.dirstate
2264 # We don't want to just call wctx.walk here, since it would return a lot of
2264 # We don't want to just call wctx.walk here, since it would return a lot of
2265 # clean files, which we aren't interested in and takes time.
2265 # clean files, which we aren't interested in and takes time.
2266 for f in sorted(dirstate.walk(badmatch, sorted(wctx.substate),
2266 for f in sorted(dirstate.walk(badmatch, sorted(wctx.substate),
2267 True, False, full=False)):
2267 True, False, full=False)):
2268 exact = match.exact(f)
2268 exact = match.exact(f)
2269 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2269 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2270 if cca:
2270 if cca:
2271 cca(f)
2271 cca(f)
2272 names.append(f)
2272 names.append(f)
2273 if ui.verbose or not exact:
2273 if ui.verbose or not exact:
2274 ui.status(_('adding %s\n') % match.rel(f))
2274 ui.status(_('adding %s\n') % match.rel(f))
2275
2275
2276 for subpath in sorted(wctx.substate):
2276 for subpath in sorted(wctx.substate):
2277 sub = wctx.sub(subpath)
2277 sub = wctx.sub(subpath)
2278 try:
2278 try:
2279 submatch = matchmod.subdirmatcher(subpath, match)
2279 submatch = matchmod.subdirmatcher(subpath, match)
2280 if opts.get('subrepos'):
2280 if opts.get('subrepos'):
2281 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2281 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2282 else:
2282 else:
2283 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2283 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2284 except error.LookupError:
2284 except error.LookupError:
2285 ui.status(_("skipping missing subrepository: %s\n")
2285 ui.status(_("skipping missing subrepository: %s\n")
2286 % join(subpath))
2286 % join(subpath))
2287
2287
2288 if not opts.get('dry_run'):
2288 if not opts.get('dry_run'):
2289 rejected = wctx.add(names, prefix)
2289 rejected = wctx.add(names, prefix)
2290 bad.extend(f for f in rejected if f in match.files())
2290 bad.extend(f for f in rejected if f in match.files())
2291 return bad
2291 return bad
2292
2292
2293 def addwebdirpath(repo, serverpath, webconf):
2294 webconf[serverpath] = repo.root
2295 repo.ui.debug('adding %s = %s\n' % (serverpath, repo.root))
2296
2297 for r in repo.revs('filelog("path:.hgsub")'):
2298 ctx = repo[r]
2299 for subpath in ctx.substate:
2300 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2301
2293 def forget(ui, repo, match, prefix, explicitonly):
2302 def forget(ui, repo, match, prefix, explicitonly):
2294 join = lambda f: os.path.join(prefix, f)
2303 join = lambda f: os.path.join(prefix, f)
2295 bad = []
2304 bad = []
2296 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2305 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2297 wctx = repo[None]
2306 wctx = repo[None]
2298 forgot = []
2307 forgot = []
2299
2308
2300 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2309 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2301 forget = sorted(s[0] + s[1] + s[3] + s[6])
2310 forget = sorted(s[0] + s[1] + s[3] + s[6])
2302 if explicitonly:
2311 if explicitonly:
2303 forget = [f for f in forget if match.exact(f)]
2312 forget = [f for f in forget if match.exact(f)]
2304
2313
2305 for subpath in sorted(wctx.substate):
2314 for subpath in sorted(wctx.substate):
2306 sub = wctx.sub(subpath)
2315 sub = wctx.sub(subpath)
2307 try:
2316 try:
2308 submatch = matchmod.subdirmatcher(subpath, match)
2317 submatch = matchmod.subdirmatcher(subpath, match)
2309 subbad, subforgot = sub.forget(submatch, prefix)
2318 subbad, subforgot = sub.forget(submatch, prefix)
2310 bad.extend([subpath + '/' + f for f in subbad])
2319 bad.extend([subpath + '/' + f for f in subbad])
2311 forgot.extend([subpath + '/' + f for f in subforgot])
2320 forgot.extend([subpath + '/' + f for f in subforgot])
2312 except error.LookupError:
2321 except error.LookupError:
2313 ui.status(_("skipping missing subrepository: %s\n")
2322 ui.status(_("skipping missing subrepository: %s\n")
2314 % join(subpath))
2323 % join(subpath))
2315
2324
2316 if not explicitonly:
2325 if not explicitonly:
2317 for f in match.files():
2326 for f in match.files():
2318 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2327 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2319 if f not in forgot:
2328 if f not in forgot:
2320 if repo.wvfs.exists(f):
2329 if repo.wvfs.exists(f):
2321 # Don't complain if the exact case match wasn't given.
2330 # Don't complain if the exact case match wasn't given.
2322 # But don't do this until after checking 'forgot', so
2331 # But don't do this until after checking 'forgot', so
2323 # that subrepo files aren't normalized, and this op is
2332 # that subrepo files aren't normalized, and this op is
2324 # purely from data cached by the status walk above.
2333 # purely from data cached by the status walk above.
2325 if repo.dirstate.normalize(f) in repo.dirstate:
2334 if repo.dirstate.normalize(f) in repo.dirstate:
2326 continue
2335 continue
2327 ui.warn(_('not removing %s: '
2336 ui.warn(_('not removing %s: '
2328 'file is already untracked\n')
2337 'file is already untracked\n')
2329 % match.rel(f))
2338 % match.rel(f))
2330 bad.append(f)
2339 bad.append(f)
2331
2340
2332 for f in forget:
2341 for f in forget:
2333 if ui.verbose or not match.exact(f):
2342 if ui.verbose or not match.exact(f):
2334 ui.status(_('removing %s\n') % match.rel(f))
2343 ui.status(_('removing %s\n') % match.rel(f))
2335
2344
2336 rejected = wctx.forget(forget, prefix)
2345 rejected = wctx.forget(forget, prefix)
2337 bad.extend(f for f in rejected if f in match.files())
2346 bad.extend(f for f in rejected if f in match.files())
2338 forgot.extend(f for f in forget if f not in rejected)
2347 forgot.extend(f for f in forget if f not in rejected)
2339 return bad, forgot
2348 return bad, forgot
2340
2349
2341 def files(ui, ctx, m, fm, fmt, subrepos):
2350 def files(ui, ctx, m, fm, fmt, subrepos):
2342 rev = ctx.rev()
2351 rev = ctx.rev()
2343 ret = 1
2352 ret = 1
2344 ds = ctx.repo().dirstate
2353 ds = ctx.repo().dirstate
2345
2354
2346 for f in ctx.matches(m):
2355 for f in ctx.matches(m):
2347 if rev is None and ds[f] == 'r':
2356 if rev is None and ds[f] == 'r':
2348 continue
2357 continue
2349 fm.startitem()
2358 fm.startitem()
2350 if ui.verbose:
2359 if ui.verbose:
2351 fc = ctx[f]
2360 fc = ctx[f]
2352 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2361 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2353 fm.data(abspath=f)
2362 fm.data(abspath=f)
2354 fm.write('path', fmt, m.rel(f))
2363 fm.write('path', fmt, m.rel(f))
2355 ret = 0
2364 ret = 0
2356
2365
2357 for subpath in sorted(ctx.substate):
2366 for subpath in sorted(ctx.substate):
2358 submatch = matchmod.subdirmatcher(subpath, m)
2367 submatch = matchmod.subdirmatcher(subpath, m)
2359 if (subrepos or m.exact(subpath) or any(submatch.files())):
2368 if (subrepos or m.exact(subpath) or any(submatch.files())):
2360 sub = ctx.sub(subpath)
2369 sub = ctx.sub(subpath)
2361 try:
2370 try:
2362 recurse = m.exact(subpath) or subrepos
2371 recurse = m.exact(subpath) or subrepos
2363 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2372 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2364 ret = 0
2373 ret = 0
2365 except error.LookupError:
2374 except error.LookupError:
2366 ui.status(_("skipping missing subrepository: %s\n")
2375 ui.status(_("skipping missing subrepository: %s\n")
2367 % m.abs(subpath))
2376 % m.abs(subpath))
2368
2377
2369 return ret
2378 return ret
2370
2379
2371 def remove(ui, repo, m, prefix, after, force, subrepos, warnings=None):
2380 def remove(ui, repo, m, prefix, after, force, subrepos, warnings=None):
2372 join = lambda f: os.path.join(prefix, f)
2381 join = lambda f: os.path.join(prefix, f)
2373 ret = 0
2382 ret = 0
2374 s = repo.status(match=m, clean=True)
2383 s = repo.status(match=m, clean=True)
2375 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2384 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2376
2385
2377 wctx = repo[None]
2386 wctx = repo[None]
2378
2387
2379 if warnings is None:
2388 if warnings is None:
2380 warnings = []
2389 warnings = []
2381 warn = True
2390 warn = True
2382 else:
2391 else:
2383 warn = False
2392 warn = False
2384
2393
2385 subs = sorted(wctx.substate)
2394 subs = sorted(wctx.substate)
2386 total = len(subs)
2395 total = len(subs)
2387 count = 0
2396 count = 0
2388 for subpath in subs:
2397 for subpath in subs:
2389 count += 1
2398 count += 1
2390 submatch = matchmod.subdirmatcher(subpath, m)
2399 submatch = matchmod.subdirmatcher(subpath, m)
2391 if subrepos or m.exact(subpath) or any(submatch.files()):
2400 if subrepos or m.exact(subpath) or any(submatch.files()):
2392 ui.progress(_('searching'), count, total=total, unit=_('subrepos'))
2401 ui.progress(_('searching'), count, total=total, unit=_('subrepos'))
2393 sub = wctx.sub(subpath)
2402 sub = wctx.sub(subpath)
2394 try:
2403 try:
2395 if sub.removefiles(submatch, prefix, after, force, subrepos,
2404 if sub.removefiles(submatch, prefix, after, force, subrepos,
2396 warnings):
2405 warnings):
2397 ret = 1
2406 ret = 1
2398 except error.LookupError:
2407 except error.LookupError:
2399 warnings.append(_("skipping missing subrepository: %s\n")
2408 warnings.append(_("skipping missing subrepository: %s\n")
2400 % join(subpath))
2409 % join(subpath))
2401 ui.progress(_('searching'), None)
2410 ui.progress(_('searching'), None)
2402
2411
2403 # warn about failure to delete explicit files/dirs
2412 # warn about failure to delete explicit files/dirs
2404 deleteddirs = util.dirs(deleted)
2413 deleteddirs = util.dirs(deleted)
2405 files = m.files()
2414 files = m.files()
2406 total = len(files)
2415 total = len(files)
2407 count = 0
2416 count = 0
2408 for f in files:
2417 for f in files:
2409 def insubrepo():
2418 def insubrepo():
2410 for subpath in wctx.substate:
2419 for subpath in wctx.substate:
2411 if f.startswith(subpath + '/'):
2420 if f.startswith(subpath + '/'):
2412 return True
2421 return True
2413 return False
2422 return False
2414
2423
2415 count += 1
2424 count += 1
2416 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2425 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2417 isdir = f in deleteddirs or wctx.hasdir(f)
2426 isdir = f in deleteddirs or wctx.hasdir(f)
2418 if (f in repo.dirstate or isdir or f == '.'
2427 if (f in repo.dirstate or isdir or f == '.'
2419 or insubrepo() or f in subs):
2428 or insubrepo() or f in subs):
2420 continue
2429 continue
2421
2430
2422 if repo.wvfs.exists(f):
2431 if repo.wvfs.exists(f):
2423 if repo.wvfs.isdir(f):
2432 if repo.wvfs.isdir(f):
2424 warnings.append(_('not removing %s: no tracked files\n')
2433 warnings.append(_('not removing %s: no tracked files\n')
2425 % m.rel(f))
2434 % m.rel(f))
2426 else:
2435 else:
2427 warnings.append(_('not removing %s: file is untracked\n')
2436 warnings.append(_('not removing %s: file is untracked\n')
2428 % m.rel(f))
2437 % m.rel(f))
2429 # missing files will generate a warning elsewhere
2438 # missing files will generate a warning elsewhere
2430 ret = 1
2439 ret = 1
2431 ui.progress(_('deleting'), None)
2440 ui.progress(_('deleting'), None)
2432
2441
2433 if force:
2442 if force:
2434 list = modified + deleted + clean + added
2443 list = modified + deleted + clean + added
2435 elif after:
2444 elif after:
2436 list = deleted
2445 list = deleted
2437 remaining = modified + added + clean
2446 remaining = modified + added + clean
2438 total = len(remaining)
2447 total = len(remaining)
2439 count = 0
2448 count = 0
2440 for f in remaining:
2449 for f in remaining:
2441 count += 1
2450 count += 1
2442 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2451 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2443 warnings.append(_('not removing %s: file still exists\n')
2452 warnings.append(_('not removing %s: file still exists\n')
2444 % m.rel(f))
2453 % m.rel(f))
2445 ret = 1
2454 ret = 1
2446 ui.progress(_('skipping'), None)
2455 ui.progress(_('skipping'), None)
2447 else:
2456 else:
2448 list = deleted + clean
2457 list = deleted + clean
2449 total = len(modified) + len(added)
2458 total = len(modified) + len(added)
2450 count = 0
2459 count = 0
2451 for f in modified:
2460 for f in modified:
2452 count += 1
2461 count += 1
2453 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2462 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2454 warnings.append(_('not removing %s: file is modified (use -f'
2463 warnings.append(_('not removing %s: file is modified (use -f'
2455 ' to force removal)\n') % m.rel(f))
2464 ' to force removal)\n') % m.rel(f))
2456 ret = 1
2465 ret = 1
2457 for f in added:
2466 for f in added:
2458 count += 1
2467 count += 1
2459 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2468 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2460 warnings.append(_("not removing %s: file has been marked for add"
2469 warnings.append(_("not removing %s: file has been marked for add"
2461 " (use 'hg forget' to undo add)\n") % m.rel(f))
2470 " (use 'hg forget' to undo add)\n") % m.rel(f))
2462 ret = 1
2471 ret = 1
2463 ui.progress(_('skipping'), None)
2472 ui.progress(_('skipping'), None)
2464
2473
2465 list = sorted(list)
2474 list = sorted(list)
2466 total = len(list)
2475 total = len(list)
2467 count = 0
2476 count = 0
2468 for f in list:
2477 for f in list:
2469 count += 1
2478 count += 1
2470 if ui.verbose or not m.exact(f):
2479 if ui.verbose or not m.exact(f):
2471 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2480 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2472 ui.status(_('removing %s\n') % m.rel(f))
2481 ui.status(_('removing %s\n') % m.rel(f))
2473 ui.progress(_('deleting'), None)
2482 ui.progress(_('deleting'), None)
2474
2483
2475 with repo.wlock():
2484 with repo.wlock():
2476 if not after:
2485 if not after:
2477 for f in list:
2486 for f in list:
2478 if f in added:
2487 if f in added:
2479 continue # we never unlink added files on remove
2488 continue # we never unlink added files on remove
2480 repo.wvfs.unlinkpath(f, ignoremissing=True)
2489 repo.wvfs.unlinkpath(f, ignoremissing=True)
2481 repo[None].forget(list)
2490 repo[None].forget(list)
2482
2491
2483 if warn:
2492 if warn:
2484 for warning in warnings:
2493 for warning in warnings:
2485 ui.warn(warning)
2494 ui.warn(warning)
2486
2495
2487 return ret
2496 return ret
2488
2497
2489 def cat(ui, repo, ctx, matcher, prefix, **opts):
2498 def cat(ui, repo, ctx, matcher, prefix, **opts):
2490 err = 1
2499 err = 1
2491
2500
2492 def write(path):
2501 def write(path):
2493 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2502 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2494 pathname=os.path.join(prefix, path))
2503 pathname=os.path.join(prefix, path))
2495 data = ctx[path].data()
2504 data = ctx[path].data()
2496 if opts.get('decode'):
2505 if opts.get('decode'):
2497 data = repo.wwritedata(path, data)
2506 data = repo.wwritedata(path, data)
2498 fp.write(data)
2507 fp.write(data)
2499 fp.close()
2508 fp.close()
2500
2509
2501 # Automation often uses hg cat on single files, so special case it
2510 # Automation often uses hg cat on single files, so special case it
2502 # for performance to avoid the cost of parsing the manifest.
2511 # for performance to avoid the cost of parsing the manifest.
2503 if len(matcher.files()) == 1 and not matcher.anypats():
2512 if len(matcher.files()) == 1 and not matcher.anypats():
2504 file = matcher.files()[0]
2513 file = matcher.files()[0]
2505 mfl = repo.manifestlog
2514 mfl = repo.manifestlog
2506 mfnode = ctx.manifestnode()
2515 mfnode = ctx.manifestnode()
2507 try:
2516 try:
2508 if mfnode and mfl[mfnode].find(file)[0]:
2517 if mfnode and mfl[mfnode].find(file)[0]:
2509 write(file)
2518 write(file)
2510 return 0
2519 return 0
2511 except KeyError:
2520 except KeyError:
2512 pass
2521 pass
2513
2522
2514 for abs in ctx.walk(matcher):
2523 for abs in ctx.walk(matcher):
2515 write(abs)
2524 write(abs)
2516 err = 0
2525 err = 0
2517
2526
2518 for subpath in sorted(ctx.substate):
2527 for subpath in sorted(ctx.substate):
2519 sub = ctx.sub(subpath)
2528 sub = ctx.sub(subpath)
2520 try:
2529 try:
2521 submatch = matchmod.subdirmatcher(subpath, matcher)
2530 submatch = matchmod.subdirmatcher(subpath, matcher)
2522
2531
2523 if not sub.cat(submatch, os.path.join(prefix, sub._path),
2532 if not sub.cat(submatch, os.path.join(prefix, sub._path),
2524 **opts):
2533 **opts):
2525 err = 0
2534 err = 0
2526 except error.RepoLookupError:
2535 except error.RepoLookupError:
2527 ui.status(_("skipping missing subrepository: %s\n")
2536 ui.status(_("skipping missing subrepository: %s\n")
2528 % os.path.join(prefix, subpath))
2537 % os.path.join(prefix, subpath))
2529
2538
2530 return err
2539 return err
2531
2540
2532 def commit(ui, repo, commitfunc, pats, opts):
2541 def commit(ui, repo, commitfunc, pats, opts):
2533 '''commit the specified files or all outstanding changes'''
2542 '''commit the specified files or all outstanding changes'''
2534 date = opts.get('date')
2543 date = opts.get('date')
2535 if date:
2544 if date:
2536 opts['date'] = util.parsedate(date)
2545 opts['date'] = util.parsedate(date)
2537 message = logmessage(ui, opts)
2546 message = logmessage(ui, opts)
2538 matcher = scmutil.match(repo[None], pats, opts)
2547 matcher = scmutil.match(repo[None], pats, opts)
2539
2548
2540 # extract addremove carefully -- this function can be called from a command
2549 # extract addremove carefully -- this function can be called from a command
2541 # that doesn't support addremove
2550 # that doesn't support addremove
2542 if opts.get('addremove'):
2551 if opts.get('addremove'):
2543 if scmutil.addremove(repo, matcher, "", opts) != 0:
2552 if scmutil.addremove(repo, matcher, "", opts) != 0:
2544 raise error.Abort(
2553 raise error.Abort(
2545 _("failed to mark all new/missing files as added/removed"))
2554 _("failed to mark all new/missing files as added/removed"))
2546
2555
2547 return commitfunc(ui, repo, message, matcher, opts)
2556 return commitfunc(ui, repo, message, matcher, opts)
2548
2557
2549 def samefile(f, ctx1, ctx2):
2558 def samefile(f, ctx1, ctx2):
2550 if f in ctx1.manifest():
2559 if f in ctx1.manifest():
2551 a = ctx1.filectx(f)
2560 a = ctx1.filectx(f)
2552 if f in ctx2.manifest():
2561 if f in ctx2.manifest():
2553 b = ctx2.filectx(f)
2562 b = ctx2.filectx(f)
2554 return (not a.cmp(b)
2563 return (not a.cmp(b)
2555 and a.flags() == b.flags())
2564 and a.flags() == b.flags())
2556 else:
2565 else:
2557 return False
2566 return False
2558 else:
2567 else:
2559 return f not in ctx2.manifest()
2568 return f not in ctx2.manifest()
2560
2569
2561 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2570 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2562 # avoid cycle context -> subrepo -> cmdutil
2571 # avoid cycle context -> subrepo -> cmdutil
2563 from . import context
2572 from . import context
2564
2573
2565 # amend will reuse the existing user if not specified, but the obsolete
2574 # amend will reuse the existing user if not specified, but the obsolete
2566 # marker creation requires that the current user's name is specified.
2575 # marker creation requires that the current user's name is specified.
2567 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2576 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2568 ui.username() # raise exception if username not set
2577 ui.username() # raise exception if username not set
2569
2578
2570 ui.note(_('amending changeset %s\n') % old)
2579 ui.note(_('amending changeset %s\n') % old)
2571 base = old.p1()
2580 base = old.p1()
2572 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2581 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2573
2582
2574 wlock = lock = newid = None
2583 wlock = lock = newid = None
2575 try:
2584 try:
2576 wlock = repo.wlock()
2585 wlock = repo.wlock()
2577 lock = repo.lock()
2586 lock = repo.lock()
2578 with repo.transaction('amend') as tr:
2587 with repo.transaction('amend') as tr:
2579 # See if we got a message from -m or -l, if not, open the editor
2588 # See if we got a message from -m or -l, if not, open the editor
2580 # with the message of the changeset to amend
2589 # with the message of the changeset to amend
2581 message = logmessage(ui, opts)
2590 message = logmessage(ui, opts)
2582 # ensure logfile does not conflict with later enforcement of the
2591 # ensure logfile does not conflict with later enforcement of the
2583 # message. potential logfile content has been processed by
2592 # message. potential logfile content has been processed by
2584 # `logmessage` anyway.
2593 # `logmessage` anyway.
2585 opts.pop('logfile')
2594 opts.pop('logfile')
2586 # First, do a regular commit to record all changes in the working
2595 # First, do a regular commit to record all changes in the working
2587 # directory (if there are any)
2596 # directory (if there are any)
2588 ui.callhooks = False
2597 ui.callhooks = False
2589 activebookmark = repo._bookmarks.active
2598 activebookmark = repo._bookmarks.active
2590 try:
2599 try:
2591 repo._bookmarks.active = None
2600 repo._bookmarks.active = None
2592 opts['message'] = 'temporary amend commit for %s' % old
2601 opts['message'] = 'temporary amend commit for %s' % old
2593 node = commit(ui, repo, commitfunc, pats, opts)
2602 node = commit(ui, repo, commitfunc, pats, opts)
2594 finally:
2603 finally:
2595 repo._bookmarks.active = activebookmark
2604 repo._bookmarks.active = activebookmark
2596 repo._bookmarks.recordchange(tr)
2605 repo._bookmarks.recordchange(tr)
2597 ui.callhooks = True
2606 ui.callhooks = True
2598 ctx = repo[node]
2607 ctx = repo[node]
2599
2608
2600 # Participating changesets:
2609 # Participating changesets:
2601 #
2610 #
2602 # node/ctx o - new (intermediate) commit that contains changes
2611 # node/ctx o - new (intermediate) commit that contains changes
2603 # | from working dir to go into amending commit
2612 # | from working dir to go into amending commit
2604 # | (or a workingctx if there were no changes)
2613 # | (or a workingctx if there were no changes)
2605 # |
2614 # |
2606 # old o - changeset to amend
2615 # old o - changeset to amend
2607 # |
2616 # |
2608 # base o - parent of amending changeset
2617 # base o - parent of amending changeset
2609
2618
2610 # Update extra dict from amended commit (e.g. to preserve graft
2619 # Update extra dict from amended commit (e.g. to preserve graft
2611 # source)
2620 # source)
2612 extra.update(old.extra())
2621 extra.update(old.extra())
2613
2622
2614 # Also update it from the intermediate commit or from the wctx
2623 # Also update it from the intermediate commit or from the wctx
2615 extra.update(ctx.extra())
2624 extra.update(ctx.extra())
2616
2625
2617 if len(old.parents()) > 1:
2626 if len(old.parents()) > 1:
2618 # ctx.files() isn't reliable for merges, so fall back to the
2627 # ctx.files() isn't reliable for merges, so fall back to the
2619 # slower repo.status() method
2628 # slower repo.status() method
2620 files = set([fn for st in repo.status(base, old)[:3]
2629 files = set([fn for st in repo.status(base, old)[:3]
2621 for fn in st])
2630 for fn in st])
2622 else:
2631 else:
2623 files = set(old.files())
2632 files = set(old.files())
2624
2633
2625 # Second, we use either the commit we just did, or if there were no
2634 # Second, we use either the commit we just did, or if there were no
2626 # changes the parent of the working directory as the version of the
2635 # changes the parent of the working directory as the version of the
2627 # files in the final amend commit
2636 # files in the final amend commit
2628 if node:
2637 if node:
2629 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2638 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2630
2639
2631 user = ctx.user()
2640 user = ctx.user()
2632 date = ctx.date()
2641 date = ctx.date()
2633 # Recompute copies (avoid recording a -> b -> a)
2642 # Recompute copies (avoid recording a -> b -> a)
2634 copied = copies.pathcopies(base, ctx)
2643 copied = copies.pathcopies(base, ctx)
2635 if old.p2:
2644 if old.p2:
2636 copied.update(copies.pathcopies(old.p2(), ctx))
2645 copied.update(copies.pathcopies(old.p2(), ctx))
2637
2646
2638 # Prune files which were reverted by the updates: if old
2647 # Prune files which were reverted by the updates: if old
2639 # introduced file X and our intermediate commit, node,
2648 # introduced file X and our intermediate commit, node,
2640 # renamed that file, then those two files are the same and
2649 # renamed that file, then those two files are the same and
2641 # we can discard X from our list of files. Likewise if X
2650 # we can discard X from our list of files. Likewise if X
2642 # was deleted, it's no longer relevant
2651 # was deleted, it's no longer relevant
2643 files.update(ctx.files())
2652 files.update(ctx.files())
2644 files = [f for f in files if not samefile(f, ctx, base)]
2653 files = [f for f in files if not samefile(f, ctx, base)]
2645
2654
2646 def filectxfn(repo, ctx_, path):
2655 def filectxfn(repo, ctx_, path):
2647 try:
2656 try:
2648 fctx = ctx[path]
2657 fctx = ctx[path]
2649 flags = fctx.flags()
2658 flags = fctx.flags()
2650 mctx = context.memfilectx(repo,
2659 mctx = context.memfilectx(repo,
2651 fctx.path(), fctx.data(),
2660 fctx.path(), fctx.data(),
2652 islink='l' in flags,
2661 islink='l' in flags,
2653 isexec='x' in flags,
2662 isexec='x' in flags,
2654 copied=copied.get(path))
2663 copied=copied.get(path))
2655 return mctx
2664 return mctx
2656 except KeyError:
2665 except KeyError:
2657 return None
2666 return None
2658 else:
2667 else:
2659 ui.note(_('copying changeset %s to %s\n') % (old, base))
2668 ui.note(_('copying changeset %s to %s\n') % (old, base))
2660
2669
2661 # Use version of files as in the old cset
2670 # Use version of files as in the old cset
2662 def filectxfn(repo, ctx_, path):
2671 def filectxfn(repo, ctx_, path):
2663 try:
2672 try:
2664 return old.filectx(path)
2673 return old.filectx(path)
2665 except KeyError:
2674 except KeyError:
2666 return None
2675 return None
2667
2676
2668 user = opts.get('user') or old.user()
2677 user = opts.get('user') or old.user()
2669 date = opts.get('date') or old.date()
2678 date = opts.get('date') or old.date()
2670 editform = mergeeditform(old, 'commit.amend')
2679 editform = mergeeditform(old, 'commit.amend')
2671 editor = getcommiteditor(editform=editform, **opts)
2680 editor = getcommiteditor(editform=editform, **opts)
2672 if not message:
2681 if not message:
2673 editor = getcommiteditor(edit=True, editform=editform)
2682 editor = getcommiteditor(edit=True, editform=editform)
2674 message = old.description()
2683 message = old.description()
2675
2684
2676 pureextra = extra.copy()
2685 pureextra = extra.copy()
2677 extra['amend_source'] = old.hex()
2686 extra['amend_source'] = old.hex()
2678
2687
2679 new = context.memctx(repo,
2688 new = context.memctx(repo,
2680 parents=[base.node(), old.p2().node()],
2689 parents=[base.node(), old.p2().node()],
2681 text=message,
2690 text=message,
2682 files=files,
2691 files=files,
2683 filectxfn=filectxfn,
2692 filectxfn=filectxfn,
2684 user=user,
2693 user=user,
2685 date=date,
2694 date=date,
2686 extra=extra,
2695 extra=extra,
2687 editor=editor)
2696 editor=editor)
2688
2697
2689 newdesc = changelog.stripdesc(new.description())
2698 newdesc = changelog.stripdesc(new.description())
2690 if ((not node)
2699 if ((not node)
2691 and newdesc == old.description()
2700 and newdesc == old.description()
2692 and user == old.user()
2701 and user == old.user()
2693 and date == old.date()
2702 and date == old.date()
2694 and pureextra == old.extra()):
2703 and pureextra == old.extra()):
2695 # nothing changed. continuing here would create a new node
2704 # nothing changed. continuing here would create a new node
2696 # anyway because of the amend_source noise.
2705 # anyway because of the amend_source noise.
2697 #
2706 #
2698 # This not what we expect from amend.
2707 # This not what we expect from amend.
2699 return old.node()
2708 return old.node()
2700
2709
2701 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2710 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2702 try:
2711 try:
2703 if opts.get('secret'):
2712 if opts.get('secret'):
2704 commitphase = 'secret'
2713 commitphase = 'secret'
2705 else:
2714 else:
2706 commitphase = old.phase()
2715 commitphase = old.phase()
2707 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2716 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2708 newid = repo.commitctx(new)
2717 newid = repo.commitctx(new)
2709 finally:
2718 finally:
2710 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2719 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2711 if newid != old.node():
2720 if newid != old.node():
2712 # Reroute the working copy parent to the new changeset
2721 # Reroute the working copy parent to the new changeset
2713 repo.setparents(newid, nullid)
2722 repo.setparents(newid, nullid)
2714
2723
2715 # Move bookmarks from old parent to amend commit
2724 # Move bookmarks from old parent to amend commit
2716 bms = repo.nodebookmarks(old.node())
2725 bms = repo.nodebookmarks(old.node())
2717 if bms:
2726 if bms:
2718 marks = repo._bookmarks
2727 marks = repo._bookmarks
2719 for bm in bms:
2728 for bm in bms:
2720 ui.debug('moving bookmarks %r from %s to %s\n' %
2729 ui.debug('moving bookmarks %r from %s to %s\n' %
2721 (marks, old.hex(), hex(newid)))
2730 (marks, old.hex(), hex(newid)))
2722 marks[bm] = newid
2731 marks[bm] = newid
2723 marks.recordchange(tr)
2732 marks.recordchange(tr)
2724 #commit the whole amend process
2733 #commit the whole amend process
2725 if createmarkers:
2734 if createmarkers:
2726 # mark the new changeset as successor of the rewritten one
2735 # mark the new changeset as successor of the rewritten one
2727 new = repo[newid]
2736 new = repo[newid]
2728 obs = [(old, (new,))]
2737 obs = [(old, (new,))]
2729 if node:
2738 if node:
2730 obs.append((ctx, ()))
2739 obs.append((ctx, ()))
2731
2740
2732 obsolete.createmarkers(repo, obs)
2741 obsolete.createmarkers(repo, obs)
2733 if not createmarkers and newid != old.node():
2742 if not createmarkers and newid != old.node():
2734 # Strip the intermediate commit (if there was one) and the amended
2743 # Strip the intermediate commit (if there was one) and the amended
2735 # commit
2744 # commit
2736 if node:
2745 if node:
2737 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2746 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2738 ui.note(_('stripping amended changeset %s\n') % old)
2747 ui.note(_('stripping amended changeset %s\n') % old)
2739 repair.strip(ui, repo, old.node(), topic='amend-backup')
2748 repair.strip(ui, repo, old.node(), topic='amend-backup')
2740 finally:
2749 finally:
2741 lockmod.release(lock, wlock)
2750 lockmod.release(lock, wlock)
2742 return newid
2751 return newid
2743
2752
2744 def commiteditor(repo, ctx, subs, editform=''):
2753 def commiteditor(repo, ctx, subs, editform=''):
2745 if ctx.description():
2754 if ctx.description():
2746 return ctx.description()
2755 return ctx.description()
2747 return commitforceeditor(repo, ctx, subs, editform=editform,
2756 return commitforceeditor(repo, ctx, subs, editform=editform,
2748 unchangedmessagedetection=True)
2757 unchangedmessagedetection=True)
2749
2758
2750 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2759 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2751 editform='', unchangedmessagedetection=False):
2760 editform='', unchangedmessagedetection=False):
2752 if not extramsg:
2761 if not extramsg:
2753 extramsg = _("Leave message empty to abort commit.")
2762 extramsg = _("Leave message empty to abort commit.")
2754
2763
2755 forms = [e for e in editform.split('.') if e]
2764 forms = [e for e in editform.split('.') if e]
2756 forms.insert(0, 'changeset')
2765 forms.insert(0, 'changeset')
2757 templatetext = None
2766 templatetext = None
2758 while forms:
2767 while forms:
2759 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2768 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2760 if tmpl:
2769 if tmpl:
2761 templatetext = committext = buildcommittemplate(
2770 templatetext = committext = buildcommittemplate(
2762 repo, ctx, subs, extramsg, tmpl)
2771 repo, ctx, subs, extramsg, tmpl)
2763 break
2772 break
2764 forms.pop()
2773 forms.pop()
2765 else:
2774 else:
2766 committext = buildcommittext(repo, ctx, subs, extramsg)
2775 committext = buildcommittext(repo, ctx, subs, extramsg)
2767
2776
2768 # run editor in the repository root
2777 # run editor in the repository root
2769 olddir = pycompat.getcwd()
2778 olddir = pycompat.getcwd()
2770 os.chdir(repo.root)
2779 os.chdir(repo.root)
2771
2780
2772 # make in-memory changes visible to external process
2781 # make in-memory changes visible to external process
2773 tr = repo.currenttransaction()
2782 tr = repo.currenttransaction()
2774 repo.dirstate.write(tr)
2783 repo.dirstate.write(tr)
2775 pending = tr and tr.writepending() and repo.root
2784 pending = tr and tr.writepending() and repo.root
2776
2785
2777 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
2786 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
2778 editform=editform, pending=pending,
2787 editform=editform, pending=pending,
2779 repopath=repo.path)
2788 repopath=repo.path)
2780 text = editortext
2789 text = editortext
2781
2790
2782 # strip away anything below this special string (used for editors that want
2791 # strip away anything below this special string (used for editors that want
2783 # to display the diff)
2792 # to display the diff)
2784 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
2793 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
2785 if stripbelow:
2794 if stripbelow:
2786 text = text[:stripbelow.start()]
2795 text = text[:stripbelow.start()]
2787
2796
2788 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2797 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2789 os.chdir(olddir)
2798 os.chdir(olddir)
2790
2799
2791 if finishdesc:
2800 if finishdesc:
2792 text = finishdesc(text)
2801 text = finishdesc(text)
2793 if not text.strip():
2802 if not text.strip():
2794 raise error.Abort(_("empty commit message"))
2803 raise error.Abort(_("empty commit message"))
2795 if unchangedmessagedetection and editortext == templatetext:
2804 if unchangedmessagedetection and editortext == templatetext:
2796 raise error.Abort(_("commit message unchanged"))
2805 raise error.Abort(_("commit message unchanged"))
2797
2806
2798 return text
2807 return text
2799
2808
2800 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2809 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2801 ui = repo.ui
2810 ui = repo.ui
2802 tmpl, mapfile = gettemplate(ui, tmpl, None)
2811 tmpl, mapfile = gettemplate(ui, tmpl, None)
2803
2812
2804 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2813 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2805
2814
2806 for k, v in repo.ui.configitems('committemplate'):
2815 for k, v in repo.ui.configitems('committemplate'):
2807 if k != 'changeset':
2816 if k != 'changeset':
2808 t.t.cache[k] = v
2817 t.t.cache[k] = v
2809
2818
2810 if not extramsg:
2819 if not extramsg:
2811 extramsg = '' # ensure that extramsg is string
2820 extramsg = '' # ensure that extramsg is string
2812
2821
2813 ui.pushbuffer()
2822 ui.pushbuffer()
2814 t.show(ctx, extramsg=extramsg)
2823 t.show(ctx, extramsg=extramsg)
2815 return ui.popbuffer()
2824 return ui.popbuffer()
2816
2825
2817 def hgprefix(msg):
2826 def hgprefix(msg):
2818 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
2827 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
2819
2828
2820 def buildcommittext(repo, ctx, subs, extramsg):
2829 def buildcommittext(repo, ctx, subs, extramsg):
2821 edittext = []
2830 edittext = []
2822 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2831 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2823 if ctx.description():
2832 if ctx.description():
2824 edittext.append(ctx.description())
2833 edittext.append(ctx.description())
2825 edittext.append("")
2834 edittext.append("")
2826 edittext.append("") # Empty line between message and comments.
2835 edittext.append("") # Empty line between message and comments.
2827 edittext.append(hgprefix(_("Enter commit message."
2836 edittext.append(hgprefix(_("Enter commit message."
2828 " Lines beginning with 'HG:' are removed.")))
2837 " Lines beginning with 'HG:' are removed.")))
2829 edittext.append(hgprefix(extramsg))
2838 edittext.append(hgprefix(extramsg))
2830 edittext.append("HG: --")
2839 edittext.append("HG: --")
2831 edittext.append(hgprefix(_("user: %s") % ctx.user()))
2840 edittext.append(hgprefix(_("user: %s") % ctx.user()))
2832 if ctx.p2():
2841 if ctx.p2():
2833 edittext.append(hgprefix(_("branch merge")))
2842 edittext.append(hgprefix(_("branch merge")))
2834 if ctx.branch():
2843 if ctx.branch():
2835 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
2844 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
2836 if bookmarks.isactivewdirparent(repo):
2845 if bookmarks.isactivewdirparent(repo):
2837 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
2846 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
2838 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
2847 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
2839 edittext.extend([hgprefix(_("added %s") % f) for f in added])
2848 edittext.extend([hgprefix(_("added %s") % f) for f in added])
2840 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
2849 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
2841 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
2850 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
2842 if not added and not modified and not removed:
2851 if not added and not modified and not removed:
2843 edittext.append(hgprefix(_("no files changed")))
2852 edittext.append(hgprefix(_("no files changed")))
2844 edittext.append("")
2853 edittext.append("")
2845
2854
2846 return "\n".join(edittext)
2855 return "\n".join(edittext)
2847
2856
2848 def commitstatus(repo, node, branch, bheads=None, opts=None):
2857 def commitstatus(repo, node, branch, bheads=None, opts=None):
2849 if opts is None:
2858 if opts is None:
2850 opts = {}
2859 opts = {}
2851 ctx = repo[node]
2860 ctx = repo[node]
2852 parents = ctx.parents()
2861 parents = ctx.parents()
2853
2862
2854 if (not opts.get('amend') and bheads and node not in bheads and not
2863 if (not opts.get('amend') and bheads and node not in bheads and not
2855 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2864 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2856 repo.ui.status(_('created new head\n'))
2865 repo.ui.status(_('created new head\n'))
2857 # The message is not printed for initial roots. For the other
2866 # The message is not printed for initial roots. For the other
2858 # changesets, it is printed in the following situations:
2867 # changesets, it is printed in the following situations:
2859 #
2868 #
2860 # Par column: for the 2 parents with ...
2869 # Par column: for the 2 parents with ...
2861 # N: null or no parent
2870 # N: null or no parent
2862 # B: parent is on another named branch
2871 # B: parent is on another named branch
2863 # C: parent is a regular non head changeset
2872 # C: parent is a regular non head changeset
2864 # H: parent was a branch head of the current branch
2873 # H: parent was a branch head of the current branch
2865 # Msg column: whether we print "created new head" message
2874 # Msg column: whether we print "created new head" message
2866 # In the following, it is assumed that there already exists some
2875 # In the following, it is assumed that there already exists some
2867 # initial branch heads of the current branch, otherwise nothing is
2876 # initial branch heads of the current branch, otherwise nothing is
2868 # printed anyway.
2877 # printed anyway.
2869 #
2878 #
2870 # Par Msg Comment
2879 # Par Msg Comment
2871 # N N y additional topo root
2880 # N N y additional topo root
2872 #
2881 #
2873 # B N y additional branch root
2882 # B N y additional branch root
2874 # C N y additional topo head
2883 # C N y additional topo head
2875 # H N n usual case
2884 # H N n usual case
2876 #
2885 #
2877 # B B y weird additional branch root
2886 # B B y weird additional branch root
2878 # C B y branch merge
2887 # C B y branch merge
2879 # H B n merge with named branch
2888 # H B n merge with named branch
2880 #
2889 #
2881 # C C y additional head from merge
2890 # C C y additional head from merge
2882 # C H n merge with a head
2891 # C H n merge with a head
2883 #
2892 #
2884 # H H n head merge: head count decreases
2893 # H H n head merge: head count decreases
2885
2894
2886 if not opts.get('close_branch'):
2895 if not opts.get('close_branch'):
2887 for r in parents:
2896 for r in parents:
2888 if r.closesbranch() and r.branch() == branch:
2897 if r.closesbranch() and r.branch() == branch:
2889 repo.ui.status(_('reopening closed branch head %d\n') % r)
2898 repo.ui.status(_('reopening closed branch head %d\n') % r)
2890
2899
2891 if repo.ui.debugflag:
2900 if repo.ui.debugflag:
2892 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2901 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2893 elif repo.ui.verbose:
2902 elif repo.ui.verbose:
2894 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2903 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2895
2904
2896 def postcommitstatus(repo, pats, opts):
2905 def postcommitstatus(repo, pats, opts):
2897 return repo.status(match=scmutil.match(repo[None], pats, opts))
2906 return repo.status(match=scmutil.match(repo[None], pats, opts))
2898
2907
2899 def revert(ui, repo, ctx, parents, *pats, **opts):
2908 def revert(ui, repo, ctx, parents, *pats, **opts):
2900 parent, p2 = parents
2909 parent, p2 = parents
2901 node = ctx.node()
2910 node = ctx.node()
2902
2911
2903 mf = ctx.manifest()
2912 mf = ctx.manifest()
2904 if node == p2:
2913 if node == p2:
2905 parent = p2
2914 parent = p2
2906
2915
2907 # need all matching names in dirstate and manifest of target rev,
2916 # need all matching names in dirstate and manifest of target rev,
2908 # so have to walk both. do not print errors if files exist in one
2917 # so have to walk both. do not print errors if files exist in one
2909 # but not other. in both cases, filesets should be evaluated against
2918 # but not other. in both cases, filesets should be evaluated against
2910 # workingctx to get consistent result (issue4497). this means 'set:**'
2919 # workingctx to get consistent result (issue4497). this means 'set:**'
2911 # cannot be used to select missing files from target rev.
2920 # cannot be used to select missing files from target rev.
2912
2921
2913 # `names` is a mapping for all elements in working copy and target revision
2922 # `names` is a mapping for all elements in working copy and target revision
2914 # The mapping is in the form:
2923 # The mapping is in the form:
2915 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2924 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2916 names = {}
2925 names = {}
2917
2926
2918 with repo.wlock():
2927 with repo.wlock():
2919 ## filling of the `names` mapping
2928 ## filling of the `names` mapping
2920 # walk dirstate to fill `names`
2929 # walk dirstate to fill `names`
2921
2930
2922 interactive = opts.get('interactive', False)
2931 interactive = opts.get('interactive', False)
2923 wctx = repo[None]
2932 wctx = repo[None]
2924 m = scmutil.match(wctx, pats, opts)
2933 m = scmutil.match(wctx, pats, opts)
2925
2934
2926 # we'll need this later
2935 # we'll need this later
2927 targetsubs = sorted(s for s in wctx.substate if m(s))
2936 targetsubs = sorted(s for s in wctx.substate if m(s))
2928
2937
2929 if not m.always():
2938 if not m.always():
2930 for abs in repo.walk(matchmod.badmatch(m, lambda x, y: False)):
2939 for abs in repo.walk(matchmod.badmatch(m, lambda x, y: False)):
2931 names[abs] = m.rel(abs), m.exact(abs)
2940 names[abs] = m.rel(abs), m.exact(abs)
2932
2941
2933 # walk target manifest to fill `names`
2942 # walk target manifest to fill `names`
2934
2943
2935 def badfn(path, msg):
2944 def badfn(path, msg):
2936 if path in names:
2945 if path in names:
2937 return
2946 return
2938 if path in ctx.substate:
2947 if path in ctx.substate:
2939 return
2948 return
2940 path_ = path + '/'
2949 path_ = path + '/'
2941 for f in names:
2950 for f in names:
2942 if f.startswith(path_):
2951 if f.startswith(path_):
2943 return
2952 return
2944 ui.warn("%s: %s\n" % (m.rel(path), msg))
2953 ui.warn("%s: %s\n" % (m.rel(path), msg))
2945
2954
2946 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
2955 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
2947 if abs not in names:
2956 if abs not in names:
2948 names[abs] = m.rel(abs), m.exact(abs)
2957 names[abs] = m.rel(abs), m.exact(abs)
2949
2958
2950 # Find status of all file in `names`.
2959 # Find status of all file in `names`.
2951 m = scmutil.matchfiles(repo, names)
2960 m = scmutil.matchfiles(repo, names)
2952
2961
2953 changes = repo.status(node1=node, match=m,
2962 changes = repo.status(node1=node, match=m,
2954 unknown=True, ignored=True, clean=True)
2963 unknown=True, ignored=True, clean=True)
2955 else:
2964 else:
2956 changes = repo.status(node1=node, match=m)
2965 changes = repo.status(node1=node, match=m)
2957 for kind in changes:
2966 for kind in changes:
2958 for abs in kind:
2967 for abs in kind:
2959 names[abs] = m.rel(abs), m.exact(abs)
2968 names[abs] = m.rel(abs), m.exact(abs)
2960
2969
2961 m = scmutil.matchfiles(repo, names)
2970 m = scmutil.matchfiles(repo, names)
2962
2971
2963 modified = set(changes.modified)
2972 modified = set(changes.modified)
2964 added = set(changes.added)
2973 added = set(changes.added)
2965 removed = set(changes.removed)
2974 removed = set(changes.removed)
2966 _deleted = set(changes.deleted)
2975 _deleted = set(changes.deleted)
2967 unknown = set(changes.unknown)
2976 unknown = set(changes.unknown)
2968 unknown.update(changes.ignored)
2977 unknown.update(changes.ignored)
2969 clean = set(changes.clean)
2978 clean = set(changes.clean)
2970 modadded = set()
2979 modadded = set()
2971
2980
2972 # We need to account for the state of the file in the dirstate,
2981 # We need to account for the state of the file in the dirstate,
2973 # even when we revert against something else than parent. This will
2982 # even when we revert against something else than parent. This will
2974 # slightly alter the behavior of revert (doing back up or not, delete
2983 # slightly alter the behavior of revert (doing back up or not, delete
2975 # or just forget etc).
2984 # or just forget etc).
2976 if parent == node:
2985 if parent == node:
2977 dsmodified = modified
2986 dsmodified = modified
2978 dsadded = added
2987 dsadded = added
2979 dsremoved = removed
2988 dsremoved = removed
2980 # store all local modifications, useful later for rename detection
2989 # store all local modifications, useful later for rename detection
2981 localchanges = dsmodified | dsadded
2990 localchanges = dsmodified | dsadded
2982 modified, added, removed = set(), set(), set()
2991 modified, added, removed = set(), set(), set()
2983 else:
2992 else:
2984 changes = repo.status(node1=parent, match=m)
2993 changes = repo.status(node1=parent, match=m)
2985 dsmodified = set(changes.modified)
2994 dsmodified = set(changes.modified)
2986 dsadded = set(changes.added)
2995 dsadded = set(changes.added)
2987 dsremoved = set(changes.removed)
2996 dsremoved = set(changes.removed)
2988 # store all local modifications, useful later for rename detection
2997 # store all local modifications, useful later for rename detection
2989 localchanges = dsmodified | dsadded
2998 localchanges = dsmodified | dsadded
2990
2999
2991 # only take into account for removes between wc and target
3000 # only take into account for removes between wc and target
2992 clean |= dsremoved - removed
3001 clean |= dsremoved - removed
2993 dsremoved &= removed
3002 dsremoved &= removed
2994 # distinct between dirstate remove and other
3003 # distinct between dirstate remove and other
2995 removed -= dsremoved
3004 removed -= dsremoved
2996
3005
2997 modadded = added & dsmodified
3006 modadded = added & dsmodified
2998 added -= modadded
3007 added -= modadded
2999
3008
3000 # tell newly modified apart.
3009 # tell newly modified apart.
3001 dsmodified &= modified
3010 dsmodified &= modified
3002 dsmodified |= modified & dsadded # dirstate added may need backup
3011 dsmodified |= modified & dsadded # dirstate added may need backup
3003 modified -= dsmodified
3012 modified -= dsmodified
3004
3013
3005 # We need to wait for some post-processing to update this set
3014 # We need to wait for some post-processing to update this set
3006 # before making the distinction. The dirstate will be used for
3015 # before making the distinction. The dirstate will be used for
3007 # that purpose.
3016 # that purpose.
3008 dsadded = added
3017 dsadded = added
3009
3018
3010 # in case of merge, files that are actually added can be reported as
3019 # in case of merge, files that are actually added can be reported as
3011 # modified, we need to post process the result
3020 # modified, we need to post process the result
3012 if p2 != nullid:
3021 if p2 != nullid:
3013 mergeadd = set(dsmodified)
3022 mergeadd = set(dsmodified)
3014 for path in dsmodified:
3023 for path in dsmodified:
3015 if path in mf:
3024 if path in mf:
3016 mergeadd.remove(path)
3025 mergeadd.remove(path)
3017 dsadded |= mergeadd
3026 dsadded |= mergeadd
3018 dsmodified -= mergeadd
3027 dsmodified -= mergeadd
3019
3028
3020 # if f is a rename, update `names` to also revert the source
3029 # if f is a rename, update `names` to also revert the source
3021 cwd = repo.getcwd()
3030 cwd = repo.getcwd()
3022 for f in localchanges:
3031 for f in localchanges:
3023 src = repo.dirstate.copied(f)
3032 src = repo.dirstate.copied(f)
3024 # XXX should we check for rename down to target node?
3033 # XXX should we check for rename down to target node?
3025 if src and src not in names and repo.dirstate[src] == 'r':
3034 if src and src not in names and repo.dirstate[src] == 'r':
3026 dsremoved.add(src)
3035 dsremoved.add(src)
3027 names[src] = (repo.pathto(src, cwd), True)
3036 names[src] = (repo.pathto(src, cwd), True)
3028
3037
3029 # determine the exact nature of the deleted changesets
3038 # determine the exact nature of the deleted changesets
3030 deladded = set(_deleted)
3039 deladded = set(_deleted)
3031 for path in _deleted:
3040 for path in _deleted:
3032 if path in mf:
3041 if path in mf:
3033 deladded.remove(path)
3042 deladded.remove(path)
3034 deleted = _deleted - deladded
3043 deleted = _deleted - deladded
3035
3044
3036 # distinguish between file to forget and the other
3045 # distinguish between file to forget and the other
3037 added = set()
3046 added = set()
3038 for abs in dsadded:
3047 for abs in dsadded:
3039 if repo.dirstate[abs] != 'a':
3048 if repo.dirstate[abs] != 'a':
3040 added.add(abs)
3049 added.add(abs)
3041 dsadded -= added
3050 dsadded -= added
3042
3051
3043 for abs in deladded:
3052 for abs in deladded:
3044 if repo.dirstate[abs] == 'a':
3053 if repo.dirstate[abs] == 'a':
3045 dsadded.add(abs)
3054 dsadded.add(abs)
3046 deladded -= dsadded
3055 deladded -= dsadded
3047
3056
3048 # For files marked as removed, we check if an unknown file is present at
3057 # For files marked as removed, we check if an unknown file is present at
3049 # the same path. If a such file exists it may need to be backed up.
3058 # the same path. If a such file exists it may need to be backed up.
3050 # Making the distinction at this stage helps have simpler backup
3059 # Making the distinction at this stage helps have simpler backup
3051 # logic.
3060 # logic.
3052 removunk = set()
3061 removunk = set()
3053 for abs in removed:
3062 for abs in removed:
3054 target = repo.wjoin(abs)
3063 target = repo.wjoin(abs)
3055 if os.path.lexists(target):
3064 if os.path.lexists(target):
3056 removunk.add(abs)
3065 removunk.add(abs)
3057 removed -= removunk
3066 removed -= removunk
3058
3067
3059 dsremovunk = set()
3068 dsremovunk = set()
3060 for abs in dsremoved:
3069 for abs in dsremoved:
3061 target = repo.wjoin(abs)
3070 target = repo.wjoin(abs)
3062 if os.path.lexists(target):
3071 if os.path.lexists(target):
3063 dsremovunk.add(abs)
3072 dsremovunk.add(abs)
3064 dsremoved -= dsremovunk
3073 dsremoved -= dsremovunk
3065
3074
3066 # action to be actually performed by revert
3075 # action to be actually performed by revert
3067 # (<list of file>, message>) tuple
3076 # (<list of file>, message>) tuple
3068 actions = {'revert': ([], _('reverting %s\n')),
3077 actions = {'revert': ([], _('reverting %s\n')),
3069 'add': ([], _('adding %s\n')),
3078 'add': ([], _('adding %s\n')),
3070 'remove': ([], _('removing %s\n')),
3079 'remove': ([], _('removing %s\n')),
3071 'drop': ([], _('removing %s\n')),
3080 'drop': ([], _('removing %s\n')),
3072 'forget': ([], _('forgetting %s\n')),
3081 'forget': ([], _('forgetting %s\n')),
3073 'undelete': ([], _('undeleting %s\n')),
3082 'undelete': ([], _('undeleting %s\n')),
3074 'noop': (None, _('no changes needed to %s\n')),
3083 'noop': (None, _('no changes needed to %s\n')),
3075 'unknown': (None, _('file not managed: %s\n')),
3084 'unknown': (None, _('file not managed: %s\n')),
3076 }
3085 }
3077
3086
3078 # "constant" that convey the backup strategy.
3087 # "constant" that convey the backup strategy.
3079 # All set to `discard` if `no-backup` is set do avoid checking
3088 # All set to `discard` if `no-backup` is set do avoid checking
3080 # no_backup lower in the code.
3089 # no_backup lower in the code.
3081 # These values are ordered for comparison purposes
3090 # These values are ordered for comparison purposes
3082 backupinteractive = 3 # do backup if interactively modified
3091 backupinteractive = 3 # do backup if interactively modified
3083 backup = 2 # unconditionally do backup
3092 backup = 2 # unconditionally do backup
3084 check = 1 # check if the existing file differs from target
3093 check = 1 # check if the existing file differs from target
3085 discard = 0 # never do backup
3094 discard = 0 # never do backup
3086 if opts.get('no_backup'):
3095 if opts.get('no_backup'):
3087 backupinteractive = backup = check = discard
3096 backupinteractive = backup = check = discard
3088 if interactive:
3097 if interactive:
3089 dsmodifiedbackup = backupinteractive
3098 dsmodifiedbackup = backupinteractive
3090 else:
3099 else:
3091 dsmodifiedbackup = backup
3100 dsmodifiedbackup = backup
3092 tobackup = set()
3101 tobackup = set()
3093
3102
3094 backupanddel = actions['remove']
3103 backupanddel = actions['remove']
3095 if not opts.get('no_backup'):
3104 if not opts.get('no_backup'):
3096 backupanddel = actions['drop']
3105 backupanddel = actions['drop']
3097
3106
3098 disptable = (
3107 disptable = (
3099 # dispatch table:
3108 # dispatch table:
3100 # file state
3109 # file state
3101 # action
3110 # action
3102 # make backup
3111 # make backup
3103
3112
3104 ## Sets that results that will change file on disk
3113 ## Sets that results that will change file on disk
3105 # Modified compared to target, no local change
3114 # Modified compared to target, no local change
3106 (modified, actions['revert'], discard),
3115 (modified, actions['revert'], discard),
3107 # Modified compared to target, but local file is deleted
3116 # Modified compared to target, but local file is deleted
3108 (deleted, actions['revert'], discard),
3117 (deleted, actions['revert'], discard),
3109 # Modified compared to target, local change
3118 # Modified compared to target, local change
3110 (dsmodified, actions['revert'], dsmodifiedbackup),
3119 (dsmodified, actions['revert'], dsmodifiedbackup),
3111 # Added since target
3120 # Added since target
3112 (added, actions['remove'], discard),
3121 (added, actions['remove'], discard),
3113 # Added in working directory
3122 # Added in working directory
3114 (dsadded, actions['forget'], discard),
3123 (dsadded, actions['forget'], discard),
3115 # Added since target, have local modification
3124 # Added since target, have local modification
3116 (modadded, backupanddel, backup),
3125 (modadded, backupanddel, backup),
3117 # Added since target but file is missing in working directory
3126 # Added since target but file is missing in working directory
3118 (deladded, actions['drop'], discard),
3127 (deladded, actions['drop'], discard),
3119 # Removed since target, before working copy parent
3128 # Removed since target, before working copy parent
3120 (removed, actions['add'], discard),
3129 (removed, actions['add'], discard),
3121 # Same as `removed` but an unknown file exists at the same path
3130 # Same as `removed` but an unknown file exists at the same path
3122 (removunk, actions['add'], check),
3131 (removunk, actions['add'], check),
3123 # Removed since targe, marked as such in working copy parent
3132 # Removed since targe, marked as such in working copy parent
3124 (dsremoved, actions['undelete'], discard),
3133 (dsremoved, actions['undelete'], discard),
3125 # Same as `dsremoved` but an unknown file exists at the same path
3134 # Same as `dsremoved` but an unknown file exists at the same path
3126 (dsremovunk, actions['undelete'], check),
3135 (dsremovunk, actions['undelete'], check),
3127 ## the following sets does not result in any file changes
3136 ## the following sets does not result in any file changes
3128 # File with no modification
3137 # File with no modification
3129 (clean, actions['noop'], discard),
3138 (clean, actions['noop'], discard),
3130 # Existing file, not tracked anywhere
3139 # Existing file, not tracked anywhere
3131 (unknown, actions['unknown'], discard),
3140 (unknown, actions['unknown'], discard),
3132 )
3141 )
3133
3142
3134 for abs, (rel, exact) in sorted(names.items()):
3143 for abs, (rel, exact) in sorted(names.items()):
3135 # target file to be touch on disk (relative to cwd)
3144 # target file to be touch on disk (relative to cwd)
3136 target = repo.wjoin(abs)
3145 target = repo.wjoin(abs)
3137 # search the entry in the dispatch table.
3146 # search the entry in the dispatch table.
3138 # if the file is in any of these sets, it was touched in the working
3147 # if the file is in any of these sets, it was touched in the working
3139 # directory parent and we are sure it needs to be reverted.
3148 # directory parent and we are sure it needs to be reverted.
3140 for table, (xlist, msg), dobackup in disptable:
3149 for table, (xlist, msg), dobackup in disptable:
3141 if abs not in table:
3150 if abs not in table:
3142 continue
3151 continue
3143 if xlist is not None:
3152 if xlist is not None:
3144 xlist.append(abs)
3153 xlist.append(abs)
3145 if dobackup:
3154 if dobackup:
3146 # If in interactive mode, don't automatically create
3155 # If in interactive mode, don't automatically create
3147 # .orig files (issue4793)
3156 # .orig files (issue4793)
3148 if dobackup == backupinteractive:
3157 if dobackup == backupinteractive:
3149 tobackup.add(abs)
3158 tobackup.add(abs)
3150 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
3159 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
3151 bakname = scmutil.origpath(ui, repo, rel)
3160 bakname = scmutil.origpath(ui, repo, rel)
3152 ui.note(_('saving current version of %s as %s\n') %
3161 ui.note(_('saving current version of %s as %s\n') %
3153 (rel, bakname))
3162 (rel, bakname))
3154 if not opts.get('dry_run'):
3163 if not opts.get('dry_run'):
3155 if interactive:
3164 if interactive:
3156 util.copyfile(target, bakname)
3165 util.copyfile(target, bakname)
3157 else:
3166 else:
3158 util.rename(target, bakname)
3167 util.rename(target, bakname)
3159 if ui.verbose or not exact:
3168 if ui.verbose or not exact:
3160 if not isinstance(msg, basestring):
3169 if not isinstance(msg, basestring):
3161 msg = msg(abs)
3170 msg = msg(abs)
3162 ui.status(msg % rel)
3171 ui.status(msg % rel)
3163 elif exact:
3172 elif exact:
3164 ui.warn(msg % rel)
3173 ui.warn(msg % rel)
3165 break
3174 break
3166
3175
3167 if not opts.get('dry_run'):
3176 if not opts.get('dry_run'):
3168 needdata = ('revert', 'add', 'undelete')
3177 needdata = ('revert', 'add', 'undelete')
3169 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3178 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3170 _performrevert(repo, parents, ctx, actions, interactive, tobackup)
3179 _performrevert(repo, parents, ctx, actions, interactive, tobackup)
3171
3180
3172 if targetsubs:
3181 if targetsubs:
3173 # Revert the subrepos on the revert list
3182 # Revert the subrepos on the revert list
3174 for sub in targetsubs:
3183 for sub in targetsubs:
3175 try:
3184 try:
3176 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3185 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3177 except KeyError:
3186 except KeyError:
3178 raise error.Abort("subrepository '%s' does not exist in %s!"
3187 raise error.Abort("subrepository '%s' does not exist in %s!"
3179 % (sub, short(ctx.node())))
3188 % (sub, short(ctx.node())))
3180
3189
3181 def _revertprefetch(repo, ctx, *files):
3190 def _revertprefetch(repo, ctx, *files):
3182 """Let extension changing the storage layer prefetch content"""
3191 """Let extension changing the storage layer prefetch content"""
3183 pass
3192 pass
3184
3193
3185 def _performrevert(repo, parents, ctx, actions, interactive=False,
3194 def _performrevert(repo, parents, ctx, actions, interactive=False,
3186 tobackup=None):
3195 tobackup=None):
3187 """function that actually perform all the actions computed for revert
3196 """function that actually perform all the actions computed for revert
3188
3197
3189 This is an independent function to let extension to plug in and react to
3198 This is an independent function to let extension to plug in and react to
3190 the imminent revert.
3199 the imminent revert.
3191
3200
3192 Make sure you have the working directory locked when calling this function.
3201 Make sure you have the working directory locked when calling this function.
3193 """
3202 """
3194 parent, p2 = parents
3203 parent, p2 = parents
3195 node = ctx.node()
3204 node = ctx.node()
3196 excluded_files = []
3205 excluded_files = []
3197 matcher_opts = {"exclude": excluded_files}
3206 matcher_opts = {"exclude": excluded_files}
3198
3207
3199 def checkout(f):
3208 def checkout(f):
3200 fc = ctx[f]
3209 fc = ctx[f]
3201 repo.wwrite(f, fc.data(), fc.flags())
3210 repo.wwrite(f, fc.data(), fc.flags())
3202
3211
3203 def doremove(f):
3212 def doremove(f):
3204 try:
3213 try:
3205 repo.wvfs.unlinkpath(f)
3214 repo.wvfs.unlinkpath(f)
3206 except OSError:
3215 except OSError:
3207 pass
3216 pass
3208 repo.dirstate.remove(f)
3217 repo.dirstate.remove(f)
3209
3218
3210 audit_path = pathutil.pathauditor(repo.root)
3219 audit_path = pathutil.pathauditor(repo.root)
3211 for f in actions['forget'][0]:
3220 for f in actions['forget'][0]:
3212 if interactive:
3221 if interactive:
3213 choice = repo.ui.promptchoice(
3222 choice = repo.ui.promptchoice(
3214 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f)
3223 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f)
3215 if choice == 0:
3224 if choice == 0:
3216 repo.dirstate.drop(f)
3225 repo.dirstate.drop(f)
3217 else:
3226 else:
3218 excluded_files.append(repo.wjoin(f))
3227 excluded_files.append(repo.wjoin(f))
3219 else:
3228 else:
3220 repo.dirstate.drop(f)
3229 repo.dirstate.drop(f)
3221 for f in actions['remove'][0]:
3230 for f in actions['remove'][0]:
3222 audit_path(f)
3231 audit_path(f)
3223 if interactive:
3232 if interactive:
3224 choice = repo.ui.promptchoice(
3233 choice = repo.ui.promptchoice(
3225 _("remove added file %s (Yn)?$$ &Yes $$ &No") % f)
3234 _("remove added file %s (Yn)?$$ &Yes $$ &No") % f)
3226 if choice == 0:
3235 if choice == 0:
3227 doremove(f)
3236 doremove(f)
3228 else:
3237 else:
3229 excluded_files.append(repo.wjoin(f))
3238 excluded_files.append(repo.wjoin(f))
3230 else:
3239 else:
3231 doremove(f)
3240 doremove(f)
3232 for f in actions['drop'][0]:
3241 for f in actions['drop'][0]:
3233 audit_path(f)
3242 audit_path(f)
3234 repo.dirstate.remove(f)
3243 repo.dirstate.remove(f)
3235
3244
3236 normal = None
3245 normal = None
3237 if node == parent:
3246 if node == parent:
3238 # We're reverting to our parent. If possible, we'd like status
3247 # We're reverting to our parent. If possible, we'd like status
3239 # to report the file as clean. We have to use normallookup for
3248 # to report the file as clean. We have to use normallookup for
3240 # merges to avoid losing information about merged/dirty files.
3249 # merges to avoid losing information about merged/dirty files.
3241 if p2 != nullid:
3250 if p2 != nullid:
3242 normal = repo.dirstate.normallookup
3251 normal = repo.dirstate.normallookup
3243 else:
3252 else:
3244 normal = repo.dirstate.normal
3253 normal = repo.dirstate.normal
3245
3254
3246 newlyaddedandmodifiedfiles = set()
3255 newlyaddedandmodifiedfiles = set()
3247 if interactive:
3256 if interactive:
3248 # Prompt the user for changes to revert
3257 # Prompt the user for changes to revert
3249 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3258 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3250 m = scmutil.match(ctx, torevert, matcher_opts)
3259 m = scmutil.match(ctx, torevert, matcher_opts)
3251 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3260 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3252 diffopts.nodates = True
3261 diffopts.nodates = True
3253 diffopts.git = True
3262 diffopts.git = True
3254 operation = 'discard'
3263 operation = 'discard'
3255 reversehunks = True
3264 reversehunks = True
3256 if node != parent:
3265 if node != parent:
3257 operation = 'revert'
3266 operation = 'revert'
3258 reversehunks = repo.ui.configbool('experimental',
3267 reversehunks = repo.ui.configbool('experimental',
3259 'revertalternateinteractivemode',
3268 'revertalternateinteractivemode',
3260 True)
3269 True)
3261 if reversehunks:
3270 if reversehunks:
3262 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3271 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3263 else:
3272 else:
3264 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3273 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3265 originalchunks = patch.parsepatch(diff)
3274 originalchunks = patch.parsepatch(diff)
3266
3275
3267 try:
3276 try:
3268
3277
3269 chunks, opts = recordfilter(repo.ui, originalchunks,
3278 chunks, opts = recordfilter(repo.ui, originalchunks,
3270 operation=operation)
3279 operation=operation)
3271 if reversehunks:
3280 if reversehunks:
3272 chunks = patch.reversehunks(chunks)
3281 chunks = patch.reversehunks(chunks)
3273
3282
3274 except patch.PatchError as err:
3283 except patch.PatchError as err:
3275 raise error.Abort(_('error parsing patch: %s') % err)
3284 raise error.Abort(_('error parsing patch: %s') % err)
3276
3285
3277 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3286 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3278 if tobackup is None:
3287 if tobackup is None:
3279 tobackup = set()
3288 tobackup = set()
3280 # Apply changes
3289 # Apply changes
3281 fp = stringio()
3290 fp = stringio()
3282 for c in chunks:
3291 for c in chunks:
3283 # Create a backup file only if this hunk should be backed up
3292 # Create a backup file only if this hunk should be backed up
3284 if ishunk(c) and c.header.filename() in tobackup:
3293 if ishunk(c) and c.header.filename() in tobackup:
3285 abs = c.header.filename()
3294 abs = c.header.filename()
3286 target = repo.wjoin(abs)
3295 target = repo.wjoin(abs)
3287 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3296 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3288 util.copyfile(target, bakname)
3297 util.copyfile(target, bakname)
3289 tobackup.remove(abs)
3298 tobackup.remove(abs)
3290 c.write(fp)
3299 c.write(fp)
3291 dopatch = fp.tell()
3300 dopatch = fp.tell()
3292 fp.seek(0)
3301 fp.seek(0)
3293 if dopatch:
3302 if dopatch:
3294 try:
3303 try:
3295 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3304 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3296 except patch.PatchError as err:
3305 except patch.PatchError as err:
3297 raise error.Abort(str(err))
3306 raise error.Abort(str(err))
3298 del fp
3307 del fp
3299 else:
3308 else:
3300 for f in actions['revert'][0]:
3309 for f in actions['revert'][0]:
3301 checkout(f)
3310 checkout(f)
3302 if normal:
3311 if normal:
3303 normal(f)
3312 normal(f)
3304
3313
3305 for f in actions['add'][0]:
3314 for f in actions['add'][0]:
3306 # Don't checkout modified files, they are already created by the diff
3315 # Don't checkout modified files, they are already created by the diff
3307 if f not in newlyaddedandmodifiedfiles:
3316 if f not in newlyaddedandmodifiedfiles:
3308 checkout(f)
3317 checkout(f)
3309 repo.dirstate.add(f)
3318 repo.dirstate.add(f)
3310
3319
3311 normal = repo.dirstate.normallookup
3320 normal = repo.dirstate.normallookup
3312 if node == parent and p2 == nullid:
3321 if node == parent and p2 == nullid:
3313 normal = repo.dirstate.normal
3322 normal = repo.dirstate.normal
3314 for f in actions['undelete'][0]:
3323 for f in actions['undelete'][0]:
3315 checkout(f)
3324 checkout(f)
3316 normal(f)
3325 normal(f)
3317
3326
3318 copied = copies.pathcopies(repo[parent], ctx)
3327 copied = copies.pathcopies(repo[parent], ctx)
3319
3328
3320 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3329 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3321 if f in copied:
3330 if f in copied:
3322 repo.dirstate.copy(copied[f], f)
3331 repo.dirstate.copy(copied[f], f)
3323
3332
3324 def command(table):
3333 def command(table):
3325 """Returns a function object to be used as a decorator for making commands.
3334 """Returns a function object to be used as a decorator for making commands.
3326
3335
3327 This function receives a command table as its argument. The table should
3336 This function receives a command table as its argument. The table should
3328 be a dict.
3337 be a dict.
3329
3338
3330 The returned function can be used as a decorator for adding commands
3339 The returned function can be used as a decorator for adding commands
3331 to that command table. This function accepts multiple arguments to define
3340 to that command table. This function accepts multiple arguments to define
3332 a command.
3341 a command.
3333
3342
3334 The first argument is the command name.
3343 The first argument is the command name.
3335
3344
3336 The options argument is an iterable of tuples defining command arguments.
3345 The options argument is an iterable of tuples defining command arguments.
3337 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
3346 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
3338
3347
3339 The synopsis argument defines a short, one line summary of how to use the
3348 The synopsis argument defines a short, one line summary of how to use the
3340 command. This shows up in the help output.
3349 command. This shows up in the help output.
3341
3350
3342 The norepo argument defines whether the command does not require a
3351 The norepo argument defines whether the command does not require a
3343 local repository. Most commands operate against a repository, thus the
3352 local repository. Most commands operate against a repository, thus the
3344 default is False.
3353 default is False.
3345
3354
3346 The optionalrepo argument defines whether the command optionally requires
3355 The optionalrepo argument defines whether the command optionally requires
3347 a local repository.
3356 a local repository.
3348
3357
3349 The inferrepo argument defines whether to try to find a repository from the
3358 The inferrepo argument defines whether to try to find a repository from the
3350 command line arguments. If True, arguments will be examined for potential
3359 command line arguments. If True, arguments will be examined for potential
3351 repository locations. See ``findrepo()``. If a repository is found, it
3360 repository locations. See ``findrepo()``. If a repository is found, it
3352 will be used.
3361 will be used.
3353 """
3362 """
3354 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
3363 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
3355 inferrepo=False):
3364 inferrepo=False):
3356 def decorator(func):
3365 def decorator(func):
3357 func.norepo = norepo
3366 func.norepo = norepo
3358 func.optionalrepo = optionalrepo
3367 func.optionalrepo = optionalrepo
3359 func.inferrepo = inferrepo
3368 func.inferrepo = inferrepo
3360 if synopsis:
3369 if synopsis:
3361 table[name] = func, list(options), synopsis
3370 table[name] = func, list(options), synopsis
3362 else:
3371 else:
3363 table[name] = func, list(options)
3372 table[name] = func, list(options)
3364 return func
3373 return func
3365 return decorator
3374 return decorator
3366
3375
3367 return cmd
3376 return cmd
3368
3377
3369 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3378 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3370 # commands.outgoing. "missing" is "missing" of the result of
3379 # commands.outgoing. "missing" is "missing" of the result of
3371 # "findcommonoutgoing()"
3380 # "findcommonoutgoing()"
3372 outgoinghooks = util.hooks()
3381 outgoinghooks = util.hooks()
3373
3382
3374 # a list of (ui, repo) functions called by commands.summary
3383 # a list of (ui, repo) functions called by commands.summary
3375 summaryhooks = util.hooks()
3384 summaryhooks = util.hooks()
3376
3385
3377 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3386 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3378 #
3387 #
3379 # functions should return tuple of booleans below, if 'changes' is None:
3388 # functions should return tuple of booleans below, if 'changes' is None:
3380 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3389 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3381 #
3390 #
3382 # otherwise, 'changes' is a tuple of tuples below:
3391 # otherwise, 'changes' is a tuple of tuples below:
3383 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3392 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3384 # - (desturl, destbranch, destpeer, outgoing)
3393 # - (desturl, destbranch, destpeer, outgoing)
3385 summaryremotehooks = util.hooks()
3394 summaryremotehooks = util.hooks()
3386
3395
3387 # A list of state files kept by multistep operations like graft.
3396 # A list of state files kept by multistep operations like graft.
3388 # Since graft cannot be aborted, it is considered 'clearable' by update.
3397 # Since graft cannot be aborted, it is considered 'clearable' by update.
3389 # note: bisect is intentionally excluded
3398 # note: bisect is intentionally excluded
3390 # (state file, clearable, allowcommit, error, hint)
3399 # (state file, clearable, allowcommit, error, hint)
3391 unfinishedstates = [
3400 unfinishedstates = [
3392 ('graftstate', True, False, _('graft in progress'),
3401 ('graftstate', True, False, _('graft in progress'),
3393 _("use 'hg graft --continue' or 'hg update' to abort")),
3402 _("use 'hg graft --continue' or 'hg update' to abort")),
3394 ('updatestate', True, False, _('last update was interrupted'),
3403 ('updatestate', True, False, _('last update was interrupted'),
3395 _("use 'hg update' to get a consistent checkout"))
3404 _("use 'hg update' to get a consistent checkout"))
3396 ]
3405 ]
3397
3406
3398 def checkunfinished(repo, commit=False):
3407 def checkunfinished(repo, commit=False):
3399 '''Look for an unfinished multistep operation, like graft, and abort
3408 '''Look for an unfinished multistep operation, like graft, and abort
3400 if found. It's probably good to check this right before
3409 if found. It's probably good to check this right before
3401 bailifchanged().
3410 bailifchanged().
3402 '''
3411 '''
3403 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3412 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3404 if commit and allowcommit:
3413 if commit and allowcommit:
3405 continue
3414 continue
3406 if repo.vfs.exists(f):
3415 if repo.vfs.exists(f):
3407 raise error.Abort(msg, hint=hint)
3416 raise error.Abort(msg, hint=hint)
3408
3417
3409 def clearunfinished(repo):
3418 def clearunfinished(repo):
3410 '''Check for unfinished operations (as above), and clear the ones
3419 '''Check for unfinished operations (as above), and clear the ones
3411 that are clearable.
3420 that are clearable.
3412 '''
3421 '''
3413 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3422 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3414 if not clearable and repo.vfs.exists(f):
3423 if not clearable and repo.vfs.exists(f):
3415 raise error.Abort(msg, hint=hint)
3424 raise error.Abort(msg, hint=hint)
3416 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3425 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3417 if clearable and repo.vfs.exists(f):
3426 if clearable and repo.vfs.exists(f):
3418 util.unlink(repo.vfs.join(f))
3427 util.unlink(repo.vfs.join(f))
3419
3428
3420 afterresolvedstates = [
3429 afterresolvedstates = [
3421 ('graftstate',
3430 ('graftstate',
3422 _('hg graft --continue')),
3431 _('hg graft --continue')),
3423 ]
3432 ]
3424
3433
3425 def howtocontinue(repo):
3434 def howtocontinue(repo):
3426 '''Check for an unfinished operation and return the command to finish
3435 '''Check for an unfinished operation and return the command to finish
3427 it.
3436 it.
3428
3437
3429 afterresolvedstates tuples define a .hg/{file} and the corresponding
3438 afterresolvedstates tuples define a .hg/{file} and the corresponding
3430 command needed to finish it.
3439 command needed to finish it.
3431
3440
3432 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3441 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3433 a boolean.
3442 a boolean.
3434 '''
3443 '''
3435 contmsg = _("continue: %s")
3444 contmsg = _("continue: %s")
3436 for f, msg in afterresolvedstates:
3445 for f, msg in afterresolvedstates:
3437 if repo.vfs.exists(f):
3446 if repo.vfs.exists(f):
3438 return contmsg % msg, True
3447 return contmsg % msg, True
3439 workingctx = repo[None]
3448 workingctx = repo[None]
3440 dirty = any(repo.status()) or any(workingctx.sub(s).dirty()
3449 dirty = any(repo.status()) or any(workingctx.sub(s).dirty()
3441 for s in workingctx.substate)
3450 for s in workingctx.substate)
3442 if dirty:
3451 if dirty:
3443 return contmsg % _("hg commit"), False
3452 return contmsg % _("hg commit"), False
3444 return None, None
3453 return None, None
3445
3454
3446 def checkafterresolved(repo):
3455 def checkafterresolved(repo):
3447 '''Inform the user about the next action after completing hg resolve
3456 '''Inform the user about the next action after completing hg resolve
3448
3457
3449 If there's a matching afterresolvedstates, howtocontinue will yield
3458 If there's a matching afterresolvedstates, howtocontinue will yield
3450 repo.ui.warn as the reporter.
3459 repo.ui.warn as the reporter.
3451
3460
3452 Otherwise, it will yield repo.ui.note.
3461 Otherwise, it will yield repo.ui.note.
3453 '''
3462 '''
3454 msg, warning = howtocontinue(repo)
3463 msg, warning = howtocontinue(repo)
3455 if msg is not None:
3464 if msg is not None:
3456 if warning:
3465 if warning:
3457 repo.ui.warn("%s\n" % msg)
3466 repo.ui.warn("%s\n" % msg)
3458 else:
3467 else:
3459 repo.ui.note("%s\n" % msg)
3468 repo.ui.note("%s\n" % msg)
3460
3469
3461 def wrongtooltocontinue(repo, task):
3470 def wrongtooltocontinue(repo, task):
3462 '''Raise an abort suggesting how to properly continue if there is an
3471 '''Raise an abort suggesting how to properly continue if there is an
3463 active task.
3472 active task.
3464
3473
3465 Uses howtocontinue() to find the active task.
3474 Uses howtocontinue() to find the active task.
3466
3475
3467 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3476 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3468 a hint.
3477 a hint.
3469 '''
3478 '''
3470 after = howtocontinue(repo)
3479 after = howtocontinue(repo)
3471 hint = None
3480 hint = None
3472 if after[1]:
3481 if after[1]:
3473 hint = after[0]
3482 hint = after[0]
3474 raise error.Abort(_('no %s in progress') % task, hint=hint)
3483 raise error.Abort(_('no %s in progress') % task, hint=hint)
@@ -1,5477 +1,5478 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import difflib
10 import difflib
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 hex,
17 hex,
18 nullid,
18 nullid,
19 nullrev,
19 nullrev,
20 short,
20 short,
21 )
21 )
22 from . import (
22 from . import (
23 archival,
23 archival,
24 bookmarks,
24 bookmarks,
25 bundle2,
25 bundle2,
26 changegroup,
26 changegroup,
27 cmdutil,
27 cmdutil,
28 copies,
28 copies,
29 destutil,
29 destutil,
30 dirstateguard,
30 dirstateguard,
31 discovery,
31 discovery,
32 encoding,
32 encoding,
33 error,
33 error,
34 exchange,
34 exchange,
35 extensions,
35 extensions,
36 graphmod,
36 graphmod,
37 hbisect,
37 hbisect,
38 help,
38 help,
39 hg,
39 hg,
40 lock as lockmod,
40 lock as lockmod,
41 merge as mergemod,
41 merge as mergemod,
42 obsolete,
42 obsolete,
43 patch,
43 patch,
44 phases,
44 phases,
45 pycompat,
45 pycompat,
46 rcutil,
46 rcutil,
47 revsetlang,
47 revsetlang,
48 scmutil,
48 scmutil,
49 server,
49 server,
50 sshserver,
50 sshserver,
51 streamclone,
51 streamclone,
52 tags as tagsmod,
52 tags as tagsmod,
53 templatekw,
53 templatekw,
54 ui as uimod,
54 ui as uimod,
55 util,
55 util,
56 )
56 )
57
57
58 release = lockmod.release
58 release = lockmod.release
59
59
60 table = {}
60 table = {}
61
61
62 command = cmdutil.command(table)
62 command = cmdutil.command(table)
63
63
64 # label constants
64 # label constants
65 # until 3.5, bookmarks.current was the advertised name, not
65 # until 3.5, bookmarks.current was the advertised name, not
66 # bookmarks.active, so we must use both to avoid breaking old
66 # bookmarks.active, so we must use both to avoid breaking old
67 # custom styles
67 # custom styles
68 activebookmarklabel = 'bookmarks.active bookmarks.current'
68 activebookmarklabel = 'bookmarks.active bookmarks.current'
69
69
70 # common command options
70 # common command options
71
71
72 globalopts = [
72 globalopts = [
73 ('R', 'repository', '',
73 ('R', 'repository', '',
74 _('repository root directory or name of overlay bundle file'),
74 _('repository root directory or name of overlay bundle file'),
75 _('REPO')),
75 _('REPO')),
76 ('', 'cwd', '',
76 ('', 'cwd', '',
77 _('change working directory'), _('DIR')),
77 _('change working directory'), _('DIR')),
78 ('y', 'noninteractive', None,
78 ('y', 'noninteractive', None,
79 _('do not prompt, automatically pick the first choice for all prompts')),
79 _('do not prompt, automatically pick the first choice for all prompts')),
80 ('q', 'quiet', None, _('suppress output')),
80 ('q', 'quiet', None, _('suppress output')),
81 ('v', 'verbose', None, _('enable additional output')),
81 ('v', 'verbose', None, _('enable additional output')),
82 ('', 'color', '',
82 ('', 'color', '',
83 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
83 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
84 # and should not be translated
84 # and should not be translated
85 _("when to colorize (boolean, always, auto, never, or debug)"),
85 _("when to colorize (boolean, always, auto, never, or debug)"),
86 _('TYPE')),
86 _('TYPE')),
87 ('', 'config', [],
87 ('', 'config', [],
88 _('set/override config option (use \'section.name=value\')'),
88 _('set/override config option (use \'section.name=value\')'),
89 _('CONFIG')),
89 _('CONFIG')),
90 ('', 'debug', None, _('enable debugging output')),
90 ('', 'debug', None, _('enable debugging output')),
91 ('', 'debugger', None, _('start debugger')),
91 ('', 'debugger', None, _('start debugger')),
92 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
92 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
93 _('ENCODE')),
93 _('ENCODE')),
94 ('', 'encodingmode', encoding.encodingmode,
94 ('', 'encodingmode', encoding.encodingmode,
95 _('set the charset encoding mode'), _('MODE')),
95 _('set the charset encoding mode'), _('MODE')),
96 ('', 'traceback', None, _('always print a traceback on exception')),
96 ('', 'traceback', None, _('always print a traceback on exception')),
97 ('', 'time', None, _('time how long the command takes')),
97 ('', 'time', None, _('time how long the command takes')),
98 ('', 'profile', None, _('print command execution profile')),
98 ('', 'profile', None, _('print command execution profile')),
99 ('', 'version', None, _('output version information and exit')),
99 ('', 'version', None, _('output version information and exit')),
100 ('h', 'help', None, _('display help and exit')),
100 ('h', 'help', None, _('display help and exit')),
101 ('', 'hidden', False, _('consider hidden changesets')),
101 ('', 'hidden', False, _('consider hidden changesets')),
102 ('', 'pager', 'auto',
102 ('', 'pager', 'auto',
103 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
103 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
104 ]
104 ]
105
105
106 dryrunopts = [('n', 'dry-run', None,
106 dryrunopts = [('n', 'dry-run', None,
107 _('do not perform actions, just print output'))]
107 _('do not perform actions, just print output'))]
108
108
109 remoteopts = [
109 remoteopts = [
110 ('e', 'ssh', '',
110 ('e', 'ssh', '',
111 _('specify ssh command to use'), _('CMD')),
111 _('specify ssh command to use'), _('CMD')),
112 ('', 'remotecmd', '',
112 ('', 'remotecmd', '',
113 _('specify hg command to run on the remote side'), _('CMD')),
113 _('specify hg command to run on the remote side'), _('CMD')),
114 ('', 'insecure', None,
114 ('', 'insecure', None,
115 _('do not verify server certificate (ignoring web.cacerts config)')),
115 _('do not verify server certificate (ignoring web.cacerts config)')),
116 ]
116 ]
117
117
118 walkopts = [
118 walkopts = [
119 ('I', 'include', [],
119 ('I', 'include', [],
120 _('include names matching the given patterns'), _('PATTERN')),
120 _('include names matching the given patterns'), _('PATTERN')),
121 ('X', 'exclude', [],
121 ('X', 'exclude', [],
122 _('exclude names matching the given patterns'), _('PATTERN')),
122 _('exclude names matching the given patterns'), _('PATTERN')),
123 ]
123 ]
124
124
125 commitopts = [
125 commitopts = [
126 ('m', 'message', '',
126 ('m', 'message', '',
127 _('use text as commit message'), _('TEXT')),
127 _('use text as commit message'), _('TEXT')),
128 ('l', 'logfile', '',
128 ('l', 'logfile', '',
129 _('read commit message from file'), _('FILE')),
129 _('read commit message from file'), _('FILE')),
130 ]
130 ]
131
131
132 commitopts2 = [
132 commitopts2 = [
133 ('d', 'date', '',
133 ('d', 'date', '',
134 _('record the specified date as commit date'), _('DATE')),
134 _('record the specified date as commit date'), _('DATE')),
135 ('u', 'user', '',
135 ('u', 'user', '',
136 _('record the specified user as committer'), _('USER')),
136 _('record the specified user as committer'), _('USER')),
137 ]
137 ]
138
138
139 # hidden for now
139 # hidden for now
140 formatteropts = [
140 formatteropts = [
141 ('T', 'template', '',
141 ('T', 'template', '',
142 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
142 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
143 ]
143 ]
144
144
145 templateopts = [
145 templateopts = [
146 ('', 'style', '',
146 ('', 'style', '',
147 _('display using template map file (DEPRECATED)'), _('STYLE')),
147 _('display using template map file (DEPRECATED)'), _('STYLE')),
148 ('T', 'template', '',
148 ('T', 'template', '',
149 _('display with template'), _('TEMPLATE')),
149 _('display with template'), _('TEMPLATE')),
150 ]
150 ]
151
151
152 logopts = [
152 logopts = [
153 ('p', 'patch', None, _('show patch')),
153 ('p', 'patch', None, _('show patch')),
154 ('g', 'git', None, _('use git extended diff format')),
154 ('g', 'git', None, _('use git extended diff format')),
155 ('l', 'limit', '',
155 ('l', 'limit', '',
156 _('limit number of changes displayed'), _('NUM')),
156 _('limit number of changes displayed'), _('NUM')),
157 ('M', 'no-merges', None, _('do not show merges')),
157 ('M', 'no-merges', None, _('do not show merges')),
158 ('', 'stat', None, _('output diffstat-style summary of changes')),
158 ('', 'stat', None, _('output diffstat-style summary of changes')),
159 ('G', 'graph', None, _("show the revision DAG")),
159 ('G', 'graph', None, _("show the revision DAG")),
160 ] + templateopts
160 ] + templateopts
161
161
162 diffopts = [
162 diffopts = [
163 ('a', 'text', None, _('treat all files as text')),
163 ('a', 'text', None, _('treat all files as text')),
164 ('g', 'git', None, _('use git extended diff format')),
164 ('g', 'git', None, _('use git extended diff format')),
165 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
165 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
166 ('', 'nodates', None, _('omit dates from diff headers'))
166 ('', 'nodates', None, _('omit dates from diff headers'))
167 ]
167 ]
168
168
169 diffwsopts = [
169 diffwsopts = [
170 ('w', 'ignore-all-space', None,
170 ('w', 'ignore-all-space', None,
171 _('ignore white space when comparing lines')),
171 _('ignore white space when comparing lines')),
172 ('b', 'ignore-space-change', None,
172 ('b', 'ignore-space-change', None,
173 _('ignore changes in the amount of white space')),
173 _('ignore changes in the amount of white space')),
174 ('B', 'ignore-blank-lines', None,
174 ('B', 'ignore-blank-lines', None,
175 _('ignore changes whose lines are all blank')),
175 _('ignore changes whose lines are all blank')),
176 ]
176 ]
177
177
178 diffopts2 = [
178 diffopts2 = [
179 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
179 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
180 ('p', 'show-function', None, _('show which function each change is in')),
180 ('p', 'show-function', None, _('show which function each change is in')),
181 ('', 'reverse', None, _('produce a diff that undoes the changes')),
181 ('', 'reverse', None, _('produce a diff that undoes the changes')),
182 ] + diffwsopts + [
182 ] + diffwsopts + [
183 ('U', 'unified', '',
183 ('U', 'unified', '',
184 _('number of lines of context to show'), _('NUM')),
184 _('number of lines of context to show'), _('NUM')),
185 ('', 'stat', None, _('output diffstat-style summary of changes')),
185 ('', 'stat', None, _('output diffstat-style summary of changes')),
186 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
186 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
187 ]
187 ]
188
188
189 mergetoolopts = [
189 mergetoolopts = [
190 ('t', 'tool', '', _('specify merge tool')),
190 ('t', 'tool', '', _('specify merge tool')),
191 ]
191 ]
192
192
193 similarityopts = [
193 similarityopts = [
194 ('s', 'similarity', '',
194 ('s', 'similarity', '',
195 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
195 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
196 ]
196 ]
197
197
198 subrepoopts = [
198 subrepoopts = [
199 ('S', 'subrepos', None,
199 ('S', 'subrepos', None,
200 _('recurse into subrepositories'))
200 _('recurse into subrepositories'))
201 ]
201 ]
202
202
203 debugrevlogopts = [
203 debugrevlogopts = [
204 ('c', 'changelog', False, _('open changelog')),
204 ('c', 'changelog', False, _('open changelog')),
205 ('m', 'manifest', False, _('open manifest')),
205 ('m', 'manifest', False, _('open manifest')),
206 ('', 'dir', '', _('open directory manifest')),
206 ('', 'dir', '', _('open directory manifest')),
207 ]
207 ]
208
208
209 # Commands start here, listed alphabetically
209 # Commands start here, listed alphabetically
210
210
211 @command('^add',
211 @command('^add',
212 walkopts + subrepoopts + dryrunopts,
212 walkopts + subrepoopts + dryrunopts,
213 _('[OPTION]... [FILE]...'),
213 _('[OPTION]... [FILE]...'),
214 inferrepo=True)
214 inferrepo=True)
215 def add(ui, repo, *pats, **opts):
215 def add(ui, repo, *pats, **opts):
216 """add the specified files on the next commit
216 """add the specified files on the next commit
217
217
218 Schedule files to be version controlled and added to the
218 Schedule files to be version controlled and added to the
219 repository.
219 repository.
220
220
221 The files will be added to the repository at the next commit. To
221 The files will be added to the repository at the next commit. To
222 undo an add before that, see :hg:`forget`.
222 undo an add before that, see :hg:`forget`.
223
223
224 If no names are given, add all files to the repository (except
224 If no names are given, add all files to the repository (except
225 files matching ``.hgignore``).
225 files matching ``.hgignore``).
226
226
227 .. container:: verbose
227 .. container:: verbose
228
228
229 Examples:
229 Examples:
230
230
231 - New (unknown) files are added
231 - New (unknown) files are added
232 automatically by :hg:`add`::
232 automatically by :hg:`add`::
233
233
234 $ ls
234 $ ls
235 foo.c
235 foo.c
236 $ hg status
236 $ hg status
237 ? foo.c
237 ? foo.c
238 $ hg add
238 $ hg add
239 adding foo.c
239 adding foo.c
240 $ hg status
240 $ hg status
241 A foo.c
241 A foo.c
242
242
243 - Specific files to be added can be specified::
243 - Specific files to be added can be specified::
244
244
245 $ ls
245 $ ls
246 bar.c foo.c
246 bar.c foo.c
247 $ hg status
247 $ hg status
248 ? bar.c
248 ? bar.c
249 ? foo.c
249 ? foo.c
250 $ hg add bar.c
250 $ hg add bar.c
251 $ hg status
251 $ hg status
252 A bar.c
252 A bar.c
253 ? foo.c
253 ? foo.c
254
254
255 Returns 0 if all files are successfully added.
255 Returns 0 if all files are successfully added.
256 """
256 """
257
257
258 m = scmutil.match(repo[None], pats, opts)
258 m = scmutil.match(repo[None], pats, opts)
259 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
259 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
260 return rejected and 1 or 0
260 return rejected and 1 or 0
261
261
262 @command('addremove',
262 @command('addremove',
263 similarityopts + subrepoopts + walkopts + dryrunopts,
263 similarityopts + subrepoopts + walkopts + dryrunopts,
264 _('[OPTION]... [FILE]...'),
264 _('[OPTION]... [FILE]...'),
265 inferrepo=True)
265 inferrepo=True)
266 def addremove(ui, repo, *pats, **opts):
266 def addremove(ui, repo, *pats, **opts):
267 """add all new files, delete all missing files
267 """add all new files, delete all missing files
268
268
269 Add all new files and remove all missing files from the
269 Add all new files and remove all missing files from the
270 repository.
270 repository.
271
271
272 Unless names are given, new files are ignored if they match any of
272 Unless names are given, new files are ignored if they match any of
273 the patterns in ``.hgignore``. As with add, these changes take
273 the patterns in ``.hgignore``. As with add, these changes take
274 effect at the next commit.
274 effect at the next commit.
275
275
276 Use the -s/--similarity option to detect renamed files. This
276 Use the -s/--similarity option to detect renamed files. This
277 option takes a percentage between 0 (disabled) and 100 (files must
277 option takes a percentage between 0 (disabled) and 100 (files must
278 be identical) as its parameter. With a parameter greater than 0,
278 be identical) as its parameter. With a parameter greater than 0,
279 this compares every removed file with every added file and records
279 this compares every removed file with every added file and records
280 those similar enough as renames. Detecting renamed files this way
280 those similar enough as renames. Detecting renamed files this way
281 can be expensive. After using this option, :hg:`status -C` can be
281 can be expensive. After using this option, :hg:`status -C` can be
282 used to check which files were identified as moved or renamed. If
282 used to check which files were identified as moved or renamed. If
283 not specified, -s/--similarity defaults to 100 and only renames of
283 not specified, -s/--similarity defaults to 100 and only renames of
284 identical files are detected.
284 identical files are detected.
285
285
286 .. container:: verbose
286 .. container:: verbose
287
287
288 Examples:
288 Examples:
289
289
290 - A number of files (bar.c and foo.c) are new,
290 - A number of files (bar.c and foo.c) are new,
291 while foobar.c has been removed (without using :hg:`remove`)
291 while foobar.c has been removed (without using :hg:`remove`)
292 from the repository::
292 from the repository::
293
293
294 $ ls
294 $ ls
295 bar.c foo.c
295 bar.c foo.c
296 $ hg status
296 $ hg status
297 ! foobar.c
297 ! foobar.c
298 ? bar.c
298 ? bar.c
299 ? foo.c
299 ? foo.c
300 $ hg addremove
300 $ hg addremove
301 adding bar.c
301 adding bar.c
302 adding foo.c
302 adding foo.c
303 removing foobar.c
303 removing foobar.c
304 $ hg status
304 $ hg status
305 A bar.c
305 A bar.c
306 A foo.c
306 A foo.c
307 R foobar.c
307 R foobar.c
308
308
309 - A file foobar.c was moved to foo.c without using :hg:`rename`.
309 - A file foobar.c was moved to foo.c without using :hg:`rename`.
310 Afterwards, it was edited slightly::
310 Afterwards, it was edited slightly::
311
311
312 $ ls
312 $ ls
313 foo.c
313 foo.c
314 $ hg status
314 $ hg status
315 ! foobar.c
315 ! foobar.c
316 ? foo.c
316 ? foo.c
317 $ hg addremove --similarity 90
317 $ hg addremove --similarity 90
318 removing foobar.c
318 removing foobar.c
319 adding foo.c
319 adding foo.c
320 recording removal of foobar.c as rename to foo.c (94% similar)
320 recording removal of foobar.c as rename to foo.c (94% similar)
321 $ hg status -C
321 $ hg status -C
322 A foo.c
322 A foo.c
323 foobar.c
323 foobar.c
324 R foobar.c
324 R foobar.c
325
325
326 Returns 0 if all files are successfully added.
326 Returns 0 if all files are successfully added.
327 """
327 """
328 try:
328 try:
329 sim = float(opts.get('similarity') or 100)
329 sim = float(opts.get('similarity') or 100)
330 except ValueError:
330 except ValueError:
331 raise error.Abort(_('similarity must be a number'))
331 raise error.Abort(_('similarity must be a number'))
332 if sim < 0 or sim > 100:
332 if sim < 0 or sim > 100:
333 raise error.Abort(_('similarity must be between 0 and 100'))
333 raise error.Abort(_('similarity must be between 0 and 100'))
334 matcher = scmutil.match(repo[None], pats, opts)
334 matcher = scmutil.match(repo[None], pats, opts)
335 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
335 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
336
336
337 @command('^annotate|blame',
337 @command('^annotate|blame',
338 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
338 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
339 ('', 'follow', None,
339 ('', 'follow', None,
340 _('follow copies/renames and list the filename (DEPRECATED)')),
340 _('follow copies/renames and list the filename (DEPRECATED)')),
341 ('', 'no-follow', None, _("don't follow copies and renames")),
341 ('', 'no-follow', None, _("don't follow copies and renames")),
342 ('a', 'text', None, _('treat all files as text')),
342 ('a', 'text', None, _('treat all files as text')),
343 ('u', 'user', None, _('list the author (long with -v)')),
343 ('u', 'user', None, _('list the author (long with -v)')),
344 ('f', 'file', None, _('list the filename')),
344 ('f', 'file', None, _('list the filename')),
345 ('d', 'date', None, _('list the date (short with -q)')),
345 ('d', 'date', None, _('list the date (short with -q)')),
346 ('n', 'number', None, _('list the revision number (default)')),
346 ('n', 'number', None, _('list the revision number (default)')),
347 ('c', 'changeset', None, _('list the changeset')),
347 ('c', 'changeset', None, _('list the changeset')),
348 ('l', 'line-number', None, _('show line number at the first appearance'))
348 ('l', 'line-number', None, _('show line number at the first appearance'))
349 ] + diffwsopts + walkopts + formatteropts,
349 ] + diffwsopts + walkopts + formatteropts,
350 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
350 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
351 inferrepo=True)
351 inferrepo=True)
352 def annotate(ui, repo, *pats, **opts):
352 def annotate(ui, repo, *pats, **opts):
353 """show changeset information by line for each file
353 """show changeset information by line for each file
354
354
355 List changes in files, showing the revision id responsible for
355 List changes in files, showing the revision id responsible for
356 each line.
356 each line.
357
357
358 This command is useful for discovering when a change was made and
358 This command is useful for discovering when a change was made and
359 by whom.
359 by whom.
360
360
361 If you include --file, --user, or --date, the revision number is
361 If you include --file, --user, or --date, the revision number is
362 suppressed unless you also include --number.
362 suppressed unless you also include --number.
363
363
364 Without the -a/--text option, annotate will avoid processing files
364 Without the -a/--text option, annotate will avoid processing files
365 it detects as binary. With -a, annotate will annotate the file
365 it detects as binary. With -a, annotate will annotate the file
366 anyway, although the results will probably be neither useful
366 anyway, although the results will probably be neither useful
367 nor desirable.
367 nor desirable.
368
368
369 Returns 0 on success.
369 Returns 0 on success.
370 """
370 """
371 if not pats:
371 if not pats:
372 raise error.Abort(_('at least one filename or pattern is required'))
372 raise error.Abort(_('at least one filename or pattern is required'))
373
373
374 if opts.get('follow'):
374 if opts.get('follow'):
375 # --follow is deprecated and now just an alias for -f/--file
375 # --follow is deprecated and now just an alias for -f/--file
376 # to mimic the behavior of Mercurial before version 1.5
376 # to mimic the behavior of Mercurial before version 1.5
377 opts['file'] = True
377 opts['file'] = True
378
378
379 ctx = scmutil.revsingle(repo, opts.get('rev'))
379 ctx = scmutil.revsingle(repo, opts.get('rev'))
380
380
381 fm = ui.formatter('annotate', opts)
381 fm = ui.formatter('annotate', opts)
382 if ui.quiet:
382 if ui.quiet:
383 datefunc = util.shortdate
383 datefunc = util.shortdate
384 else:
384 else:
385 datefunc = util.datestr
385 datefunc = util.datestr
386 if ctx.rev() is None:
386 if ctx.rev() is None:
387 def hexfn(node):
387 def hexfn(node):
388 if node is None:
388 if node is None:
389 return None
389 return None
390 else:
390 else:
391 return fm.hexfunc(node)
391 return fm.hexfunc(node)
392 if opts.get('changeset'):
392 if opts.get('changeset'):
393 # omit "+" suffix which is appended to node hex
393 # omit "+" suffix which is appended to node hex
394 def formatrev(rev):
394 def formatrev(rev):
395 if rev is None:
395 if rev is None:
396 return '%d' % ctx.p1().rev()
396 return '%d' % ctx.p1().rev()
397 else:
397 else:
398 return '%d' % rev
398 return '%d' % rev
399 else:
399 else:
400 def formatrev(rev):
400 def formatrev(rev):
401 if rev is None:
401 if rev is None:
402 return '%d+' % ctx.p1().rev()
402 return '%d+' % ctx.p1().rev()
403 else:
403 else:
404 return '%d ' % rev
404 return '%d ' % rev
405 def formathex(hex):
405 def formathex(hex):
406 if hex is None:
406 if hex is None:
407 return '%s+' % fm.hexfunc(ctx.p1().node())
407 return '%s+' % fm.hexfunc(ctx.p1().node())
408 else:
408 else:
409 return '%s ' % hex
409 return '%s ' % hex
410 else:
410 else:
411 hexfn = fm.hexfunc
411 hexfn = fm.hexfunc
412 formatrev = formathex = str
412 formatrev = formathex = str
413
413
414 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
414 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
415 ('number', ' ', lambda x: x[0].rev(), formatrev),
415 ('number', ' ', lambda x: x[0].rev(), formatrev),
416 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
416 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
417 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
417 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
418 ('file', ' ', lambda x: x[0].path(), str),
418 ('file', ' ', lambda x: x[0].path(), str),
419 ('line_number', ':', lambda x: x[1], str),
419 ('line_number', ':', lambda x: x[1], str),
420 ]
420 ]
421 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
421 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
422
422
423 if (not opts.get('user') and not opts.get('changeset')
423 if (not opts.get('user') and not opts.get('changeset')
424 and not opts.get('date') and not opts.get('file')):
424 and not opts.get('date') and not opts.get('file')):
425 opts['number'] = True
425 opts['number'] = True
426
426
427 linenumber = opts.get('line_number') is not None
427 linenumber = opts.get('line_number') is not None
428 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
428 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
429 raise error.Abort(_('at least one of -n/-c is required for -l'))
429 raise error.Abort(_('at least one of -n/-c is required for -l'))
430
430
431 ui.pager('annotate')
431 ui.pager('annotate')
432
432
433 if fm.isplain():
433 if fm.isplain():
434 def makefunc(get, fmt):
434 def makefunc(get, fmt):
435 return lambda x: fmt(get(x))
435 return lambda x: fmt(get(x))
436 else:
436 else:
437 def makefunc(get, fmt):
437 def makefunc(get, fmt):
438 return get
438 return get
439 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
439 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
440 if opts.get(op)]
440 if opts.get(op)]
441 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
441 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
442 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
442 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
443 if opts.get(op))
443 if opts.get(op))
444
444
445 def bad(x, y):
445 def bad(x, y):
446 raise error.Abort("%s: %s" % (x, y))
446 raise error.Abort("%s: %s" % (x, y))
447
447
448 m = scmutil.match(ctx, pats, opts, badfn=bad)
448 m = scmutil.match(ctx, pats, opts, badfn=bad)
449
449
450 follow = not opts.get('no_follow')
450 follow = not opts.get('no_follow')
451 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
451 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
452 whitespace=True)
452 whitespace=True)
453 for abs in ctx.walk(m):
453 for abs in ctx.walk(m):
454 fctx = ctx[abs]
454 fctx = ctx[abs]
455 if not opts.get('text') and util.binary(fctx.data()):
455 if not opts.get('text') and util.binary(fctx.data()):
456 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
456 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
457 continue
457 continue
458
458
459 lines = fctx.annotate(follow=follow, linenumber=linenumber,
459 lines = fctx.annotate(follow=follow, linenumber=linenumber,
460 diffopts=diffopts)
460 diffopts=diffopts)
461 if not lines:
461 if not lines:
462 continue
462 continue
463 formats = []
463 formats = []
464 pieces = []
464 pieces = []
465
465
466 for f, sep in funcmap:
466 for f, sep in funcmap:
467 l = [f(n) for n, dummy in lines]
467 l = [f(n) for n, dummy in lines]
468 if fm.isplain():
468 if fm.isplain():
469 sizes = [encoding.colwidth(x) for x in l]
469 sizes = [encoding.colwidth(x) for x in l]
470 ml = max(sizes)
470 ml = max(sizes)
471 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
471 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
472 else:
472 else:
473 formats.append(['%s' for x in l])
473 formats.append(['%s' for x in l])
474 pieces.append(l)
474 pieces.append(l)
475
475
476 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
476 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
477 fm.startitem()
477 fm.startitem()
478 fm.write(fields, "".join(f), *p)
478 fm.write(fields, "".join(f), *p)
479 fm.write('line', ": %s", l[1])
479 fm.write('line', ": %s", l[1])
480
480
481 if not lines[-1][1].endswith('\n'):
481 if not lines[-1][1].endswith('\n'):
482 fm.plain('\n')
482 fm.plain('\n')
483
483
484 fm.end()
484 fm.end()
485
485
486 @command('archive',
486 @command('archive',
487 [('', 'no-decode', None, _('do not pass files through decoders')),
487 [('', 'no-decode', None, _('do not pass files through decoders')),
488 ('p', 'prefix', '', _('directory prefix for files in archive'),
488 ('p', 'prefix', '', _('directory prefix for files in archive'),
489 _('PREFIX')),
489 _('PREFIX')),
490 ('r', 'rev', '', _('revision to distribute'), _('REV')),
490 ('r', 'rev', '', _('revision to distribute'), _('REV')),
491 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
491 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
492 ] + subrepoopts + walkopts,
492 ] + subrepoopts + walkopts,
493 _('[OPTION]... DEST'))
493 _('[OPTION]... DEST'))
494 def archive(ui, repo, dest, **opts):
494 def archive(ui, repo, dest, **opts):
495 '''create an unversioned archive of a repository revision
495 '''create an unversioned archive of a repository revision
496
496
497 By default, the revision used is the parent of the working
497 By default, the revision used is the parent of the working
498 directory; use -r/--rev to specify a different revision.
498 directory; use -r/--rev to specify a different revision.
499
499
500 The archive type is automatically detected based on file
500 The archive type is automatically detected based on file
501 extension (to override, use -t/--type).
501 extension (to override, use -t/--type).
502
502
503 .. container:: verbose
503 .. container:: verbose
504
504
505 Examples:
505 Examples:
506
506
507 - create a zip file containing the 1.0 release::
507 - create a zip file containing the 1.0 release::
508
508
509 hg archive -r 1.0 project-1.0.zip
509 hg archive -r 1.0 project-1.0.zip
510
510
511 - create a tarball excluding .hg files::
511 - create a tarball excluding .hg files::
512
512
513 hg archive project.tar.gz -X ".hg*"
513 hg archive project.tar.gz -X ".hg*"
514
514
515 Valid types are:
515 Valid types are:
516
516
517 :``files``: a directory full of files (default)
517 :``files``: a directory full of files (default)
518 :``tar``: tar archive, uncompressed
518 :``tar``: tar archive, uncompressed
519 :``tbz2``: tar archive, compressed using bzip2
519 :``tbz2``: tar archive, compressed using bzip2
520 :``tgz``: tar archive, compressed using gzip
520 :``tgz``: tar archive, compressed using gzip
521 :``uzip``: zip archive, uncompressed
521 :``uzip``: zip archive, uncompressed
522 :``zip``: zip archive, compressed using deflate
522 :``zip``: zip archive, compressed using deflate
523
523
524 The exact name of the destination archive or directory is given
524 The exact name of the destination archive or directory is given
525 using a format string; see :hg:`help export` for details.
525 using a format string; see :hg:`help export` for details.
526
526
527 Each member added to an archive file has a directory prefix
527 Each member added to an archive file has a directory prefix
528 prepended. Use -p/--prefix to specify a format string for the
528 prepended. Use -p/--prefix to specify a format string for the
529 prefix. The default is the basename of the archive, with suffixes
529 prefix. The default is the basename of the archive, with suffixes
530 removed.
530 removed.
531
531
532 Returns 0 on success.
532 Returns 0 on success.
533 '''
533 '''
534
534
535 ctx = scmutil.revsingle(repo, opts.get('rev'))
535 ctx = scmutil.revsingle(repo, opts.get('rev'))
536 if not ctx:
536 if not ctx:
537 raise error.Abort(_('no working directory: please specify a revision'))
537 raise error.Abort(_('no working directory: please specify a revision'))
538 node = ctx.node()
538 node = ctx.node()
539 dest = cmdutil.makefilename(repo, dest, node)
539 dest = cmdutil.makefilename(repo, dest, node)
540 if os.path.realpath(dest) == repo.root:
540 if os.path.realpath(dest) == repo.root:
541 raise error.Abort(_('repository root cannot be destination'))
541 raise error.Abort(_('repository root cannot be destination'))
542
542
543 kind = opts.get('type') or archival.guesskind(dest) or 'files'
543 kind = opts.get('type') or archival.guesskind(dest) or 'files'
544 prefix = opts.get('prefix')
544 prefix = opts.get('prefix')
545
545
546 if dest == '-':
546 if dest == '-':
547 if kind == 'files':
547 if kind == 'files':
548 raise error.Abort(_('cannot archive plain files to stdout'))
548 raise error.Abort(_('cannot archive plain files to stdout'))
549 dest = cmdutil.makefileobj(repo, dest)
549 dest = cmdutil.makefileobj(repo, dest)
550 if not prefix:
550 if not prefix:
551 prefix = os.path.basename(repo.root) + '-%h'
551 prefix = os.path.basename(repo.root) + '-%h'
552
552
553 prefix = cmdutil.makefilename(repo, prefix, node)
553 prefix = cmdutil.makefilename(repo, prefix, node)
554 matchfn = scmutil.match(ctx, [], opts)
554 matchfn = scmutil.match(ctx, [], opts)
555 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
555 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
556 matchfn, prefix, subrepos=opts.get('subrepos'))
556 matchfn, prefix, subrepos=opts.get('subrepos'))
557
557
558 @command('backout',
558 @command('backout',
559 [('', 'merge', None, _('merge with old dirstate parent after backout')),
559 [('', 'merge', None, _('merge with old dirstate parent after backout')),
560 ('', 'commit', None,
560 ('', 'commit', None,
561 _('commit if no conflicts were encountered (DEPRECATED)')),
561 _('commit if no conflicts were encountered (DEPRECATED)')),
562 ('', 'no-commit', None, _('do not commit')),
562 ('', 'no-commit', None, _('do not commit')),
563 ('', 'parent', '',
563 ('', 'parent', '',
564 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
564 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
565 ('r', 'rev', '', _('revision to backout'), _('REV')),
565 ('r', 'rev', '', _('revision to backout'), _('REV')),
566 ('e', 'edit', False, _('invoke editor on commit messages')),
566 ('e', 'edit', False, _('invoke editor on commit messages')),
567 ] + mergetoolopts + walkopts + commitopts + commitopts2,
567 ] + mergetoolopts + walkopts + commitopts + commitopts2,
568 _('[OPTION]... [-r] REV'))
568 _('[OPTION]... [-r] REV'))
569 def backout(ui, repo, node=None, rev=None, **opts):
569 def backout(ui, repo, node=None, rev=None, **opts):
570 '''reverse effect of earlier changeset
570 '''reverse effect of earlier changeset
571
571
572 Prepare a new changeset with the effect of REV undone in the
572 Prepare a new changeset with the effect of REV undone in the
573 current working directory. If no conflicts were encountered,
573 current working directory. If no conflicts were encountered,
574 it will be committed immediately.
574 it will be committed immediately.
575
575
576 If REV is the parent of the working directory, then this new changeset
576 If REV is the parent of the working directory, then this new changeset
577 is committed automatically (unless --no-commit is specified).
577 is committed automatically (unless --no-commit is specified).
578
578
579 .. note::
579 .. note::
580
580
581 :hg:`backout` cannot be used to fix either an unwanted or
581 :hg:`backout` cannot be used to fix either an unwanted or
582 incorrect merge.
582 incorrect merge.
583
583
584 .. container:: verbose
584 .. container:: verbose
585
585
586 Examples:
586 Examples:
587
587
588 - Reverse the effect of the parent of the working directory.
588 - Reverse the effect of the parent of the working directory.
589 This backout will be committed immediately::
589 This backout will be committed immediately::
590
590
591 hg backout -r .
591 hg backout -r .
592
592
593 - Reverse the effect of previous bad revision 23::
593 - Reverse the effect of previous bad revision 23::
594
594
595 hg backout -r 23
595 hg backout -r 23
596
596
597 - Reverse the effect of previous bad revision 23 and
597 - Reverse the effect of previous bad revision 23 and
598 leave changes uncommitted::
598 leave changes uncommitted::
599
599
600 hg backout -r 23 --no-commit
600 hg backout -r 23 --no-commit
601 hg commit -m "Backout revision 23"
601 hg commit -m "Backout revision 23"
602
602
603 By default, the pending changeset will have one parent,
603 By default, the pending changeset will have one parent,
604 maintaining a linear history. With --merge, the pending
604 maintaining a linear history. With --merge, the pending
605 changeset will instead have two parents: the old parent of the
605 changeset will instead have two parents: the old parent of the
606 working directory and a new child of REV that simply undoes REV.
606 working directory and a new child of REV that simply undoes REV.
607
607
608 Before version 1.7, the behavior without --merge was equivalent
608 Before version 1.7, the behavior without --merge was equivalent
609 to specifying --merge followed by :hg:`update --clean .` to
609 to specifying --merge followed by :hg:`update --clean .` to
610 cancel the merge and leave the child of REV as a head to be
610 cancel the merge and leave the child of REV as a head to be
611 merged separately.
611 merged separately.
612
612
613 See :hg:`help dates` for a list of formats valid for -d/--date.
613 See :hg:`help dates` for a list of formats valid for -d/--date.
614
614
615 See :hg:`help revert` for a way to restore files to the state
615 See :hg:`help revert` for a way to restore files to the state
616 of another revision.
616 of another revision.
617
617
618 Returns 0 on success, 1 if nothing to backout or there are unresolved
618 Returns 0 on success, 1 if nothing to backout or there are unresolved
619 files.
619 files.
620 '''
620 '''
621 wlock = lock = None
621 wlock = lock = None
622 try:
622 try:
623 wlock = repo.wlock()
623 wlock = repo.wlock()
624 lock = repo.lock()
624 lock = repo.lock()
625 return _dobackout(ui, repo, node, rev, **opts)
625 return _dobackout(ui, repo, node, rev, **opts)
626 finally:
626 finally:
627 release(lock, wlock)
627 release(lock, wlock)
628
628
629 def _dobackout(ui, repo, node=None, rev=None, **opts):
629 def _dobackout(ui, repo, node=None, rev=None, **opts):
630 if opts.get('commit') and opts.get('no_commit'):
630 if opts.get('commit') and opts.get('no_commit'):
631 raise error.Abort(_("cannot use --commit with --no-commit"))
631 raise error.Abort(_("cannot use --commit with --no-commit"))
632 if opts.get('merge') and opts.get('no_commit'):
632 if opts.get('merge') and opts.get('no_commit'):
633 raise error.Abort(_("cannot use --merge with --no-commit"))
633 raise error.Abort(_("cannot use --merge with --no-commit"))
634
634
635 if rev and node:
635 if rev and node:
636 raise error.Abort(_("please specify just one revision"))
636 raise error.Abort(_("please specify just one revision"))
637
637
638 if not rev:
638 if not rev:
639 rev = node
639 rev = node
640
640
641 if not rev:
641 if not rev:
642 raise error.Abort(_("please specify a revision to backout"))
642 raise error.Abort(_("please specify a revision to backout"))
643
643
644 date = opts.get('date')
644 date = opts.get('date')
645 if date:
645 if date:
646 opts['date'] = util.parsedate(date)
646 opts['date'] = util.parsedate(date)
647
647
648 cmdutil.checkunfinished(repo)
648 cmdutil.checkunfinished(repo)
649 cmdutil.bailifchanged(repo)
649 cmdutil.bailifchanged(repo)
650 node = scmutil.revsingle(repo, rev).node()
650 node = scmutil.revsingle(repo, rev).node()
651
651
652 op1, op2 = repo.dirstate.parents()
652 op1, op2 = repo.dirstate.parents()
653 if not repo.changelog.isancestor(node, op1):
653 if not repo.changelog.isancestor(node, op1):
654 raise error.Abort(_('cannot backout change that is not an ancestor'))
654 raise error.Abort(_('cannot backout change that is not an ancestor'))
655
655
656 p1, p2 = repo.changelog.parents(node)
656 p1, p2 = repo.changelog.parents(node)
657 if p1 == nullid:
657 if p1 == nullid:
658 raise error.Abort(_('cannot backout a change with no parents'))
658 raise error.Abort(_('cannot backout a change with no parents'))
659 if p2 != nullid:
659 if p2 != nullid:
660 if not opts.get('parent'):
660 if not opts.get('parent'):
661 raise error.Abort(_('cannot backout a merge changeset'))
661 raise error.Abort(_('cannot backout a merge changeset'))
662 p = repo.lookup(opts['parent'])
662 p = repo.lookup(opts['parent'])
663 if p not in (p1, p2):
663 if p not in (p1, p2):
664 raise error.Abort(_('%s is not a parent of %s') %
664 raise error.Abort(_('%s is not a parent of %s') %
665 (short(p), short(node)))
665 (short(p), short(node)))
666 parent = p
666 parent = p
667 else:
667 else:
668 if opts.get('parent'):
668 if opts.get('parent'):
669 raise error.Abort(_('cannot use --parent on non-merge changeset'))
669 raise error.Abort(_('cannot use --parent on non-merge changeset'))
670 parent = p1
670 parent = p1
671
671
672 # the backout should appear on the same branch
672 # the backout should appear on the same branch
673 branch = repo.dirstate.branch()
673 branch = repo.dirstate.branch()
674 bheads = repo.branchheads(branch)
674 bheads = repo.branchheads(branch)
675 rctx = scmutil.revsingle(repo, hex(parent))
675 rctx = scmutil.revsingle(repo, hex(parent))
676 if not opts.get('merge') and op1 != node:
676 if not opts.get('merge') and op1 != node:
677 dsguard = dirstateguard.dirstateguard(repo, 'backout')
677 dsguard = dirstateguard.dirstateguard(repo, 'backout')
678 try:
678 try:
679 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
679 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
680 'backout')
680 'backout')
681 stats = mergemod.update(repo, parent, True, True, node, False)
681 stats = mergemod.update(repo, parent, True, True, node, False)
682 repo.setparents(op1, op2)
682 repo.setparents(op1, op2)
683 dsguard.close()
683 dsguard.close()
684 hg._showstats(repo, stats)
684 hg._showstats(repo, stats)
685 if stats[3]:
685 if stats[3]:
686 repo.ui.status(_("use 'hg resolve' to retry unresolved "
686 repo.ui.status(_("use 'hg resolve' to retry unresolved "
687 "file merges\n"))
687 "file merges\n"))
688 return 1
688 return 1
689 finally:
689 finally:
690 ui.setconfig('ui', 'forcemerge', '', '')
690 ui.setconfig('ui', 'forcemerge', '', '')
691 lockmod.release(dsguard)
691 lockmod.release(dsguard)
692 else:
692 else:
693 hg.clean(repo, node, show_stats=False)
693 hg.clean(repo, node, show_stats=False)
694 repo.dirstate.setbranch(branch)
694 repo.dirstate.setbranch(branch)
695 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
695 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
696
696
697 if opts.get('no_commit'):
697 if opts.get('no_commit'):
698 msg = _("changeset %s backed out, "
698 msg = _("changeset %s backed out, "
699 "don't forget to commit.\n")
699 "don't forget to commit.\n")
700 ui.status(msg % short(node))
700 ui.status(msg % short(node))
701 return 0
701 return 0
702
702
703 def commitfunc(ui, repo, message, match, opts):
703 def commitfunc(ui, repo, message, match, opts):
704 editform = 'backout'
704 editform = 'backout'
705 e = cmdutil.getcommiteditor(editform=editform, **opts)
705 e = cmdutil.getcommiteditor(editform=editform, **opts)
706 if not message:
706 if not message:
707 # we don't translate commit messages
707 # we don't translate commit messages
708 message = "Backed out changeset %s" % short(node)
708 message = "Backed out changeset %s" % short(node)
709 e = cmdutil.getcommiteditor(edit=True, editform=editform)
709 e = cmdutil.getcommiteditor(edit=True, editform=editform)
710 return repo.commit(message, opts.get('user'), opts.get('date'),
710 return repo.commit(message, opts.get('user'), opts.get('date'),
711 match, editor=e)
711 match, editor=e)
712 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
712 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
713 if not newnode:
713 if not newnode:
714 ui.status(_("nothing changed\n"))
714 ui.status(_("nothing changed\n"))
715 return 1
715 return 1
716 cmdutil.commitstatus(repo, newnode, branch, bheads)
716 cmdutil.commitstatus(repo, newnode, branch, bheads)
717
717
718 def nice(node):
718 def nice(node):
719 return '%d:%s' % (repo.changelog.rev(node), short(node))
719 return '%d:%s' % (repo.changelog.rev(node), short(node))
720 ui.status(_('changeset %s backs out changeset %s\n') %
720 ui.status(_('changeset %s backs out changeset %s\n') %
721 (nice(repo.changelog.tip()), nice(node)))
721 (nice(repo.changelog.tip()), nice(node)))
722 if opts.get('merge') and op1 != node:
722 if opts.get('merge') and op1 != node:
723 hg.clean(repo, op1, show_stats=False)
723 hg.clean(repo, op1, show_stats=False)
724 ui.status(_('merging with changeset %s\n')
724 ui.status(_('merging with changeset %s\n')
725 % nice(repo.changelog.tip()))
725 % nice(repo.changelog.tip()))
726 try:
726 try:
727 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
727 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
728 'backout')
728 'backout')
729 return hg.merge(repo, hex(repo.changelog.tip()))
729 return hg.merge(repo, hex(repo.changelog.tip()))
730 finally:
730 finally:
731 ui.setconfig('ui', 'forcemerge', '', '')
731 ui.setconfig('ui', 'forcemerge', '', '')
732 return 0
732 return 0
733
733
734 @command('bisect',
734 @command('bisect',
735 [('r', 'reset', False, _('reset bisect state')),
735 [('r', 'reset', False, _('reset bisect state')),
736 ('g', 'good', False, _('mark changeset good')),
736 ('g', 'good', False, _('mark changeset good')),
737 ('b', 'bad', False, _('mark changeset bad')),
737 ('b', 'bad', False, _('mark changeset bad')),
738 ('s', 'skip', False, _('skip testing changeset')),
738 ('s', 'skip', False, _('skip testing changeset')),
739 ('e', 'extend', False, _('extend the bisect range')),
739 ('e', 'extend', False, _('extend the bisect range')),
740 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
740 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
741 ('U', 'noupdate', False, _('do not update to target'))],
741 ('U', 'noupdate', False, _('do not update to target'))],
742 _("[-gbsr] [-U] [-c CMD] [REV]"))
742 _("[-gbsr] [-U] [-c CMD] [REV]"))
743 def bisect(ui, repo, rev=None, extra=None, command=None,
743 def bisect(ui, repo, rev=None, extra=None, command=None,
744 reset=None, good=None, bad=None, skip=None, extend=None,
744 reset=None, good=None, bad=None, skip=None, extend=None,
745 noupdate=None):
745 noupdate=None):
746 """subdivision search of changesets
746 """subdivision search of changesets
747
747
748 This command helps to find changesets which introduce problems. To
748 This command helps to find changesets which introduce problems. To
749 use, mark the earliest changeset you know exhibits the problem as
749 use, mark the earliest changeset you know exhibits the problem as
750 bad, then mark the latest changeset which is free from the problem
750 bad, then mark the latest changeset which is free from the problem
751 as good. Bisect will update your working directory to a revision
751 as good. Bisect will update your working directory to a revision
752 for testing (unless the -U/--noupdate option is specified). Once
752 for testing (unless the -U/--noupdate option is specified). Once
753 you have performed tests, mark the working directory as good or
753 you have performed tests, mark the working directory as good or
754 bad, and bisect will either update to another candidate changeset
754 bad, and bisect will either update to another candidate changeset
755 or announce that it has found the bad revision.
755 or announce that it has found the bad revision.
756
756
757 As a shortcut, you can also use the revision argument to mark a
757 As a shortcut, you can also use the revision argument to mark a
758 revision as good or bad without checking it out first.
758 revision as good or bad without checking it out first.
759
759
760 If you supply a command, it will be used for automatic bisection.
760 If you supply a command, it will be used for automatic bisection.
761 The environment variable HG_NODE will contain the ID of the
761 The environment variable HG_NODE will contain the ID of the
762 changeset being tested. The exit status of the command will be
762 changeset being tested. The exit status of the command will be
763 used to mark revisions as good or bad: status 0 means good, 125
763 used to mark revisions as good or bad: status 0 means good, 125
764 means to skip the revision, 127 (command not found) will abort the
764 means to skip the revision, 127 (command not found) will abort the
765 bisection, and any other non-zero exit status means the revision
765 bisection, and any other non-zero exit status means the revision
766 is bad.
766 is bad.
767
767
768 .. container:: verbose
768 .. container:: verbose
769
769
770 Some examples:
770 Some examples:
771
771
772 - start a bisection with known bad revision 34, and good revision 12::
772 - start a bisection with known bad revision 34, and good revision 12::
773
773
774 hg bisect --bad 34
774 hg bisect --bad 34
775 hg bisect --good 12
775 hg bisect --good 12
776
776
777 - advance the current bisection by marking current revision as good or
777 - advance the current bisection by marking current revision as good or
778 bad::
778 bad::
779
779
780 hg bisect --good
780 hg bisect --good
781 hg bisect --bad
781 hg bisect --bad
782
782
783 - mark the current revision, or a known revision, to be skipped (e.g. if
783 - mark the current revision, or a known revision, to be skipped (e.g. if
784 that revision is not usable because of another issue)::
784 that revision is not usable because of another issue)::
785
785
786 hg bisect --skip
786 hg bisect --skip
787 hg bisect --skip 23
787 hg bisect --skip 23
788
788
789 - skip all revisions that do not touch directories ``foo`` or ``bar``::
789 - skip all revisions that do not touch directories ``foo`` or ``bar``::
790
790
791 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
791 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
792
792
793 - forget the current bisection::
793 - forget the current bisection::
794
794
795 hg bisect --reset
795 hg bisect --reset
796
796
797 - use 'make && make tests' to automatically find the first broken
797 - use 'make && make tests' to automatically find the first broken
798 revision::
798 revision::
799
799
800 hg bisect --reset
800 hg bisect --reset
801 hg bisect --bad 34
801 hg bisect --bad 34
802 hg bisect --good 12
802 hg bisect --good 12
803 hg bisect --command "make && make tests"
803 hg bisect --command "make && make tests"
804
804
805 - see all changesets whose states are already known in the current
805 - see all changesets whose states are already known in the current
806 bisection::
806 bisection::
807
807
808 hg log -r "bisect(pruned)"
808 hg log -r "bisect(pruned)"
809
809
810 - see the changeset currently being bisected (especially useful
810 - see the changeset currently being bisected (especially useful
811 if running with -U/--noupdate)::
811 if running with -U/--noupdate)::
812
812
813 hg log -r "bisect(current)"
813 hg log -r "bisect(current)"
814
814
815 - see all changesets that took part in the current bisection::
815 - see all changesets that took part in the current bisection::
816
816
817 hg log -r "bisect(range)"
817 hg log -r "bisect(range)"
818
818
819 - you can even get a nice graph::
819 - you can even get a nice graph::
820
820
821 hg log --graph -r "bisect(range)"
821 hg log --graph -r "bisect(range)"
822
822
823 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
823 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
824
824
825 Returns 0 on success.
825 Returns 0 on success.
826 """
826 """
827 # backward compatibility
827 # backward compatibility
828 if rev in "good bad reset init".split():
828 if rev in "good bad reset init".split():
829 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
829 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
830 cmd, rev, extra = rev, extra, None
830 cmd, rev, extra = rev, extra, None
831 if cmd == "good":
831 if cmd == "good":
832 good = True
832 good = True
833 elif cmd == "bad":
833 elif cmd == "bad":
834 bad = True
834 bad = True
835 else:
835 else:
836 reset = True
836 reset = True
837 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
837 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
838 raise error.Abort(_('incompatible arguments'))
838 raise error.Abort(_('incompatible arguments'))
839
839
840 cmdutil.checkunfinished(repo)
840 cmdutil.checkunfinished(repo)
841
841
842 if reset:
842 if reset:
843 hbisect.resetstate(repo)
843 hbisect.resetstate(repo)
844 return
844 return
845
845
846 state = hbisect.load_state(repo)
846 state = hbisect.load_state(repo)
847
847
848 # update state
848 # update state
849 if good or bad or skip:
849 if good or bad or skip:
850 if rev:
850 if rev:
851 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
851 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
852 else:
852 else:
853 nodes = [repo.lookup('.')]
853 nodes = [repo.lookup('.')]
854 if good:
854 if good:
855 state['good'] += nodes
855 state['good'] += nodes
856 elif bad:
856 elif bad:
857 state['bad'] += nodes
857 state['bad'] += nodes
858 elif skip:
858 elif skip:
859 state['skip'] += nodes
859 state['skip'] += nodes
860 hbisect.save_state(repo, state)
860 hbisect.save_state(repo, state)
861 if not (state['good'] and state['bad']):
861 if not (state['good'] and state['bad']):
862 return
862 return
863
863
864 def mayupdate(repo, node, show_stats=True):
864 def mayupdate(repo, node, show_stats=True):
865 """common used update sequence"""
865 """common used update sequence"""
866 if noupdate:
866 if noupdate:
867 return
867 return
868 cmdutil.bailifchanged(repo)
868 cmdutil.bailifchanged(repo)
869 return hg.clean(repo, node, show_stats=show_stats)
869 return hg.clean(repo, node, show_stats=show_stats)
870
870
871 displayer = cmdutil.show_changeset(ui, repo, {})
871 displayer = cmdutil.show_changeset(ui, repo, {})
872
872
873 if command:
873 if command:
874 changesets = 1
874 changesets = 1
875 if noupdate:
875 if noupdate:
876 try:
876 try:
877 node = state['current'][0]
877 node = state['current'][0]
878 except LookupError:
878 except LookupError:
879 raise error.Abort(_('current bisect revision is unknown - '
879 raise error.Abort(_('current bisect revision is unknown - '
880 'start a new bisect to fix'))
880 'start a new bisect to fix'))
881 else:
881 else:
882 node, p2 = repo.dirstate.parents()
882 node, p2 = repo.dirstate.parents()
883 if p2 != nullid:
883 if p2 != nullid:
884 raise error.Abort(_('current bisect revision is a merge'))
884 raise error.Abort(_('current bisect revision is a merge'))
885 if rev:
885 if rev:
886 node = repo[scmutil.revsingle(repo, rev, node)].node()
886 node = repo[scmutil.revsingle(repo, rev, node)].node()
887 try:
887 try:
888 while changesets:
888 while changesets:
889 # update state
889 # update state
890 state['current'] = [node]
890 state['current'] = [node]
891 hbisect.save_state(repo, state)
891 hbisect.save_state(repo, state)
892 status = ui.system(command, environ={'HG_NODE': hex(node)},
892 status = ui.system(command, environ={'HG_NODE': hex(node)},
893 blockedtag='bisect_check')
893 blockedtag='bisect_check')
894 if status == 125:
894 if status == 125:
895 transition = "skip"
895 transition = "skip"
896 elif status == 0:
896 elif status == 0:
897 transition = "good"
897 transition = "good"
898 # status < 0 means process was killed
898 # status < 0 means process was killed
899 elif status == 127:
899 elif status == 127:
900 raise error.Abort(_("failed to execute %s") % command)
900 raise error.Abort(_("failed to execute %s") % command)
901 elif status < 0:
901 elif status < 0:
902 raise error.Abort(_("%s killed") % command)
902 raise error.Abort(_("%s killed") % command)
903 else:
903 else:
904 transition = "bad"
904 transition = "bad"
905 state[transition].append(node)
905 state[transition].append(node)
906 ctx = repo[node]
906 ctx = repo[node]
907 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
907 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
908 hbisect.checkstate(state)
908 hbisect.checkstate(state)
909 # bisect
909 # bisect
910 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
910 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
911 # update to next check
911 # update to next check
912 node = nodes[0]
912 node = nodes[0]
913 mayupdate(repo, node, show_stats=False)
913 mayupdate(repo, node, show_stats=False)
914 finally:
914 finally:
915 state['current'] = [node]
915 state['current'] = [node]
916 hbisect.save_state(repo, state)
916 hbisect.save_state(repo, state)
917 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
917 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
918 return
918 return
919
919
920 hbisect.checkstate(state)
920 hbisect.checkstate(state)
921
921
922 # actually bisect
922 # actually bisect
923 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
923 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
924 if extend:
924 if extend:
925 if not changesets:
925 if not changesets:
926 extendnode = hbisect.extendrange(repo, state, nodes, good)
926 extendnode = hbisect.extendrange(repo, state, nodes, good)
927 if extendnode is not None:
927 if extendnode is not None:
928 ui.write(_("Extending search to changeset %d:%s\n")
928 ui.write(_("Extending search to changeset %d:%s\n")
929 % (extendnode.rev(), extendnode))
929 % (extendnode.rev(), extendnode))
930 state['current'] = [extendnode.node()]
930 state['current'] = [extendnode.node()]
931 hbisect.save_state(repo, state)
931 hbisect.save_state(repo, state)
932 return mayupdate(repo, extendnode.node())
932 return mayupdate(repo, extendnode.node())
933 raise error.Abort(_("nothing to extend"))
933 raise error.Abort(_("nothing to extend"))
934
934
935 if changesets == 0:
935 if changesets == 0:
936 hbisect.printresult(ui, repo, state, displayer, nodes, good)
936 hbisect.printresult(ui, repo, state, displayer, nodes, good)
937 else:
937 else:
938 assert len(nodes) == 1 # only a single node can be tested next
938 assert len(nodes) == 1 # only a single node can be tested next
939 node = nodes[0]
939 node = nodes[0]
940 # compute the approximate number of remaining tests
940 # compute the approximate number of remaining tests
941 tests, size = 0, 2
941 tests, size = 0, 2
942 while size <= changesets:
942 while size <= changesets:
943 tests, size = tests + 1, size * 2
943 tests, size = tests + 1, size * 2
944 rev = repo.changelog.rev(node)
944 rev = repo.changelog.rev(node)
945 ui.write(_("Testing changeset %d:%s "
945 ui.write(_("Testing changeset %d:%s "
946 "(%d changesets remaining, ~%d tests)\n")
946 "(%d changesets remaining, ~%d tests)\n")
947 % (rev, short(node), changesets, tests))
947 % (rev, short(node), changesets, tests))
948 state['current'] = [node]
948 state['current'] = [node]
949 hbisect.save_state(repo, state)
949 hbisect.save_state(repo, state)
950 return mayupdate(repo, node)
950 return mayupdate(repo, node)
951
951
952 @command('bookmarks|bookmark',
952 @command('bookmarks|bookmark',
953 [('f', 'force', False, _('force')),
953 [('f', 'force', False, _('force')),
954 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
954 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
955 ('d', 'delete', False, _('delete a given bookmark')),
955 ('d', 'delete', False, _('delete a given bookmark')),
956 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
956 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
957 ('i', 'inactive', False, _('mark a bookmark inactive')),
957 ('i', 'inactive', False, _('mark a bookmark inactive')),
958 ] + formatteropts,
958 ] + formatteropts,
959 _('hg bookmarks [OPTIONS]... [NAME]...'))
959 _('hg bookmarks [OPTIONS]... [NAME]...'))
960 def bookmark(ui, repo, *names, **opts):
960 def bookmark(ui, repo, *names, **opts):
961 '''create a new bookmark or list existing bookmarks
961 '''create a new bookmark or list existing bookmarks
962
962
963 Bookmarks are labels on changesets to help track lines of development.
963 Bookmarks are labels on changesets to help track lines of development.
964 Bookmarks are unversioned and can be moved, renamed and deleted.
964 Bookmarks are unversioned and can be moved, renamed and deleted.
965 Deleting or moving a bookmark has no effect on the associated changesets.
965 Deleting or moving a bookmark has no effect on the associated changesets.
966
966
967 Creating or updating to a bookmark causes it to be marked as 'active'.
967 Creating or updating to a bookmark causes it to be marked as 'active'.
968 The active bookmark is indicated with a '*'.
968 The active bookmark is indicated with a '*'.
969 When a commit is made, the active bookmark will advance to the new commit.
969 When a commit is made, the active bookmark will advance to the new commit.
970 A plain :hg:`update` will also advance an active bookmark, if possible.
970 A plain :hg:`update` will also advance an active bookmark, if possible.
971 Updating away from a bookmark will cause it to be deactivated.
971 Updating away from a bookmark will cause it to be deactivated.
972
972
973 Bookmarks can be pushed and pulled between repositories (see
973 Bookmarks can be pushed and pulled between repositories (see
974 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
974 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
975 diverged, a new 'divergent bookmark' of the form 'name@path' will
975 diverged, a new 'divergent bookmark' of the form 'name@path' will
976 be created. Using :hg:`merge` will resolve the divergence.
976 be created. Using :hg:`merge` will resolve the divergence.
977
977
978 A bookmark named '@' has the special property that :hg:`clone` will
978 A bookmark named '@' has the special property that :hg:`clone` will
979 check it out by default if it exists.
979 check it out by default if it exists.
980
980
981 .. container:: verbose
981 .. container:: verbose
982
982
983 Examples:
983 Examples:
984
984
985 - create an active bookmark for a new line of development::
985 - create an active bookmark for a new line of development::
986
986
987 hg book new-feature
987 hg book new-feature
988
988
989 - create an inactive bookmark as a place marker::
989 - create an inactive bookmark as a place marker::
990
990
991 hg book -i reviewed
991 hg book -i reviewed
992
992
993 - create an inactive bookmark on another changeset::
993 - create an inactive bookmark on another changeset::
994
994
995 hg book -r .^ tested
995 hg book -r .^ tested
996
996
997 - rename bookmark turkey to dinner::
997 - rename bookmark turkey to dinner::
998
998
999 hg book -m turkey dinner
999 hg book -m turkey dinner
1000
1000
1001 - move the '@' bookmark from another branch::
1001 - move the '@' bookmark from another branch::
1002
1002
1003 hg book -f @
1003 hg book -f @
1004 '''
1004 '''
1005 force = opts.get('force')
1005 force = opts.get('force')
1006 rev = opts.get('rev')
1006 rev = opts.get('rev')
1007 delete = opts.get('delete')
1007 delete = opts.get('delete')
1008 rename = opts.get('rename')
1008 rename = opts.get('rename')
1009 inactive = opts.get('inactive')
1009 inactive = opts.get('inactive')
1010
1010
1011 def checkformat(mark):
1011 def checkformat(mark):
1012 mark = mark.strip()
1012 mark = mark.strip()
1013 if not mark:
1013 if not mark:
1014 raise error.Abort(_("bookmark names cannot consist entirely of "
1014 raise error.Abort(_("bookmark names cannot consist entirely of "
1015 "whitespace"))
1015 "whitespace"))
1016 scmutil.checknewlabel(repo, mark, 'bookmark')
1016 scmutil.checknewlabel(repo, mark, 'bookmark')
1017 return mark
1017 return mark
1018
1018
1019 def checkconflict(repo, mark, cur, force=False, target=None):
1019 def checkconflict(repo, mark, cur, force=False, target=None):
1020 if mark in marks and not force:
1020 if mark in marks and not force:
1021 if target:
1021 if target:
1022 if marks[mark] == target and target == cur:
1022 if marks[mark] == target and target == cur:
1023 # re-activating a bookmark
1023 # re-activating a bookmark
1024 return
1024 return
1025 anc = repo.changelog.ancestors([repo[target].rev()])
1025 anc = repo.changelog.ancestors([repo[target].rev()])
1026 bmctx = repo[marks[mark]]
1026 bmctx = repo[marks[mark]]
1027 divs = [repo[b].node() for b in marks
1027 divs = [repo[b].node() for b in marks
1028 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
1028 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
1029
1029
1030 # allow resolving a single divergent bookmark even if moving
1030 # allow resolving a single divergent bookmark even if moving
1031 # the bookmark across branches when a revision is specified
1031 # the bookmark across branches when a revision is specified
1032 # that contains a divergent bookmark
1032 # that contains a divergent bookmark
1033 if bmctx.rev() not in anc and target in divs:
1033 if bmctx.rev() not in anc and target in divs:
1034 bookmarks.deletedivergent(repo, [target], mark)
1034 bookmarks.deletedivergent(repo, [target], mark)
1035 return
1035 return
1036
1036
1037 deletefrom = [b for b in divs
1037 deletefrom = [b for b in divs
1038 if repo[b].rev() in anc or b == target]
1038 if repo[b].rev() in anc or b == target]
1039 bookmarks.deletedivergent(repo, deletefrom, mark)
1039 bookmarks.deletedivergent(repo, deletefrom, mark)
1040 if bookmarks.validdest(repo, bmctx, repo[target]):
1040 if bookmarks.validdest(repo, bmctx, repo[target]):
1041 ui.status(_("moving bookmark '%s' forward from %s\n") %
1041 ui.status(_("moving bookmark '%s' forward from %s\n") %
1042 (mark, short(bmctx.node())))
1042 (mark, short(bmctx.node())))
1043 return
1043 return
1044 raise error.Abort(_("bookmark '%s' already exists "
1044 raise error.Abort(_("bookmark '%s' already exists "
1045 "(use -f to force)") % mark)
1045 "(use -f to force)") % mark)
1046 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
1046 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
1047 and not force):
1047 and not force):
1048 raise error.Abort(
1048 raise error.Abort(
1049 _("a bookmark cannot have the name of an existing branch"))
1049 _("a bookmark cannot have the name of an existing branch"))
1050
1050
1051 if delete and rename:
1051 if delete and rename:
1052 raise error.Abort(_("--delete and --rename are incompatible"))
1052 raise error.Abort(_("--delete and --rename are incompatible"))
1053 if delete and rev:
1053 if delete and rev:
1054 raise error.Abort(_("--rev is incompatible with --delete"))
1054 raise error.Abort(_("--rev is incompatible with --delete"))
1055 if rename and rev:
1055 if rename and rev:
1056 raise error.Abort(_("--rev is incompatible with --rename"))
1056 raise error.Abort(_("--rev is incompatible with --rename"))
1057 if not names and (delete or rev):
1057 if not names and (delete or rev):
1058 raise error.Abort(_("bookmark name required"))
1058 raise error.Abort(_("bookmark name required"))
1059
1059
1060 if delete or rename or names or inactive:
1060 if delete or rename or names or inactive:
1061 wlock = lock = tr = None
1061 wlock = lock = tr = None
1062 try:
1062 try:
1063 wlock = repo.wlock()
1063 wlock = repo.wlock()
1064 lock = repo.lock()
1064 lock = repo.lock()
1065 cur = repo.changectx('.').node()
1065 cur = repo.changectx('.').node()
1066 marks = repo._bookmarks
1066 marks = repo._bookmarks
1067 if delete:
1067 if delete:
1068 tr = repo.transaction('bookmark')
1068 tr = repo.transaction('bookmark')
1069 for mark in names:
1069 for mark in names:
1070 if mark not in marks:
1070 if mark not in marks:
1071 raise error.Abort(_("bookmark '%s' does not exist") %
1071 raise error.Abort(_("bookmark '%s' does not exist") %
1072 mark)
1072 mark)
1073 if mark == repo._activebookmark:
1073 if mark == repo._activebookmark:
1074 bookmarks.deactivate(repo)
1074 bookmarks.deactivate(repo)
1075 del marks[mark]
1075 del marks[mark]
1076
1076
1077 elif rename:
1077 elif rename:
1078 tr = repo.transaction('bookmark')
1078 tr = repo.transaction('bookmark')
1079 if not names:
1079 if not names:
1080 raise error.Abort(_("new bookmark name required"))
1080 raise error.Abort(_("new bookmark name required"))
1081 elif len(names) > 1:
1081 elif len(names) > 1:
1082 raise error.Abort(_("only one new bookmark name allowed"))
1082 raise error.Abort(_("only one new bookmark name allowed"))
1083 mark = checkformat(names[0])
1083 mark = checkformat(names[0])
1084 if rename not in marks:
1084 if rename not in marks:
1085 raise error.Abort(_("bookmark '%s' does not exist")
1085 raise error.Abort(_("bookmark '%s' does not exist")
1086 % rename)
1086 % rename)
1087 checkconflict(repo, mark, cur, force)
1087 checkconflict(repo, mark, cur, force)
1088 marks[mark] = marks[rename]
1088 marks[mark] = marks[rename]
1089 if repo._activebookmark == rename and not inactive:
1089 if repo._activebookmark == rename and not inactive:
1090 bookmarks.activate(repo, mark)
1090 bookmarks.activate(repo, mark)
1091 del marks[rename]
1091 del marks[rename]
1092 elif names:
1092 elif names:
1093 tr = repo.transaction('bookmark')
1093 tr = repo.transaction('bookmark')
1094 newact = None
1094 newact = None
1095 for mark in names:
1095 for mark in names:
1096 mark = checkformat(mark)
1096 mark = checkformat(mark)
1097 if newact is None:
1097 if newact is None:
1098 newact = mark
1098 newact = mark
1099 if inactive and mark == repo._activebookmark:
1099 if inactive and mark == repo._activebookmark:
1100 bookmarks.deactivate(repo)
1100 bookmarks.deactivate(repo)
1101 return
1101 return
1102 tgt = cur
1102 tgt = cur
1103 if rev:
1103 if rev:
1104 tgt = scmutil.revsingle(repo, rev).node()
1104 tgt = scmutil.revsingle(repo, rev).node()
1105 checkconflict(repo, mark, cur, force, tgt)
1105 checkconflict(repo, mark, cur, force, tgt)
1106 marks[mark] = tgt
1106 marks[mark] = tgt
1107 if not inactive and cur == marks[newact] and not rev:
1107 if not inactive and cur == marks[newact] and not rev:
1108 bookmarks.activate(repo, newact)
1108 bookmarks.activate(repo, newact)
1109 elif cur != tgt and newact == repo._activebookmark:
1109 elif cur != tgt and newact == repo._activebookmark:
1110 bookmarks.deactivate(repo)
1110 bookmarks.deactivate(repo)
1111 elif inactive:
1111 elif inactive:
1112 if len(marks) == 0:
1112 if len(marks) == 0:
1113 ui.status(_("no bookmarks set\n"))
1113 ui.status(_("no bookmarks set\n"))
1114 elif not repo._activebookmark:
1114 elif not repo._activebookmark:
1115 ui.status(_("no active bookmark\n"))
1115 ui.status(_("no active bookmark\n"))
1116 else:
1116 else:
1117 bookmarks.deactivate(repo)
1117 bookmarks.deactivate(repo)
1118 if tr is not None:
1118 if tr is not None:
1119 marks.recordchange(tr)
1119 marks.recordchange(tr)
1120 tr.close()
1120 tr.close()
1121 finally:
1121 finally:
1122 lockmod.release(tr, lock, wlock)
1122 lockmod.release(tr, lock, wlock)
1123 else: # show bookmarks
1123 else: # show bookmarks
1124 fm = ui.formatter('bookmarks', opts)
1124 fm = ui.formatter('bookmarks', opts)
1125 hexfn = fm.hexfunc
1125 hexfn = fm.hexfunc
1126 marks = repo._bookmarks
1126 marks = repo._bookmarks
1127 if len(marks) == 0 and fm.isplain():
1127 if len(marks) == 0 and fm.isplain():
1128 ui.status(_("no bookmarks set\n"))
1128 ui.status(_("no bookmarks set\n"))
1129 for bmark, n in sorted(marks.iteritems()):
1129 for bmark, n in sorted(marks.iteritems()):
1130 active = repo._activebookmark
1130 active = repo._activebookmark
1131 if bmark == active:
1131 if bmark == active:
1132 prefix, label = '*', activebookmarklabel
1132 prefix, label = '*', activebookmarklabel
1133 else:
1133 else:
1134 prefix, label = ' ', ''
1134 prefix, label = ' ', ''
1135
1135
1136 fm.startitem()
1136 fm.startitem()
1137 if not ui.quiet:
1137 if not ui.quiet:
1138 fm.plain(' %s ' % prefix, label=label)
1138 fm.plain(' %s ' % prefix, label=label)
1139 fm.write('bookmark', '%s', bmark, label=label)
1139 fm.write('bookmark', '%s', bmark, label=label)
1140 pad = " " * (25 - encoding.colwidth(bmark))
1140 pad = " " * (25 - encoding.colwidth(bmark))
1141 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1141 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1142 repo.changelog.rev(n), hexfn(n), label=label)
1142 repo.changelog.rev(n), hexfn(n), label=label)
1143 fm.data(active=(bmark == active))
1143 fm.data(active=(bmark == active))
1144 fm.plain('\n')
1144 fm.plain('\n')
1145 fm.end()
1145 fm.end()
1146
1146
1147 @command('branch',
1147 @command('branch',
1148 [('f', 'force', None,
1148 [('f', 'force', None,
1149 _('set branch name even if it shadows an existing branch')),
1149 _('set branch name even if it shadows an existing branch')),
1150 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1150 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1151 _('[-fC] [NAME]'))
1151 _('[-fC] [NAME]'))
1152 def branch(ui, repo, label=None, **opts):
1152 def branch(ui, repo, label=None, **opts):
1153 """set or show the current branch name
1153 """set or show the current branch name
1154
1154
1155 .. note::
1155 .. note::
1156
1156
1157 Branch names are permanent and global. Use :hg:`bookmark` to create a
1157 Branch names are permanent and global. Use :hg:`bookmark` to create a
1158 light-weight bookmark instead. See :hg:`help glossary` for more
1158 light-weight bookmark instead. See :hg:`help glossary` for more
1159 information about named branches and bookmarks.
1159 information about named branches and bookmarks.
1160
1160
1161 With no argument, show the current branch name. With one argument,
1161 With no argument, show the current branch name. With one argument,
1162 set the working directory branch name (the branch will not exist
1162 set the working directory branch name (the branch will not exist
1163 in the repository until the next commit). Standard practice
1163 in the repository until the next commit). Standard practice
1164 recommends that primary development take place on the 'default'
1164 recommends that primary development take place on the 'default'
1165 branch.
1165 branch.
1166
1166
1167 Unless -f/--force is specified, branch will not let you set a
1167 Unless -f/--force is specified, branch will not let you set a
1168 branch name that already exists.
1168 branch name that already exists.
1169
1169
1170 Use -C/--clean to reset the working directory branch to that of
1170 Use -C/--clean to reset the working directory branch to that of
1171 the parent of the working directory, negating a previous branch
1171 the parent of the working directory, negating a previous branch
1172 change.
1172 change.
1173
1173
1174 Use the command :hg:`update` to switch to an existing branch. Use
1174 Use the command :hg:`update` to switch to an existing branch. Use
1175 :hg:`commit --close-branch` to mark this branch head as closed.
1175 :hg:`commit --close-branch` to mark this branch head as closed.
1176 When all heads of a branch are closed, the branch will be
1176 When all heads of a branch are closed, the branch will be
1177 considered closed.
1177 considered closed.
1178
1178
1179 Returns 0 on success.
1179 Returns 0 on success.
1180 """
1180 """
1181 if label:
1181 if label:
1182 label = label.strip()
1182 label = label.strip()
1183
1183
1184 if not opts.get('clean') and not label:
1184 if not opts.get('clean') and not label:
1185 ui.write("%s\n" % repo.dirstate.branch())
1185 ui.write("%s\n" % repo.dirstate.branch())
1186 return
1186 return
1187
1187
1188 with repo.wlock():
1188 with repo.wlock():
1189 if opts.get('clean'):
1189 if opts.get('clean'):
1190 label = repo[None].p1().branch()
1190 label = repo[None].p1().branch()
1191 repo.dirstate.setbranch(label)
1191 repo.dirstate.setbranch(label)
1192 ui.status(_('reset working directory to branch %s\n') % label)
1192 ui.status(_('reset working directory to branch %s\n') % label)
1193 elif label:
1193 elif label:
1194 if not opts.get('force') and label in repo.branchmap():
1194 if not opts.get('force') and label in repo.branchmap():
1195 if label not in [p.branch() for p in repo[None].parents()]:
1195 if label not in [p.branch() for p in repo[None].parents()]:
1196 raise error.Abort(_('a branch of the same name already'
1196 raise error.Abort(_('a branch of the same name already'
1197 ' exists'),
1197 ' exists'),
1198 # i18n: "it" refers to an existing branch
1198 # i18n: "it" refers to an existing branch
1199 hint=_("use 'hg update' to switch to it"))
1199 hint=_("use 'hg update' to switch to it"))
1200 scmutil.checknewlabel(repo, label, 'branch')
1200 scmutil.checknewlabel(repo, label, 'branch')
1201 repo.dirstate.setbranch(label)
1201 repo.dirstate.setbranch(label)
1202 ui.status(_('marked working directory as branch %s\n') % label)
1202 ui.status(_('marked working directory as branch %s\n') % label)
1203
1203
1204 # find any open named branches aside from default
1204 # find any open named branches aside from default
1205 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1205 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1206 if n != "default" and not c]
1206 if n != "default" and not c]
1207 if not others:
1207 if not others:
1208 ui.status(_('(branches are permanent and global, '
1208 ui.status(_('(branches are permanent and global, '
1209 'did you want a bookmark?)\n'))
1209 'did you want a bookmark?)\n'))
1210
1210
1211 @command('branches',
1211 @command('branches',
1212 [('a', 'active', False,
1212 [('a', 'active', False,
1213 _('show only branches that have unmerged heads (DEPRECATED)')),
1213 _('show only branches that have unmerged heads (DEPRECATED)')),
1214 ('c', 'closed', False, _('show normal and closed branches')),
1214 ('c', 'closed', False, _('show normal and closed branches')),
1215 ] + formatteropts,
1215 ] + formatteropts,
1216 _('[-c]'))
1216 _('[-c]'))
1217 def branches(ui, repo, active=False, closed=False, **opts):
1217 def branches(ui, repo, active=False, closed=False, **opts):
1218 """list repository named branches
1218 """list repository named branches
1219
1219
1220 List the repository's named branches, indicating which ones are
1220 List the repository's named branches, indicating which ones are
1221 inactive. If -c/--closed is specified, also list branches which have
1221 inactive. If -c/--closed is specified, also list branches which have
1222 been marked closed (see :hg:`commit --close-branch`).
1222 been marked closed (see :hg:`commit --close-branch`).
1223
1223
1224 Use the command :hg:`update` to switch to an existing branch.
1224 Use the command :hg:`update` to switch to an existing branch.
1225
1225
1226 Returns 0.
1226 Returns 0.
1227 """
1227 """
1228
1228
1229 ui.pager('branches')
1229 ui.pager('branches')
1230 fm = ui.formatter('branches', opts)
1230 fm = ui.formatter('branches', opts)
1231 hexfunc = fm.hexfunc
1231 hexfunc = fm.hexfunc
1232
1232
1233 allheads = set(repo.heads())
1233 allheads = set(repo.heads())
1234 branches = []
1234 branches = []
1235 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1235 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1236 isactive = not isclosed and bool(set(heads) & allheads)
1236 isactive = not isclosed and bool(set(heads) & allheads)
1237 branches.append((tag, repo[tip], isactive, not isclosed))
1237 branches.append((tag, repo[tip], isactive, not isclosed))
1238 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1238 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1239 reverse=True)
1239 reverse=True)
1240
1240
1241 for tag, ctx, isactive, isopen in branches:
1241 for tag, ctx, isactive, isopen in branches:
1242 if active and not isactive:
1242 if active and not isactive:
1243 continue
1243 continue
1244 if isactive:
1244 if isactive:
1245 label = 'branches.active'
1245 label = 'branches.active'
1246 notice = ''
1246 notice = ''
1247 elif not isopen:
1247 elif not isopen:
1248 if not closed:
1248 if not closed:
1249 continue
1249 continue
1250 label = 'branches.closed'
1250 label = 'branches.closed'
1251 notice = _(' (closed)')
1251 notice = _(' (closed)')
1252 else:
1252 else:
1253 label = 'branches.inactive'
1253 label = 'branches.inactive'
1254 notice = _(' (inactive)')
1254 notice = _(' (inactive)')
1255 current = (tag == repo.dirstate.branch())
1255 current = (tag == repo.dirstate.branch())
1256 if current:
1256 if current:
1257 label = 'branches.current'
1257 label = 'branches.current'
1258
1258
1259 fm.startitem()
1259 fm.startitem()
1260 fm.write('branch', '%s', tag, label=label)
1260 fm.write('branch', '%s', tag, label=label)
1261 rev = ctx.rev()
1261 rev = ctx.rev()
1262 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1262 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1263 fmt = ' ' * padsize + ' %d:%s'
1263 fmt = ' ' * padsize + ' %d:%s'
1264 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1264 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1265 label='log.changeset changeset.%s' % ctx.phasestr())
1265 label='log.changeset changeset.%s' % ctx.phasestr())
1266 fm.context(ctx=ctx)
1266 fm.context(ctx=ctx)
1267 fm.data(active=isactive, closed=not isopen, current=current)
1267 fm.data(active=isactive, closed=not isopen, current=current)
1268 if not ui.quiet:
1268 if not ui.quiet:
1269 fm.plain(notice)
1269 fm.plain(notice)
1270 fm.plain('\n')
1270 fm.plain('\n')
1271 fm.end()
1271 fm.end()
1272
1272
1273 @command('bundle',
1273 @command('bundle',
1274 [('f', 'force', None, _('run even when the destination is unrelated')),
1274 [('f', 'force', None, _('run even when the destination is unrelated')),
1275 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1275 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1276 _('REV')),
1276 _('REV')),
1277 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1277 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1278 _('BRANCH')),
1278 _('BRANCH')),
1279 ('', 'base', [],
1279 ('', 'base', [],
1280 _('a base changeset assumed to be available at the destination'),
1280 _('a base changeset assumed to be available at the destination'),
1281 _('REV')),
1281 _('REV')),
1282 ('a', 'all', None, _('bundle all changesets in the repository')),
1282 ('a', 'all', None, _('bundle all changesets in the repository')),
1283 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1283 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1284 ] + remoteopts,
1284 ] + remoteopts,
1285 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1285 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1286 def bundle(ui, repo, fname, dest=None, **opts):
1286 def bundle(ui, repo, fname, dest=None, **opts):
1287 """create a bundle file
1287 """create a bundle file
1288
1288
1289 Generate a bundle file containing data to be added to a repository.
1289 Generate a bundle file containing data to be added to a repository.
1290
1290
1291 To create a bundle containing all changesets, use -a/--all
1291 To create a bundle containing all changesets, use -a/--all
1292 (or --base null). Otherwise, hg assumes the destination will have
1292 (or --base null). Otherwise, hg assumes the destination will have
1293 all the nodes you specify with --base parameters. Otherwise, hg
1293 all the nodes you specify with --base parameters. Otherwise, hg
1294 will assume the repository has all the nodes in destination, or
1294 will assume the repository has all the nodes in destination, or
1295 default-push/default if no destination is specified.
1295 default-push/default if no destination is specified.
1296
1296
1297 You can change bundle format with the -t/--type option. See
1297 You can change bundle format with the -t/--type option. See
1298 :hg:`help bundlespec` for documentation on this format. By default,
1298 :hg:`help bundlespec` for documentation on this format. By default,
1299 the most appropriate format is used and compression defaults to
1299 the most appropriate format is used and compression defaults to
1300 bzip2.
1300 bzip2.
1301
1301
1302 The bundle file can then be transferred using conventional means
1302 The bundle file can then be transferred using conventional means
1303 and applied to another repository with the unbundle or pull
1303 and applied to another repository with the unbundle or pull
1304 command. This is useful when direct push and pull are not
1304 command. This is useful when direct push and pull are not
1305 available or when exporting an entire repository is undesirable.
1305 available or when exporting an entire repository is undesirable.
1306
1306
1307 Applying bundles preserves all changeset contents including
1307 Applying bundles preserves all changeset contents including
1308 permissions, copy/rename information, and revision history.
1308 permissions, copy/rename information, and revision history.
1309
1309
1310 Returns 0 on success, 1 if no changes found.
1310 Returns 0 on success, 1 if no changes found.
1311 """
1311 """
1312 revs = None
1312 revs = None
1313 if 'rev' in opts:
1313 if 'rev' in opts:
1314 revstrings = opts['rev']
1314 revstrings = opts['rev']
1315 revs = scmutil.revrange(repo, revstrings)
1315 revs = scmutil.revrange(repo, revstrings)
1316 if revstrings and not revs:
1316 if revstrings and not revs:
1317 raise error.Abort(_('no commits to bundle'))
1317 raise error.Abort(_('no commits to bundle'))
1318
1318
1319 bundletype = opts.get('type', 'bzip2').lower()
1319 bundletype = opts.get('type', 'bzip2').lower()
1320 try:
1320 try:
1321 bcompression, cgversion, params = exchange.parsebundlespec(
1321 bcompression, cgversion, params = exchange.parsebundlespec(
1322 repo, bundletype, strict=False)
1322 repo, bundletype, strict=False)
1323 except error.UnsupportedBundleSpecification as e:
1323 except error.UnsupportedBundleSpecification as e:
1324 raise error.Abort(str(e),
1324 raise error.Abort(str(e),
1325 hint=_("see 'hg help bundlespec' for supported "
1325 hint=_("see 'hg help bundlespec' for supported "
1326 "values for --type"))
1326 "values for --type"))
1327
1327
1328 # Packed bundles are a pseudo bundle format for now.
1328 # Packed bundles are a pseudo bundle format for now.
1329 if cgversion == 's1':
1329 if cgversion == 's1':
1330 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1330 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1331 hint=_("use 'hg debugcreatestreamclonebundle'"))
1331 hint=_("use 'hg debugcreatestreamclonebundle'"))
1332
1332
1333 if opts.get('all'):
1333 if opts.get('all'):
1334 if dest:
1334 if dest:
1335 raise error.Abort(_("--all is incompatible with specifying "
1335 raise error.Abort(_("--all is incompatible with specifying "
1336 "a destination"))
1336 "a destination"))
1337 if opts.get('base'):
1337 if opts.get('base'):
1338 ui.warn(_("ignoring --base because --all was specified\n"))
1338 ui.warn(_("ignoring --base because --all was specified\n"))
1339 base = ['null']
1339 base = ['null']
1340 else:
1340 else:
1341 base = scmutil.revrange(repo, opts.get('base'))
1341 base = scmutil.revrange(repo, opts.get('base'))
1342 # TODO: get desired bundlecaps from command line.
1342 # TODO: get desired bundlecaps from command line.
1343 bundlecaps = None
1343 bundlecaps = None
1344 if cgversion not in changegroup.supportedoutgoingversions(repo):
1344 if cgversion not in changegroup.supportedoutgoingversions(repo):
1345 raise error.Abort(_("repository does not support bundle version %s") %
1345 raise error.Abort(_("repository does not support bundle version %s") %
1346 cgversion)
1346 cgversion)
1347
1347
1348 if base:
1348 if base:
1349 if dest:
1349 if dest:
1350 raise error.Abort(_("--base is incompatible with specifying "
1350 raise error.Abort(_("--base is incompatible with specifying "
1351 "a destination"))
1351 "a destination"))
1352 common = [repo.lookup(rev) for rev in base]
1352 common = [repo.lookup(rev) for rev in base]
1353 heads = revs and map(repo.lookup, revs) or None
1353 heads = revs and map(repo.lookup, revs) or None
1354 outgoing = discovery.outgoing(repo, common, heads)
1354 outgoing = discovery.outgoing(repo, common, heads)
1355 cg = changegroup.getchangegroup(repo, 'bundle', outgoing,
1355 cg = changegroup.getchangegroup(repo, 'bundle', outgoing,
1356 bundlecaps=bundlecaps,
1356 bundlecaps=bundlecaps,
1357 version=cgversion)
1357 version=cgversion)
1358 outgoing = None
1358 outgoing = None
1359 else:
1359 else:
1360 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1360 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1361 dest, branches = hg.parseurl(dest, opts.get('branch'))
1361 dest, branches = hg.parseurl(dest, opts.get('branch'))
1362 other = hg.peer(repo, opts, dest)
1362 other = hg.peer(repo, opts, dest)
1363 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1363 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1364 heads = revs and map(repo.lookup, revs) or revs
1364 heads = revs and map(repo.lookup, revs) or revs
1365 outgoing = discovery.findcommonoutgoing(repo, other,
1365 outgoing = discovery.findcommonoutgoing(repo, other,
1366 onlyheads=heads,
1366 onlyheads=heads,
1367 force=opts.get('force'),
1367 force=opts.get('force'),
1368 portable=True)
1368 portable=True)
1369 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1369 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1370 bundlecaps, version=cgversion)
1370 bundlecaps, version=cgversion)
1371 if not cg:
1371 if not cg:
1372 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1372 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1373 return 1
1373 return 1
1374
1374
1375 if cgversion == '01': #bundle1
1375 if cgversion == '01': #bundle1
1376 if bcompression is None:
1376 if bcompression is None:
1377 bcompression = 'UN'
1377 bcompression = 'UN'
1378 bversion = 'HG10' + bcompression
1378 bversion = 'HG10' + bcompression
1379 bcompression = None
1379 bcompression = None
1380 elif cgversion in ('02', '03'):
1380 elif cgversion in ('02', '03'):
1381 bversion = 'HG20'
1381 bversion = 'HG20'
1382 else:
1382 else:
1383 raise error.ProgrammingError(
1383 raise error.ProgrammingError(
1384 'bundle: unexpected changegroup version %s' % cgversion)
1384 'bundle: unexpected changegroup version %s' % cgversion)
1385
1385
1386 # TODO compression options should be derived from bundlespec parsing.
1386 # TODO compression options should be derived from bundlespec parsing.
1387 # This is a temporary hack to allow adjusting bundle compression
1387 # This is a temporary hack to allow adjusting bundle compression
1388 # level without a) formalizing the bundlespec changes to declare it
1388 # level without a) formalizing the bundlespec changes to declare it
1389 # b) introducing a command flag.
1389 # b) introducing a command flag.
1390 compopts = {}
1390 compopts = {}
1391 complevel = ui.configint('experimental', 'bundlecomplevel')
1391 complevel = ui.configint('experimental', 'bundlecomplevel')
1392 if complevel is not None:
1392 if complevel is not None:
1393 compopts['level'] = complevel
1393 compopts['level'] = complevel
1394
1394
1395 bundle2.writebundle(ui, cg, fname, bversion, compression=bcompression,
1395 bundle2.writebundle(ui, cg, fname, bversion, compression=bcompression,
1396 compopts=compopts)
1396 compopts=compopts)
1397
1397
1398 @command('cat',
1398 @command('cat',
1399 [('o', 'output', '',
1399 [('o', 'output', '',
1400 _('print output to file with formatted name'), _('FORMAT')),
1400 _('print output to file with formatted name'), _('FORMAT')),
1401 ('r', 'rev', '', _('print the given revision'), _('REV')),
1401 ('r', 'rev', '', _('print the given revision'), _('REV')),
1402 ('', 'decode', None, _('apply any matching decode filter')),
1402 ('', 'decode', None, _('apply any matching decode filter')),
1403 ] + walkopts,
1403 ] + walkopts,
1404 _('[OPTION]... FILE...'),
1404 _('[OPTION]... FILE...'),
1405 inferrepo=True)
1405 inferrepo=True)
1406 def cat(ui, repo, file1, *pats, **opts):
1406 def cat(ui, repo, file1, *pats, **opts):
1407 """output the current or given revision of files
1407 """output the current or given revision of files
1408
1408
1409 Print the specified files as they were at the given revision. If
1409 Print the specified files as they were at the given revision. If
1410 no revision is given, the parent of the working directory is used.
1410 no revision is given, the parent of the working directory is used.
1411
1411
1412 Output may be to a file, in which case the name of the file is
1412 Output may be to a file, in which case the name of the file is
1413 given using a format string. The formatting rules as follows:
1413 given using a format string. The formatting rules as follows:
1414
1414
1415 :``%%``: literal "%" character
1415 :``%%``: literal "%" character
1416 :``%s``: basename of file being printed
1416 :``%s``: basename of file being printed
1417 :``%d``: dirname of file being printed, or '.' if in repository root
1417 :``%d``: dirname of file being printed, or '.' if in repository root
1418 :``%p``: root-relative path name of file being printed
1418 :``%p``: root-relative path name of file being printed
1419 :``%H``: changeset hash (40 hexadecimal digits)
1419 :``%H``: changeset hash (40 hexadecimal digits)
1420 :``%R``: changeset revision number
1420 :``%R``: changeset revision number
1421 :``%h``: short-form changeset hash (12 hexadecimal digits)
1421 :``%h``: short-form changeset hash (12 hexadecimal digits)
1422 :``%r``: zero-padded changeset revision number
1422 :``%r``: zero-padded changeset revision number
1423 :``%b``: basename of the exporting repository
1423 :``%b``: basename of the exporting repository
1424
1424
1425 Returns 0 on success.
1425 Returns 0 on success.
1426 """
1426 """
1427 ctx = scmutil.revsingle(repo, opts.get('rev'))
1427 ctx = scmutil.revsingle(repo, opts.get('rev'))
1428 m = scmutil.match(ctx, (file1,) + pats, opts)
1428 m = scmutil.match(ctx, (file1,) + pats, opts)
1429
1429
1430 ui.pager('cat')
1430 ui.pager('cat')
1431 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1431 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1432
1432
1433 @command('^clone',
1433 @command('^clone',
1434 [('U', 'noupdate', None, _('the clone will include an empty working '
1434 [('U', 'noupdate', None, _('the clone will include an empty working '
1435 'directory (only a repository)')),
1435 'directory (only a repository)')),
1436 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1436 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1437 _('REV')),
1437 _('REV')),
1438 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1438 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1439 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1439 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1440 ('', 'pull', None, _('use pull protocol to copy metadata')),
1440 ('', 'pull', None, _('use pull protocol to copy metadata')),
1441 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1441 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1442 ] + remoteopts,
1442 ] + remoteopts,
1443 _('[OPTION]... SOURCE [DEST]'),
1443 _('[OPTION]... SOURCE [DEST]'),
1444 norepo=True)
1444 norepo=True)
1445 def clone(ui, source, dest=None, **opts):
1445 def clone(ui, source, dest=None, **opts):
1446 """make a copy of an existing repository
1446 """make a copy of an existing repository
1447
1447
1448 Create a copy of an existing repository in a new directory.
1448 Create a copy of an existing repository in a new directory.
1449
1449
1450 If no destination directory name is specified, it defaults to the
1450 If no destination directory name is specified, it defaults to the
1451 basename of the source.
1451 basename of the source.
1452
1452
1453 The location of the source is added to the new repository's
1453 The location of the source is added to the new repository's
1454 ``.hg/hgrc`` file, as the default to be used for future pulls.
1454 ``.hg/hgrc`` file, as the default to be used for future pulls.
1455
1455
1456 Only local paths and ``ssh://`` URLs are supported as
1456 Only local paths and ``ssh://`` URLs are supported as
1457 destinations. For ``ssh://`` destinations, no working directory or
1457 destinations. For ``ssh://`` destinations, no working directory or
1458 ``.hg/hgrc`` will be created on the remote side.
1458 ``.hg/hgrc`` will be created on the remote side.
1459
1459
1460 If the source repository has a bookmark called '@' set, that
1460 If the source repository has a bookmark called '@' set, that
1461 revision will be checked out in the new repository by default.
1461 revision will be checked out in the new repository by default.
1462
1462
1463 To check out a particular version, use -u/--update, or
1463 To check out a particular version, use -u/--update, or
1464 -U/--noupdate to create a clone with no working directory.
1464 -U/--noupdate to create a clone with no working directory.
1465
1465
1466 To pull only a subset of changesets, specify one or more revisions
1466 To pull only a subset of changesets, specify one or more revisions
1467 identifiers with -r/--rev or branches with -b/--branch. The
1467 identifiers with -r/--rev or branches with -b/--branch. The
1468 resulting clone will contain only the specified changesets and
1468 resulting clone will contain only the specified changesets and
1469 their ancestors. These options (or 'clone src#rev dest') imply
1469 their ancestors. These options (or 'clone src#rev dest') imply
1470 --pull, even for local source repositories.
1470 --pull, even for local source repositories.
1471
1471
1472 .. note::
1472 .. note::
1473
1473
1474 Specifying a tag will include the tagged changeset but not the
1474 Specifying a tag will include the tagged changeset but not the
1475 changeset containing the tag.
1475 changeset containing the tag.
1476
1476
1477 .. container:: verbose
1477 .. container:: verbose
1478
1478
1479 For efficiency, hardlinks are used for cloning whenever the
1479 For efficiency, hardlinks are used for cloning whenever the
1480 source and destination are on the same filesystem (note this
1480 source and destination are on the same filesystem (note this
1481 applies only to the repository data, not to the working
1481 applies only to the repository data, not to the working
1482 directory). Some filesystems, such as AFS, implement hardlinking
1482 directory). Some filesystems, such as AFS, implement hardlinking
1483 incorrectly, but do not report errors. In these cases, use the
1483 incorrectly, but do not report errors. In these cases, use the
1484 --pull option to avoid hardlinking.
1484 --pull option to avoid hardlinking.
1485
1485
1486 In some cases, you can clone repositories and the working
1486 In some cases, you can clone repositories and the working
1487 directory using full hardlinks with ::
1487 directory using full hardlinks with ::
1488
1488
1489 $ cp -al REPO REPOCLONE
1489 $ cp -al REPO REPOCLONE
1490
1490
1491 This is the fastest way to clone, but it is not always safe. The
1491 This is the fastest way to clone, but it is not always safe. The
1492 operation is not atomic (making sure REPO is not modified during
1492 operation is not atomic (making sure REPO is not modified during
1493 the operation is up to you) and you have to make sure your
1493 the operation is up to you) and you have to make sure your
1494 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1494 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1495 so). Also, this is not compatible with certain extensions that
1495 so). Also, this is not compatible with certain extensions that
1496 place their metadata under the .hg directory, such as mq.
1496 place their metadata under the .hg directory, such as mq.
1497
1497
1498 Mercurial will update the working directory to the first applicable
1498 Mercurial will update the working directory to the first applicable
1499 revision from this list:
1499 revision from this list:
1500
1500
1501 a) null if -U or the source repository has no changesets
1501 a) null if -U or the source repository has no changesets
1502 b) if -u . and the source repository is local, the first parent of
1502 b) if -u . and the source repository is local, the first parent of
1503 the source repository's working directory
1503 the source repository's working directory
1504 c) the changeset specified with -u (if a branch name, this means the
1504 c) the changeset specified with -u (if a branch name, this means the
1505 latest head of that branch)
1505 latest head of that branch)
1506 d) the changeset specified with -r
1506 d) the changeset specified with -r
1507 e) the tipmost head specified with -b
1507 e) the tipmost head specified with -b
1508 f) the tipmost head specified with the url#branch source syntax
1508 f) the tipmost head specified with the url#branch source syntax
1509 g) the revision marked with the '@' bookmark, if present
1509 g) the revision marked with the '@' bookmark, if present
1510 h) the tipmost head of the default branch
1510 h) the tipmost head of the default branch
1511 i) tip
1511 i) tip
1512
1512
1513 When cloning from servers that support it, Mercurial may fetch
1513 When cloning from servers that support it, Mercurial may fetch
1514 pre-generated data from a server-advertised URL. When this is done,
1514 pre-generated data from a server-advertised URL. When this is done,
1515 hooks operating on incoming changesets and changegroups may fire twice,
1515 hooks operating on incoming changesets and changegroups may fire twice,
1516 once for the bundle fetched from the URL and another for any additional
1516 once for the bundle fetched from the URL and another for any additional
1517 data not fetched from this URL. In addition, if an error occurs, the
1517 data not fetched from this URL. In addition, if an error occurs, the
1518 repository may be rolled back to a partial clone. This behavior may
1518 repository may be rolled back to a partial clone. This behavior may
1519 change in future releases. See :hg:`help -e clonebundles` for more.
1519 change in future releases. See :hg:`help -e clonebundles` for more.
1520
1520
1521 Examples:
1521 Examples:
1522
1522
1523 - clone a remote repository to a new directory named hg/::
1523 - clone a remote repository to a new directory named hg/::
1524
1524
1525 hg clone https://www.mercurial-scm.org/repo/hg/
1525 hg clone https://www.mercurial-scm.org/repo/hg/
1526
1526
1527 - create a lightweight local clone::
1527 - create a lightweight local clone::
1528
1528
1529 hg clone project/ project-feature/
1529 hg clone project/ project-feature/
1530
1530
1531 - clone from an absolute path on an ssh server (note double-slash)::
1531 - clone from an absolute path on an ssh server (note double-slash)::
1532
1532
1533 hg clone ssh://user@server//home/projects/alpha/
1533 hg clone ssh://user@server//home/projects/alpha/
1534
1534
1535 - do a high-speed clone over a LAN while checking out a
1535 - do a high-speed clone over a LAN while checking out a
1536 specified version::
1536 specified version::
1537
1537
1538 hg clone --uncompressed http://server/repo -u 1.5
1538 hg clone --uncompressed http://server/repo -u 1.5
1539
1539
1540 - create a repository without changesets after a particular revision::
1540 - create a repository without changesets after a particular revision::
1541
1541
1542 hg clone -r 04e544 experimental/ good/
1542 hg clone -r 04e544 experimental/ good/
1543
1543
1544 - clone (and track) a particular named branch::
1544 - clone (and track) a particular named branch::
1545
1545
1546 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1546 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1547
1547
1548 See :hg:`help urls` for details on specifying URLs.
1548 See :hg:`help urls` for details on specifying URLs.
1549
1549
1550 Returns 0 on success.
1550 Returns 0 on success.
1551 """
1551 """
1552 if opts.get('noupdate') and opts.get('updaterev'):
1552 if opts.get('noupdate') and opts.get('updaterev'):
1553 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1553 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1554
1554
1555 r = hg.clone(ui, opts, source, dest,
1555 r = hg.clone(ui, opts, source, dest,
1556 pull=opts.get('pull'),
1556 pull=opts.get('pull'),
1557 stream=opts.get('uncompressed'),
1557 stream=opts.get('uncompressed'),
1558 rev=opts.get('rev'),
1558 rev=opts.get('rev'),
1559 update=opts.get('updaterev') or not opts.get('noupdate'),
1559 update=opts.get('updaterev') or not opts.get('noupdate'),
1560 branch=opts.get('branch'),
1560 branch=opts.get('branch'),
1561 shareopts=opts.get('shareopts'))
1561 shareopts=opts.get('shareopts'))
1562
1562
1563 return r is None
1563 return r is None
1564
1564
1565 @command('^commit|ci',
1565 @command('^commit|ci',
1566 [('A', 'addremove', None,
1566 [('A', 'addremove', None,
1567 _('mark new/missing files as added/removed before committing')),
1567 _('mark new/missing files as added/removed before committing')),
1568 ('', 'close-branch', None,
1568 ('', 'close-branch', None,
1569 _('mark a branch head as closed')),
1569 _('mark a branch head as closed')),
1570 ('', 'amend', None, _('amend the parent of the working directory')),
1570 ('', 'amend', None, _('amend the parent of the working directory')),
1571 ('s', 'secret', None, _('use the secret phase for committing')),
1571 ('s', 'secret', None, _('use the secret phase for committing')),
1572 ('e', 'edit', None, _('invoke editor on commit messages')),
1572 ('e', 'edit', None, _('invoke editor on commit messages')),
1573 ('i', 'interactive', None, _('use interactive mode')),
1573 ('i', 'interactive', None, _('use interactive mode')),
1574 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1574 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1575 _('[OPTION]... [FILE]...'),
1575 _('[OPTION]... [FILE]...'),
1576 inferrepo=True)
1576 inferrepo=True)
1577 def commit(ui, repo, *pats, **opts):
1577 def commit(ui, repo, *pats, **opts):
1578 """commit the specified files or all outstanding changes
1578 """commit the specified files or all outstanding changes
1579
1579
1580 Commit changes to the given files into the repository. Unlike a
1580 Commit changes to the given files into the repository. Unlike a
1581 centralized SCM, this operation is a local operation. See
1581 centralized SCM, this operation is a local operation. See
1582 :hg:`push` for a way to actively distribute your changes.
1582 :hg:`push` for a way to actively distribute your changes.
1583
1583
1584 If a list of files is omitted, all changes reported by :hg:`status`
1584 If a list of files is omitted, all changes reported by :hg:`status`
1585 will be committed.
1585 will be committed.
1586
1586
1587 If you are committing the result of a merge, do not provide any
1587 If you are committing the result of a merge, do not provide any
1588 filenames or -I/-X filters.
1588 filenames or -I/-X filters.
1589
1589
1590 If no commit message is specified, Mercurial starts your
1590 If no commit message is specified, Mercurial starts your
1591 configured editor where you can enter a message. In case your
1591 configured editor where you can enter a message. In case your
1592 commit fails, you will find a backup of your message in
1592 commit fails, you will find a backup of your message in
1593 ``.hg/last-message.txt``.
1593 ``.hg/last-message.txt``.
1594
1594
1595 The --close-branch flag can be used to mark the current branch
1595 The --close-branch flag can be used to mark the current branch
1596 head closed. When all heads of a branch are closed, the branch
1596 head closed. When all heads of a branch are closed, the branch
1597 will be considered closed and no longer listed.
1597 will be considered closed and no longer listed.
1598
1598
1599 The --amend flag can be used to amend the parent of the
1599 The --amend flag can be used to amend the parent of the
1600 working directory with a new commit that contains the changes
1600 working directory with a new commit that contains the changes
1601 in the parent in addition to those currently reported by :hg:`status`,
1601 in the parent in addition to those currently reported by :hg:`status`,
1602 if there are any. The old commit is stored in a backup bundle in
1602 if there are any. The old commit is stored in a backup bundle in
1603 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1603 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1604 on how to restore it).
1604 on how to restore it).
1605
1605
1606 Message, user and date are taken from the amended commit unless
1606 Message, user and date are taken from the amended commit unless
1607 specified. When a message isn't specified on the command line,
1607 specified. When a message isn't specified on the command line,
1608 the editor will open with the message of the amended commit.
1608 the editor will open with the message of the amended commit.
1609
1609
1610 It is not possible to amend public changesets (see :hg:`help phases`)
1610 It is not possible to amend public changesets (see :hg:`help phases`)
1611 or changesets that have children.
1611 or changesets that have children.
1612
1612
1613 See :hg:`help dates` for a list of formats valid for -d/--date.
1613 See :hg:`help dates` for a list of formats valid for -d/--date.
1614
1614
1615 Returns 0 on success, 1 if nothing changed.
1615 Returns 0 on success, 1 if nothing changed.
1616
1616
1617 .. container:: verbose
1617 .. container:: verbose
1618
1618
1619 Examples:
1619 Examples:
1620
1620
1621 - commit all files ending in .py::
1621 - commit all files ending in .py::
1622
1622
1623 hg commit --include "set:**.py"
1623 hg commit --include "set:**.py"
1624
1624
1625 - commit all non-binary files::
1625 - commit all non-binary files::
1626
1626
1627 hg commit --exclude "set:binary()"
1627 hg commit --exclude "set:binary()"
1628
1628
1629 - amend the current commit and set the date to now::
1629 - amend the current commit and set the date to now::
1630
1630
1631 hg commit --amend --date now
1631 hg commit --amend --date now
1632 """
1632 """
1633 wlock = lock = None
1633 wlock = lock = None
1634 try:
1634 try:
1635 wlock = repo.wlock()
1635 wlock = repo.wlock()
1636 lock = repo.lock()
1636 lock = repo.lock()
1637 return _docommit(ui, repo, *pats, **opts)
1637 return _docommit(ui, repo, *pats, **opts)
1638 finally:
1638 finally:
1639 release(lock, wlock)
1639 release(lock, wlock)
1640
1640
1641 def _docommit(ui, repo, *pats, **opts):
1641 def _docommit(ui, repo, *pats, **opts):
1642 opts = pycompat.byteskwargs(opts)
1642 opts = pycompat.byteskwargs(opts)
1643 if opts.get('interactive'):
1643 if opts.get('interactive'):
1644 opts.pop('interactive')
1644 opts.pop('interactive')
1645 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1645 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1646 cmdutil.recordfilter, *pats,
1646 cmdutil.recordfilter, *pats,
1647 **pycompat.strkwargs(opts))
1647 **pycompat.strkwargs(opts))
1648 # ret can be 0 (no changes to record) or the value returned by
1648 # ret can be 0 (no changes to record) or the value returned by
1649 # commit(), 1 if nothing changed or None on success.
1649 # commit(), 1 if nothing changed or None on success.
1650 return 1 if ret == 0 else ret
1650 return 1 if ret == 0 else ret
1651
1651
1652 if opts.get('subrepos'):
1652 if opts.get('subrepos'):
1653 if opts.get('amend'):
1653 if opts.get('amend'):
1654 raise error.Abort(_('cannot amend with --subrepos'))
1654 raise error.Abort(_('cannot amend with --subrepos'))
1655 # Let --subrepos on the command line override config setting.
1655 # Let --subrepos on the command line override config setting.
1656 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1656 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1657
1657
1658 cmdutil.checkunfinished(repo, commit=True)
1658 cmdutil.checkunfinished(repo, commit=True)
1659
1659
1660 branch = repo[None].branch()
1660 branch = repo[None].branch()
1661 bheads = repo.branchheads(branch)
1661 bheads = repo.branchheads(branch)
1662
1662
1663 extra = {}
1663 extra = {}
1664 if opts.get('close_branch'):
1664 if opts.get('close_branch'):
1665 extra['close'] = 1
1665 extra['close'] = 1
1666
1666
1667 if not bheads:
1667 if not bheads:
1668 raise error.Abort(_('can only close branch heads'))
1668 raise error.Abort(_('can only close branch heads'))
1669 elif opts.get('amend'):
1669 elif opts.get('amend'):
1670 if repo[None].parents()[0].p1().branch() != branch and \
1670 if repo[None].parents()[0].p1().branch() != branch and \
1671 repo[None].parents()[0].p2().branch() != branch:
1671 repo[None].parents()[0].p2().branch() != branch:
1672 raise error.Abort(_('can only close branch heads'))
1672 raise error.Abort(_('can only close branch heads'))
1673
1673
1674 if opts.get('amend'):
1674 if opts.get('amend'):
1675 if ui.configbool('ui', 'commitsubrepos'):
1675 if ui.configbool('ui', 'commitsubrepos'):
1676 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1676 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1677
1677
1678 old = repo['.']
1678 old = repo['.']
1679 if not old.mutable():
1679 if not old.mutable():
1680 raise error.Abort(_('cannot amend public changesets'))
1680 raise error.Abort(_('cannot amend public changesets'))
1681 if len(repo[None].parents()) > 1:
1681 if len(repo[None].parents()) > 1:
1682 raise error.Abort(_('cannot amend while merging'))
1682 raise error.Abort(_('cannot amend while merging'))
1683 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1683 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1684 if not allowunstable and old.children():
1684 if not allowunstable and old.children():
1685 raise error.Abort(_('cannot amend changeset with children'))
1685 raise error.Abort(_('cannot amend changeset with children'))
1686
1686
1687 # Currently histedit gets confused if an amend happens while histedit
1687 # Currently histedit gets confused if an amend happens while histedit
1688 # is in progress. Since we have a checkunfinished command, we are
1688 # is in progress. Since we have a checkunfinished command, we are
1689 # temporarily honoring it.
1689 # temporarily honoring it.
1690 #
1690 #
1691 # Note: eventually this guard will be removed. Please do not expect
1691 # Note: eventually this guard will be removed. Please do not expect
1692 # this behavior to remain.
1692 # this behavior to remain.
1693 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1693 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1694 cmdutil.checkunfinished(repo)
1694 cmdutil.checkunfinished(repo)
1695
1695
1696 # commitfunc is used only for temporary amend commit by cmdutil.amend
1696 # commitfunc is used only for temporary amend commit by cmdutil.amend
1697 def commitfunc(ui, repo, message, match, opts):
1697 def commitfunc(ui, repo, message, match, opts):
1698 return repo.commit(message,
1698 return repo.commit(message,
1699 opts.get('user') or old.user(),
1699 opts.get('user') or old.user(),
1700 opts.get('date') or old.date(),
1700 opts.get('date') or old.date(),
1701 match,
1701 match,
1702 extra=extra)
1702 extra=extra)
1703
1703
1704 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1704 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1705 if node == old.node():
1705 if node == old.node():
1706 ui.status(_("nothing changed\n"))
1706 ui.status(_("nothing changed\n"))
1707 return 1
1707 return 1
1708 else:
1708 else:
1709 def commitfunc(ui, repo, message, match, opts):
1709 def commitfunc(ui, repo, message, match, opts):
1710 overrides = {}
1710 overrides = {}
1711 if opts.get('secret'):
1711 if opts.get('secret'):
1712 overrides[('phases', 'new-commit')] = 'secret'
1712 overrides[('phases', 'new-commit')] = 'secret'
1713
1713
1714 baseui = repo.baseui
1714 baseui = repo.baseui
1715 with baseui.configoverride(overrides, 'commit'):
1715 with baseui.configoverride(overrides, 'commit'):
1716 with ui.configoverride(overrides, 'commit'):
1716 with ui.configoverride(overrides, 'commit'):
1717 editform = cmdutil.mergeeditform(repo[None],
1717 editform = cmdutil.mergeeditform(repo[None],
1718 'commit.normal')
1718 'commit.normal')
1719 editor = cmdutil.getcommiteditor(
1719 editor = cmdutil.getcommiteditor(
1720 editform=editform, **pycompat.strkwargs(opts))
1720 editform=editform, **pycompat.strkwargs(opts))
1721 return repo.commit(message,
1721 return repo.commit(message,
1722 opts.get('user'),
1722 opts.get('user'),
1723 opts.get('date'),
1723 opts.get('date'),
1724 match,
1724 match,
1725 editor=editor,
1725 editor=editor,
1726 extra=extra)
1726 extra=extra)
1727
1727
1728 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1728 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1729
1729
1730 if not node:
1730 if not node:
1731 stat = cmdutil.postcommitstatus(repo, pats, opts)
1731 stat = cmdutil.postcommitstatus(repo, pats, opts)
1732 if stat[3]:
1732 if stat[3]:
1733 ui.status(_("nothing changed (%d missing files, see "
1733 ui.status(_("nothing changed (%d missing files, see "
1734 "'hg status')\n") % len(stat[3]))
1734 "'hg status')\n") % len(stat[3]))
1735 else:
1735 else:
1736 ui.status(_("nothing changed\n"))
1736 ui.status(_("nothing changed\n"))
1737 return 1
1737 return 1
1738
1738
1739 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1739 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1740
1740
1741 @command('config|showconfig|debugconfig',
1741 @command('config|showconfig|debugconfig',
1742 [('u', 'untrusted', None, _('show untrusted configuration options')),
1742 [('u', 'untrusted', None, _('show untrusted configuration options')),
1743 ('e', 'edit', None, _('edit user config')),
1743 ('e', 'edit', None, _('edit user config')),
1744 ('l', 'local', None, _('edit repository config')),
1744 ('l', 'local', None, _('edit repository config')),
1745 ('g', 'global', None, _('edit global config'))] + formatteropts,
1745 ('g', 'global', None, _('edit global config'))] + formatteropts,
1746 _('[-u] [NAME]...'),
1746 _('[-u] [NAME]...'),
1747 optionalrepo=True)
1747 optionalrepo=True)
1748 def config(ui, repo, *values, **opts):
1748 def config(ui, repo, *values, **opts):
1749 """show combined config settings from all hgrc files
1749 """show combined config settings from all hgrc files
1750
1750
1751 With no arguments, print names and values of all config items.
1751 With no arguments, print names and values of all config items.
1752
1752
1753 With one argument of the form section.name, print just the value
1753 With one argument of the form section.name, print just the value
1754 of that config item.
1754 of that config item.
1755
1755
1756 With multiple arguments, print names and values of all config
1756 With multiple arguments, print names and values of all config
1757 items with matching section names.
1757 items with matching section names.
1758
1758
1759 With --edit, start an editor on the user-level config file. With
1759 With --edit, start an editor on the user-level config file. With
1760 --global, edit the system-wide config file. With --local, edit the
1760 --global, edit the system-wide config file. With --local, edit the
1761 repository-level config file.
1761 repository-level config file.
1762
1762
1763 With --debug, the source (filename and line number) is printed
1763 With --debug, the source (filename and line number) is printed
1764 for each config item.
1764 for each config item.
1765
1765
1766 See :hg:`help config` for more information about config files.
1766 See :hg:`help config` for more information about config files.
1767
1767
1768 Returns 0 on success, 1 if NAME does not exist.
1768 Returns 0 on success, 1 if NAME does not exist.
1769
1769
1770 """
1770 """
1771
1771
1772 if opts.get('edit') or opts.get('local') or opts.get('global'):
1772 if opts.get('edit') or opts.get('local') or opts.get('global'):
1773 if opts.get('local') and opts.get('global'):
1773 if opts.get('local') and opts.get('global'):
1774 raise error.Abort(_("can't use --local and --global together"))
1774 raise error.Abort(_("can't use --local and --global together"))
1775
1775
1776 if opts.get('local'):
1776 if opts.get('local'):
1777 if not repo:
1777 if not repo:
1778 raise error.Abort(_("can't use --local outside a repository"))
1778 raise error.Abort(_("can't use --local outside a repository"))
1779 paths = [repo.vfs.join('hgrc')]
1779 paths = [repo.vfs.join('hgrc')]
1780 elif opts.get('global'):
1780 elif opts.get('global'):
1781 paths = rcutil.systemrcpath()
1781 paths = rcutil.systemrcpath()
1782 else:
1782 else:
1783 paths = rcutil.userrcpath()
1783 paths = rcutil.userrcpath()
1784
1784
1785 for f in paths:
1785 for f in paths:
1786 if os.path.exists(f):
1786 if os.path.exists(f):
1787 break
1787 break
1788 else:
1788 else:
1789 if opts.get('global'):
1789 if opts.get('global'):
1790 samplehgrc = uimod.samplehgrcs['global']
1790 samplehgrc = uimod.samplehgrcs['global']
1791 elif opts.get('local'):
1791 elif opts.get('local'):
1792 samplehgrc = uimod.samplehgrcs['local']
1792 samplehgrc = uimod.samplehgrcs['local']
1793 else:
1793 else:
1794 samplehgrc = uimod.samplehgrcs['user']
1794 samplehgrc = uimod.samplehgrcs['user']
1795
1795
1796 f = paths[0]
1796 f = paths[0]
1797 fp = open(f, "w")
1797 fp = open(f, "w")
1798 fp.write(samplehgrc)
1798 fp.write(samplehgrc)
1799 fp.close()
1799 fp.close()
1800
1800
1801 editor = ui.geteditor()
1801 editor = ui.geteditor()
1802 ui.system("%s \"%s\"" % (editor, f),
1802 ui.system("%s \"%s\"" % (editor, f),
1803 onerr=error.Abort, errprefix=_("edit failed"),
1803 onerr=error.Abort, errprefix=_("edit failed"),
1804 blockedtag='config_edit')
1804 blockedtag='config_edit')
1805 return
1805 return
1806 ui.pager('config')
1806 ui.pager('config')
1807 fm = ui.formatter('config', opts)
1807 fm = ui.formatter('config', opts)
1808 for t, f in rcutil.rccomponents():
1808 for t, f in rcutil.rccomponents():
1809 if t == 'path':
1809 if t == 'path':
1810 ui.debug('read config from: %s\n' % f)
1810 ui.debug('read config from: %s\n' % f)
1811 elif t == 'items':
1811 elif t == 'items':
1812 for section, name, value, source in f:
1812 for section, name, value, source in f:
1813 ui.debug('set config by: %s\n' % source)
1813 ui.debug('set config by: %s\n' % source)
1814 else:
1814 else:
1815 raise error.ProgrammingError('unknown rctype: %s' % t)
1815 raise error.ProgrammingError('unknown rctype: %s' % t)
1816 untrusted = bool(opts.get('untrusted'))
1816 untrusted = bool(opts.get('untrusted'))
1817 if values:
1817 if values:
1818 sections = [v for v in values if '.' not in v]
1818 sections = [v for v in values if '.' not in v]
1819 items = [v for v in values if '.' in v]
1819 items = [v for v in values if '.' in v]
1820 if len(items) > 1 or items and sections:
1820 if len(items) > 1 or items and sections:
1821 raise error.Abort(_('only one config item permitted'))
1821 raise error.Abort(_('only one config item permitted'))
1822 matched = False
1822 matched = False
1823 for section, name, value in ui.walkconfig(untrusted=untrusted):
1823 for section, name, value in ui.walkconfig(untrusted=untrusted):
1824 source = ui.configsource(section, name, untrusted)
1824 source = ui.configsource(section, name, untrusted)
1825 value = pycompat.bytestr(value)
1825 value = pycompat.bytestr(value)
1826 if fm.isplain():
1826 if fm.isplain():
1827 source = source or 'none'
1827 source = source or 'none'
1828 value = value.replace('\n', '\\n')
1828 value = value.replace('\n', '\\n')
1829 entryname = section + '.' + name
1829 entryname = section + '.' + name
1830 if values:
1830 if values:
1831 for v in values:
1831 for v in values:
1832 if v == section:
1832 if v == section:
1833 fm.startitem()
1833 fm.startitem()
1834 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1834 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1835 fm.write('name value', '%s=%s\n', entryname, value)
1835 fm.write('name value', '%s=%s\n', entryname, value)
1836 matched = True
1836 matched = True
1837 elif v == entryname:
1837 elif v == entryname:
1838 fm.startitem()
1838 fm.startitem()
1839 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1839 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1840 fm.write('value', '%s\n', value)
1840 fm.write('value', '%s\n', value)
1841 fm.data(name=entryname)
1841 fm.data(name=entryname)
1842 matched = True
1842 matched = True
1843 else:
1843 else:
1844 fm.startitem()
1844 fm.startitem()
1845 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1845 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1846 fm.write('name value', '%s=%s\n', entryname, value)
1846 fm.write('name value', '%s=%s\n', entryname, value)
1847 matched = True
1847 matched = True
1848 fm.end()
1848 fm.end()
1849 if matched:
1849 if matched:
1850 return 0
1850 return 0
1851 return 1
1851 return 1
1852
1852
1853 @command('copy|cp',
1853 @command('copy|cp',
1854 [('A', 'after', None, _('record a copy that has already occurred')),
1854 [('A', 'after', None, _('record a copy that has already occurred')),
1855 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1855 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1856 ] + walkopts + dryrunopts,
1856 ] + walkopts + dryrunopts,
1857 _('[OPTION]... [SOURCE]... DEST'))
1857 _('[OPTION]... [SOURCE]... DEST'))
1858 def copy(ui, repo, *pats, **opts):
1858 def copy(ui, repo, *pats, **opts):
1859 """mark files as copied for the next commit
1859 """mark files as copied for the next commit
1860
1860
1861 Mark dest as having copies of source files. If dest is a
1861 Mark dest as having copies of source files. If dest is a
1862 directory, copies are put in that directory. If dest is a file,
1862 directory, copies are put in that directory. If dest is a file,
1863 the source must be a single file.
1863 the source must be a single file.
1864
1864
1865 By default, this command copies the contents of files as they
1865 By default, this command copies the contents of files as they
1866 exist in the working directory. If invoked with -A/--after, the
1866 exist in the working directory. If invoked with -A/--after, the
1867 operation is recorded, but no copying is performed.
1867 operation is recorded, but no copying is performed.
1868
1868
1869 This command takes effect with the next commit. To undo a copy
1869 This command takes effect with the next commit. To undo a copy
1870 before that, see :hg:`revert`.
1870 before that, see :hg:`revert`.
1871
1871
1872 Returns 0 on success, 1 if errors are encountered.
1872 Returns 0 on success, 1 if errors are encountered.
1873 """
1873 """
1874 with repo.wlock(False):
1874 with repo.wlock(False):
1875 return cmdutil.copy(ui, repo, pats, opts)
1875 return cmdutil.copy(ui, repo, pats, opts)
1876
1876
1877 @command('^diff',
1877 @command('^diff',
1878 [('r', 'rev', [], _('revision'), _('REV')),
1878 [('r', 'rev', [], _('revision'), _('REV')),
1879 ('c', 'change', '', _('change made by revision'), _('REV'))
1879 ('c', 'change', '', _('change made by revision'), _('REV'))
1880 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1880 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1881 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1881 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1882 inferrepo=True)
1882 inferrepo=True)
1883 def diff(ui, repo, *pats, **opts):
1883 def diff(ui, repo, *pats, **opts):
1884 """diff repository (or selected files)
1884 """diff repository (or selected files)
1885
1885
1886 Show differences between revisions for the specified files.
1886 Show differences between revisions for the specified files.
1887
1887
1888 Differences between files are shown using the unified diff format.
1888 Differences between files are shown using the unified diff format.
1889
1889
1890 .. note::
1890 .. note::
1891
1891
1892 :hg:`diff` may generate unexpected results for merges, as it will
1892 :hg:`diff` may generate unexpected results for merges, as it will
1893 default to comparing against the working directory's first
1893 default to comparing against the working directory's first
1894 parent changeset if no revisions are specified.
1894 parent changeset if no revisions are specified.
1895
1895
1896 When two revision arguments are given, then changes are shown
1896 When two revision arguments are given, then changes are shown
1897 between those revisions. If only one revision is specified then
1897 between those revisions. If only one revision is specified then
1898 that revision is compared to the working directory, and, when no
1898 that revision is compared to the working directory, and, when no
1899 revisions are specified, the working directory files are compared
1899 revisions are specified, the working directory files are compared
1900 to its first parent.
1900 to its first parent.
1901
1901
1902 Alternatively you can specify -c/--change with a revision to see
1902 Alternatively you can specify -c/--change with a revision to see
1903 the changes in that changeset relative to its first parent.
1903 the changes in that changeset relative to its first parent.
1904
1904
1905 Without the -a/--text option, diff will avoid generating diffs of
1905 Without the -a/--text option, diff will avoid generating diffs of
1906 files it detects as binary. With -a, diff will generate a diff
1906 files it detects as binary. With -a, diff will generate a diff
1907 anyway, probably with undesirable results.
1907 anyway, probably with undesirable results.
1908
1908
1909 Use the -g/--git option to generate diffs in the git extended diff
1909 Use the -g/--git option to generate diffs in the git extended diff
1910 format. For more information, read :hg:`help diffs`.
1910 format. For more information, read :hg:`help diffs`.
1911
1911
1912 .. container:: verbose
1912 .. container:: verbose
1913
1913
1914 Examples:
1914 Examples:
1915
1915
1916 - compare a file in the current working directory to its parent::
1916 - compare a file in the current working directory to its parent::
1917
1917
1918 hg diff foo.c
1918 hg diff foo.c
1919
1919
1920 - compare two historical versions of a directory, with rename info::
1920 - compare two historical versions of a directory, with rename info::
1921
1921
1922 hg diff --git -r 1.0:1.2 lib/
1922 hg diff --git -r 1.0:1.2 lib/
1923
1923
1924 - get change stats relative to the last change on some date::
1924 - get change stats relative to the last change on some date::
1925
1925
1926 hg diff --stat -r "date('may 2')"
1926 hg diff --stat -r "date('may 2')"
1927
1927
1928 - diff all newly-added files that contain a keyword::
1928 - diff all newly-added files that contain a keyword::
1929
1929
1930 hg diff "set:added() and grep(GNU)"
1930 hg diff "set:added() and grep(GNU)"
1931
1931
1932 - compare a revision and its parents::
1932 - compare a revision and its parents::
1933
1933
1934 hg diff -c 9353 # compare against first parent
1934 hg diff -c 9353 # compare against first parent
1935 hg diff -r 9353^:9353 # same using revset syntax
1935 hg diff -r 9353^:9353 # same using revset syntax
1936 hg diff -r 9353^2:9353 # compare against the second parent
1936 hg diff -r 9353^2:9353 # compare against the second parent
1937
1937
1938 Returns 0 on success.
1938 Returns 0 on success.
1939 """
1939 """
1940
1940
1941 revs = opts.get('rev')
1941 revs = opts.get('rev')
1942 change = opts.get('change')
1942 change = opts.get('change')
1943 stat = opts.get('stat')
1943 stat = opts.get('stat')
1944 reverse = opts.get('reverse')
1944 reverse = opts.get('reverse')
1945
1945
1946 if revs and change:
1946 if revs and change:
1947 msg = _('cannot specify --rev and --change at the same time')
1947 msg = _('cannot specify --rev and --change at the same time')
1948 raise error.Abort(msg)
1948 raise error.Abort(msg)
1949 elif change:
1949 elif change:
1950 node2 = scmutil.revsingle(repo, change, None).node()
1950 node2 = scmutil.revsingle(repo, change, None).node()
1951 node1 = repo[node2].p1().node()
1951 node1 = repo[node2].p1().node()
1952 else:
1952 else:
1953 node1, node2 = scmutil.revpair(repo, revs)
1953 node1, node2 = scmutil.revpair(repo, revs)
1954
1954
1955 if reverse:
1955 if reverse:
1956 node1, node2 = node2, node1
1956 node1, node2 = node2, node1
1957
1957
1958 diffopts = patch.diffallopts(ui, opts)
1958 diffopts = patch.diffallopts(ui, opts)
1959 m = scmutil.match(repo[node2], pats, opts)
1959 m = scmutil.match(repo[node2], pats, opts)
1960 ui.pager('diff')
1960 ui.pager('diff')
1961 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1961 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1962 listsubrepos=opts.get('subrepos'),
1962 listsubrepos=opts.get('subrepos'),
1963 root=opts.get('root'))
1963 root=opts.get('root'))
1964
1964
1965 @command('^export',
1965 @command('^export',
1966 [('o', 'output', '',
1966 [('o', 'output', '',
1967 _('print output to file with formatted name'), _('FORMAT')),
1967 _('print output to file with formatted name'), _('FORMAT')),
1968 ('', 'switch-parent', None, _('diff against the second parent')),
1968 ('', 'switch-parent', None, _('diff against the second parent')),
1969 ('r', 'rev', [], _('revisions to export'), _('REV')),
1969 ('r', 'rev', [], _('revisions to export'), _('REV')),
1970 ] + diffopts,
1970 ] + diffopts,
1971 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
1971 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
1972 def export(ui, repo, *changesets, **opts):
1972 def export(ui, repo, *changesets, **opts):
1973 """dump the header and diffs for one or more changesets
1973 """dump the header and diffs for one or more changesets
1974
1974
1975 Print the changeset header and diffs for one or more revisions.
1975 Print the changeset header and diffs for one or more revisions.
1976 If no revision is given, the parent of the working directory is used.
1976 If no revision is given, the parent of the working directory is used.
1977
1977
1978 The information shown in the changeset header is: author, date,
1978 The information shown in the changeset header is: author, date,
1979 branch name (if non-default), changeset hash, parent(s) and commit
1979 branch name (if non-default), changeset hash, parent(s) and commit
1980 comment.
1980 comment.
1981
1981
1982 .. note::
1982 .. note::
1983
1983
1984 :hg:`export` may generate unexpected diff output for merge
1984 :hg:`export` may generate unexpected diff output for merge
1985 changesets, as it will compare the merge changeset against its
1985 changesets, as it will compare the merge changeset against its
1986 first parent only.
1986 first parent only.
1987
1987
1988 Output may be to a file, in which case the name of the file is
1988 Output may be to a file, in which case the name of the file is
1989 given using a format string. The formatting rules are as follows:
1989 given using a format string. The formatting rules are as follows:
1990
1990
1991 :``%%``: literal "%" character
1991 :``%%``: literal "%" character
1992 :``%H``: changeset hash (40 hexadecimal digits)
1992 :``%H``: changeset hash (40 hexadecimal digits)
1993 :``%N``: number of patches being generated
1993 :``%N``: number of patches being generated
1994 :``%R``: changeset revision number
1994 :``%R``: changeset revision number
1995 :``%b``: basename of the exporting repository
1995 :``%b``: basename of the exporting repository
1996 :``%h``: short-form changeset hash (12 hexadecimal digits)
1996 :``%h``: short-form changeset hash (12 hexadecimal digits)
1997 :``%m``: first line of the commit message (only alphanumeric characters)
1997 :``%m``: first line of the commit message (only alphanumeric characters)
1998 :``%n``: zero-padded sequence number, starting at 1
1998 :``%n``: zero-padded sequence number, starting at 1
1999 :``%r``: zero-padded changeset revision number
1999 :``%r``: zero-padded changeset revision number
2000
2000
2001 Without the -a/--text option, export will avoid generating diffs
2001 Without the -a/--text option, export will avoid generating diffs
2002 of files it detects as binary. With -a, export will generate a
2002 of files it detects as binary. With -a, export will generate a
2003 diff anyway, probably with undesirable results.
2003 diff anyway, probably with undesirable results.
2004
2004
2005 Use the -g/--git option to generate diffs in the git extended diff
2005 Use the -g/--git option to generate diffs in the git extended diff
2006 format. See :hg:`help diffs` for more information.
2006 format. See :hg:`help diffs` for more information.
2007
2007
2008 With the --switch-parent option, the diff will be against the
2008 With the --switch-parent option, the diff will be against the
2009 second parent. It can be useful to review a merge.
2009 second parent. It can be useful to review a merge.
2010
2010
2011 .. container:: verbose
2011 .. container:: verbose
2012
2012
2013 Examples:
2013 Examples:
2014
2014
2015 - use export and import to transplant a bugfix to the current
2015 - use export and import to transplant a bugfix to the current
2016 branch::
2016 branch::
2017
2017
2018 hg export -r 9353 | hg import -
2018 hg export -r 9353 | hg import -
2019
2019
2020 - export all the changesets between two revisions to a file with
2020 - export all the changesets between two revisions to a file with
2021 rename information::
2021 rename information::
2022
2022
2023 hg export --git -r 123:150 > changes.txt
2023 hg export --git -r 123:150 > changes.txt
2024
2024
2025 - split outgoing changes into a series of patches with
2025 - split outgoing changes into a series of patches with
2026 descriptive names::
2026 descriptive names::
2027
2027
2028 hg export -r "outgoing()" -o "%n-%m.patch"
2028 hg export -r "outgoing()" -o "%n-%m.patch"
2029
2029
2030 Returns 0 on success.
2030 Returns 0 on success.
2031 """
2031 """
2032 opts = pycompat.byteskwargs(opts)
2032 opts = pycompat.byteskwargs(opts)
2033 changesets += tuple(opts.get('rev', []))
2033 changesets += tuple(opts.get('rev', []))
2034 if not changesets:
2034 if not changesets:
2035 changesets = ['.']
2035 changesets = ['.']
2036 revs = scmutil.revrange(repo, changesets)
2036 revs = scmutil.revrange(repo, changesets)
2037 if not revs:
2037 if not revs:
2038 raise error.Abort(_("export requires at least one changeset"))
2038 raise error.Abort(_("export requires at least one changeset"))
2039 if len(revs) > 1:
2039 if len(revs) > 1:
2040 ui.note(_('exporting patches:\n'))
2040 ui.note(_('exporting patches:\n'))
2041 else:
2041 else:
2042 ui.note(_('exporting patch:\n'))
2042 ui.note(_('exporting patch:\n'))
2043 ui.pager('export')
2043 ui.pager('export')
2044 cmdutil.export(repo, revs, template=opts.get('output'),
2044 cmdutil.export(repo, revs, template=opts.get('output'),
2045 switch_parent=opts.get('switch_parent'),
2045 switch_parent=opts.get('switch_parent'),
2046 opts=patch.diffallopts(ui, opts))
2046 opts=patch.diffallopts(ui, opts))
2047
2047
2048 @command('files',
2048 @command('files',
2049 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2049 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2050 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2050 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2051 ] + walkopts + formatteropts + subrepoopts,
2051 ] + walkopts + formatteropts + subrepoopts,
2052 _('[OPTION]... [FILE]...'))
2052 _('[OPTION]... [FILE]...'))
2053 def files(ui, repo, *pats, **opts):
2053 def files(ui, repo, *pats, **opts):
2054 """list tracked files
2054 """list tracked files
2055
2055
2056 Print files under Mercurial control in the working directory or
2056 Print files under Mercurial control in the working directory or
2057 specified revision for given files (excluding removed files).
2057 specified revision for given files (excluding removed files).
2058 Files can be specified as filenames or filesets.
2058 Files can be specified as filenames or filesets.
2059
2059
2060 If no files are given to match, this command prints the names
2060 If no files are given to match, this command prints the names
2061 of all files under Mercurial control.
2061 of all files under Mercurial control.
2062
2062
2063 .. container:: verbose
2063 .. container:: verbose
2064
2064
2065 Examples:
2065 Examples:
2066
2066
2067 - list all files under the current directory::
2067 - list all files under the current directory::
2068
2068
2069 hg files .
2069 hg files .
2070
2070
2071 - shows sizes and flags for current revision::
2071 - shows sizes and flags for current revision::
2072
2072
2073 hg files -vr .
2073 hg files -vr .
2074
2074
2075 - list all files named README::
2075 - list all files named README::
2076
2076
2077 hg files -I "**/README"
2077 hg files -I "**/README"
2078
2078
2079 - list all binary files::
2079 - list all binary files::
2080
2080
2081 hg files "set:binary()"
2081 hg files "set:binary()"
2082
2082
2083 - find files containing a regular expression::
2083 - find files containing a regular expression::
2084
2084
2085 hg files "set:grep('bob')"
2085 hg files "set:grep('bob')"
2086
2086
2087 - search tracked file contents with xargs and grep::
2087 - search tracked file contents with xargs and grep::
2088
2088
2089 hg files -0 | xargs -0 grep foo
2089 hg files -0 | xargs -0 grep foo
2090
2090
2091 See :hg:`help patterns` and :hg:`help filesets` for more information
2091 See :hg:`help patterns` and :hg:`help filesets` for more information
2092 on specifying file patterns.
2092 on specifying file patterns.
2093
2093
2094 Returns 0 if a match is found, 1 otherwise.
2094 Returns 0 if a match is found, 1 otherwise.
2095
2095
2096 """
2096 """
2097 ctx = scmutil.revsingle(repo, opts.get(r'rev'), None)
2097 ctx = scmutil.revsingle(repo, opts.get(r'rev'), None)
2098
2098
2099 end = '\n'
2099 end = '\n'
2100 if opts.get('print0'):
2100 if opts.get('print0'):
2101 end = '\0'
2101 end = '\0'
2102 fmt = '%s' + end
2102 fmt = '%s' + end
2103
2103
2104 m = scmutil.match(ctx, pats, opts)
2104 m = scmutil.match(ctx, pats, opts)
2105 ui.pager('files')
2105 ui.pager('files')
2106 with ui.formatter('files', opts) as fm:
2106 with ui.formatter('files', opts) as fm:
2107 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2107 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2108
2108
2109 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
2109 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
2110 def forget(ui, repo, *pats, **opts):
2110 def forget(ui, repo, *pats, **opts):
2111 """forget the specified files on the next commit
2111 """forget the specified files on the next commit
2112
2112
2113 Mark the specified files so they will no longer be tracked
2113 Mark the specified files so they will no longer be tracked
2114 after the next commit.
2114 after the next commit.
2115
2115
2116 This only removes files from the current branch, not from the
2116 This only removes files from the current branch, not from the
2117 entire project history, and it does not delete them from the
2117 entire project history, and it does not delete them from the
2118 working directory.
2118 working directory.
2119
2119
2120 To delete the file from the working directory, see :hg:`remove`.
2120 To delete the file from the working directory, see :hg:`remove`.
2121
2121
2122 To undo a forget before the next commit, see :hg:`add`.
2122 To undo a forget before the next commit, see :hg:`add`.
2123
2123
2124 .. container:: verbose
2124 .. container:: verbose
2125
2125
2126 Examples:
2126 Examples:
2127
2127
2128 - forget newly-added binary files::
2128 - forget newly-added binary files::
2129
2129
2130 hg forget "set:added() and binary()"
2130 hg forget "set:added() and binary()"
2131
2131
2132 - forget files that would be excluded by .hgignore::
2132 - forget files that would be excluded by .hgignore::
2133
2133
2134 hg forget "set:hgignore()"
2134 hg forget "set:hgignore()"
2135
2135
2136 Returns 0 on success.
2136 Returns 0 on success.
2137 """
2137 """
2138
2138
2139 if not pats:
2139 if not pats:
2140 raise error.Abort(_('no files specified'))
2140 raise error.Abort(_('no files specified'))
2141
2141
2142 m = scmutil.match(repo[None], pats, opts)
2142 m = scmutil.match(repo[None], pats, opts)
2143 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2143 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2144 return rejected and 1 or 0
2144 return rejected and 1 or 0
2145
2145
2146 @command(
2146 @command(
2147 'graft',
2147 'graft',
2148 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2148 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2149 ('c', 'continue', False, _('resume interrupted graft')),
2149 ('c', 'continue', False, _('resume interrupted graft')),
2150 ('e', 'edit', False, _('invoke editor on commit messages')),
2150 ('e', 'edit', False, _('invoke editor on commit messages')),
2151 ('', 'log', None, _('append graft info to log message')),
2151 ('', 'log', None, _('append graft info to log message')),
2152 ('f', 'force', False, _('force graft')),
2152 ('f', 'force', False, _('force graft')),
2153 ('D', 'currentdate', False,
2153 ('D', 'currentdate', False,
2154 _('record the current date as commit date')),
2154 _('record the current date as commit date')),
2155 ('U', 'currentuser', False,
2155 ('U', 'currentuser', False,
2156 _('record the current user as committer'), _('DATE'))]
2156 _('record the current user as committer'), _('DATE'))]
2157 + commitopts2 + mergetoolopts + dryrunopts,
2157 + commitopts2 + mergetoolopts + dryrunopts,
2158 _('[OPTION]... [-r REV]... REV...'))
2158 _('[OPTION]... [-r REV]... REV...'))
2159 def graft(ui, repo, *revs, **opts):
2159 def graft(ui, repo, *revs, **opts):
2160 '''copy changes from other branches onto the current branch
2160 '''copy changes from other branches onto the current branch
2161
2161
2162 This command uses Mercurial's merge logic to copy individual
2162 This command uses Mercurial's merge logic to copy individual
2163 changes from other branches without merging branches in the
2163 changes from other branches without merging branches in the
2164 history graph. This is sometimes known as 'backporting' or
2164 history graph. This is sometimes known as 'backporting' or
2165 'cherry-picking'. By default, graft will copy user, date, and
2165 'cherry-picking'. By default, graft will copy user, date, and
2166 description from the source changesets.
2166 description from the source changesets.
2167
2167
2168 Changesets that are ancestors of the current revision, that have
2168 Changesets that are ancestors of the current revision, that have
2169 already been grafted, or that are merges will be skipped.
2169 already been grafted, or that are merges will be skipped.
2170
2170
2171 If --log is specified, log messages will have a comment appended
2171 If --log is specified, log messages will have a comment appended
2172 of the form::
2172 of the form::
2173
2173
2174 (grafted from CHANGESETHASH)
2174 (grafted from CHANGESETHASH)
2175
2175
2176 If --force is specified, revisions will be grafted even if they
2176 If --force is specified, revisions will be grafted even if they
2177 are already ancestors of or have been grafted to the destination.
2177 are already ancestors of or have been grafted to the destination.
2178 This is useful when the revisions have since been backed out.
2178 This is useful when the revisions have since been backed out.
2179
2179
2180 If a graft merge results in conflicts, the graft process is
2180 If a graft merge results in conflicts, the graft process is
2181 interrupted so that the current merge can be manually resolved.
2181 interrupted so that the current merge can be manually resolved.
2182 Once all conflicts are addressed, the graft process can be
2182 Once all conflicts are addressed, the graft process can be
2183 continued with the -c/--continue option.
2183 continued with the -c/--continue option.
2184
2184
2185 .. note::
2185 .. note::
2186
2186
2187 The -c/--continue option does not reapply earlier options, except
2187 The -c/--continue option does not reapply earlier options, except
2188 for --force.
2188 for --force.
2189
2189
2190 .. container:: verbose
2190 .. container:: verbose
2191
2191
2192 Examples:
2192 Examples:
2193
2193
2194 - copy a single change to the stable branch and edit its description::
2194 - copy a single change to the stable branch and edit its description::
2195
2195
2196 hg update stable
2196 hg update stable
2197 hg graft --edit 9393
2197 hg graft --edit 9393
2198
2198
2199 - graft a range of changesets with one exception, updating dates::
2199 - graft a range of changesets with one exception, updating dates::
2200
2200
2201 hg graft -D "2085::2093 and not 2091"
2201 hg graft -D "2085::2093 and not 2091"
2202
2202
2203 - continue a graft after resolving conflicts::
2203 - continue a graft after resolving conflicts::
2204
2204
2205 hg graft -c
2205 hg graft -c
2206
2206
2207 - show the source of a grafted changeset::
2207 - show the source of a grafted changeset::
2208
2208
2209 hg log --debug -r .
2209 hg log --debug -r .
2210
2210
2211 - show revisions sorted by date::
2211 - show revisions sorted by date::
2212
2212
2213 hg log -r "sort(all(), date)"
2213 hg log -r "sort(all(), date)"
2214
2214
2215 See :hg:`help revisions` for more about specifying revisions.
2215 See :hg:`help revisions` for more about specifying revisions.
2216
2216
2217 Returns 0 on successful completion.
2217 Returns 0 on successful completion.
2218 '''
2218 '''
2219 with repo.wlock():
2219 with repo.wlock():
2220 return _dograft(ui, repo, *revs, **opts)
2220 return _dograft(ui, repo, *revs, **opts)
2221
2221
2222 def _dograft(ui, repo, *revs, **opts):
2222 def _dograft(ui, repo, *revs, **opts):
2223 if revs and opts.get('rev'):
2223 if revs and opts.get('rev'):
2224 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2224 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2225 'revision ordering!\n'))
2225 'revision ordering!\n'))
2226
2226
2227 revs = list(revs)
2227 revs = list(revs)
2228 revs.extend(opts.get('rev'))
2228 revs.extend(opts.get('rev'))
2229
2229
2230 if not opts.get('user') and opts.get('currentuser'):
2230 if not opts.get('user') and opts.get('currentuser'):
2231 opts['user'] = ui.username()
2231 opts['user'] = ui.username()
2232 if not opts.get('date') and opts.get('currentdate'):
2232 if not opts.get('date') and opts.get('currentdate'):
2233 opts['date'] = "%d %d" % util.makedate()
2233 opts['date'] = "%d %d" % util.makedate()
2234
2234
2235 editor = cmdutil.getcommiteditor(editform='graft', **opts)
2235 editor = cmdutil.getcommiteditor(editform='graft', **opts)
2236
2236
2237 cont = False
2237 cont = False
2238 if opts.get('continue'):
2238 if opts.get('continue'):
2239 cont = True
2239 cont = True
2240 if revs:
2240 if revs:
2241 raise error.Abort(_("can't specify --continue and revisions"))
2241 raise error.Abort(_("can't specify --continue and revisions"))
2242 # read in unfinished revisions
2242 # read in unfinished revisions
2243 try:
2243 try:
2244 nodes = repo.vfs.read('graftstate').splitlines()
2244 nodes = repo.vfs.read('graftstate').splitlines()
2245 revs = [repo[node].rev() for node in nodes]
2245 revs = [repo[node].rev() for node in nodes]
2246 except IOError as inst:
2246 except IOError as inst:
2247 if inst.errno != errno.ENOENT:
2247 if inst.errno != errno.ENOENT:
2248 raise
2248 raise
2249 cmdutil.wrongtooltocontinue(repo, _('graft'))
2249 cmdutil.wrongtooltocontinue(repo, _('graft'))
2250 else:
2250 else:
2251 cmdutil.checkunfinished(repo)
2251 cmdutil.checkunfinished(repo)
2252 cmdutil.bailifchanged(repo)
2252 cmdutil.bailifchanged(repo)
2253 if not revs:
2253 if not revs:
2254 raise error.Abort(_('no revisions specified'))
2254 raise error.Abort(_('no revisions specified'))
2255 revs = scmutil.revrange(repo, revs)
2255 revs = scmutil.revrange(repo, revs)
2256
2256
2257 skipped = set()
2257 skipped = set()
2258 # check for merges
2258 # check for merges
2259 for rev in repo.revs('%ld and merge()', revs):
2259 for rev in repo.revs('%ld and merge()', revs):
2260 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2260 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2261 skipped.add(rev)
2261 skipped.add(rev)
2262 revs = [r for r in revs if r not in skipped]
2262 revs = [r for r in revs if r not in skipped]
2263 if not revs:
2263 if not revs:
2264 return -1
2264 return -1
2265
2265
2266 # Don't check in the --continue case, in effect retaining --force across
2266 # Don't check in the --continue case, in effect retaining --force across
2267 # --continues. That's because without --force, any revisions we decided to
2267 # --continues. That's because without --force, any revisions we decided to
2268 # skip would have been filtered out here, so they wouldn't have made their
2268 # skip would have been filtered out here, so they wouldn't have made their
2269 # way to the graftstate. With --force, any revisions we would have otherwise
2269 # way to the graftstate. With --force, any revisions we would have otherwise
2270 # skipped would not have been filtered out, and if they hadn't been applied
2270 # skipped would not have been filtered out, and if they hadn't been applied
2271 # already, they'd have been in the graftstate.
2271 # already, they'd have been in the graftstate.
2272 if not (cont or opts.get('force')):
2272 if not (cont or opts.get('force')):
2273 # check for ancestors of dest branch
2273 # check for ancestors of dest branch
2274 crev = repo['.'].rev()
2274 crev = repo['.'].rev()
2275 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2275 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2276 # XXX make this lazy in the future
2276 # XXX make this lazy in the future
2277 # don't mutate while iterating, create a copy
2277 # don't mutate while iterating, create a copy
2278 for rev in list(revs):
2278 for rev in list(revs):
2279 if rev in ancestors:
2279 if rev in ancestors:
2280 ui.warn(_('skipping ancestor revision %d:%s\n') %
2280 ui.warn(_('skipping ancestor revision %d:%s\n') %
2281 (rev, repo[rev]))
2281 (rev, repo[rev]))
2282 # XXX remove on list is slow
2282 # XXX remove on list is slow
2283 revs.remove(rev)
2283 revs.remove(rev)
2284 if not revs:
2284 if not revs:
2285 return -1
2285 return -1
2286
2286
2287 # analyze revs for earlier grafts
2287 # analyze revs for earlier grafts
2288 ids = {}
2288 ids = {}
2289 for ctx in repo.set("%ld", revs):
2289 for ctx in repo.set("%ld", revs):
2290 ids[ctx.hex()] = ctx.rev()
2290 ids[ctx.hex()] = ctx.rev()
2291 n = ctx.extra().get('source')
2291 n = ctx.extra().get('source')
2292 if n:
2292 if n:
2293 ids[n] = ctx.rev()
2293 ids[n] = ctx.rev()
2294
2294
2295 # check ancestors for earlier grafts
2295 # check ancestors for earlier grafts
2296 ui.debug('scanning for duplicate grafts\n')
2296 ui.debug('scanning for duplicate grafts\n')
2297
2297
2298 for rev in repo.changelog.findmissingrevs(revs, [crev]):
2298 for rev in repo.changelog.findmissingrevs(revs, [crev]):
2299 ctx = repo[rev]
2299 ctx = repo[rev]
2300 n = ctx.extra().get('source')
2300 n = ctx.extra().get('source')
2301 if n in ids:
2301 if n in ids:
2302 try:
2302 try:
2303 r = repo[n].rev()
2303 r = repo[n].rev()
2304 except error.RepoLookupError:
2304 except error.RepoLookupError:
2305 r = None
2305 r = None
2306 if r in revs:
2306 if r in revs:
2307 ui.warn(_('skipping revision %d:%s '
2307 ui.warn(_('skipping revision %d:%s '
2308 '(already grafted to %d:%s)\n')
2308 '(already grafted to %d:%s)\n')
2309 % (r, repo[r], rev, ctx))
2309 % (r, repo[r], rev, ctx))
2310 revs.remove(r)
2310 revs.remove(r)
2311 elif ids[n] in revs:
2311 elif ids[n] in revs:
2312 if r is None:
2312 if r is None:
2313 ui.warn(_('skipping already grafted revision %d:%s '
2313 ui.warn(_('skipping already grafted revision %d:%s '
2314 '(%d:%s also has unknown origin %s)\n')
2314 '(%d:%s also has unknown origin %s)\n')
2315 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2315 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2316 else:
2316 else:
2317 ui.warn(_('skipping already grafted revision %d:%s '
2317 ui.warn(_('skipping already grafted revision %d:%s '
2318 '(%d:%s also has origin %d:%s)\n')
2318 '(%d:%s also has origin %d:%s)\n')
2319 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2319 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2320 revs.remove(ids[n])
2320 revs.remove(ids[n])
2321 elif ctx.hex() in ids:
2321 elif ctx.hex() in ids:
2322 r = ids[ctx.hex()]
2322 r = ids[ctx.hex()]
2323 ui.warn(_('skipping already grafted revision %d:%s '
2323 ui.warn(_('skipping already grafted revision %d:%s '
2324 '(was grafted from %d:%s)\n') %
2324 '(was grafted from %d:%s)\n') %
2325 (r, repo[r], rev, ctx))
2325 (r, repo[r], rev, ctx))
2326 revs.remove(r)
2326 revs.remove(r)
2327 if not revs:
2327 if not revs:
2328 return -1
2328 return -1
2329
2329
2330 for pos, ctx in enumerate(repo.set("%ld", revs)):
2330 for pos, ctx in enumerate(repo.set("%ld", revs)):
2331 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2331 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2332 ctx.description().split('\n', 1)[0])
2332 ctx.description().split('\n', 1)[0])
2333 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2333 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2334 if names:
2334 if names:
2335 desc += ' (%s)' % ' '.join(names)
2335 desc += ' (%s)' % ' '.join(names)
2336 ui.status(_('grafting %s\n') % desc)
2336 ui.status(_('grafting %s\n') % desc)
2337 if opts.get('dry_run'):
2337 if opts.get('dry_run'):
2338 continue
2338 continue
2339
2339
2340 source = ctx.extra().get('source')
2340 source = ctx.extra().get('source')
2341 extra = {}
2341 extra = {}
2342 if source:
2342 if source:
2343 extra['source'] = source
2343 extra['source'] = source
2344 extra['intermediate-source'] = ctx.hex()
2344 extra['intermediate-source'] = ctx.hex()
2345 else:
2345 else:
2346 extra['source'] = ctx.hex()
2346 extra['source'] = ctx.hex()
2347 user = ctx.user()
2347 user = ctx.user()
2348 if opts.get('user'):
2348 if opts.get('user'):
2349 user = opts['user']
2349 user = opts['user']
2350 date = ctx.date()
2350 date = ctx.date()
2351 if opts.get('date'):
2351 if opts.get('date'):
2352 date = opts['date']
2352 date = opts['date']
2353 message = ctx.description()
2353 message = ctx.description()
2354 if opts.get('log'):
2354 if opts.get('log'):
2355 message += '\n(grafted from %s)' % ctx.hex()
2355 message += '\n(grafted from %s)' % ctx.hex()
2356
2356
2357 # we don't merge the first commit when continuing
2357 # we don't merge the first commit when continuing
2358 if not cont:
2358 if not cont:
2359 # perform the graft merge with p1(rev) as 'ancestor'
2359 # perform the graft merge with p1(rev) as 'ancestor'
2360 try:
2360 try:
2361 # ui.forcemerge is an internal variable, do not document
2361 # ui.forcemerge is an internal variable, do not document
2362 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2362 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2363 'graft')
2363 'graft')
2364 stats = mergemod.graft(repo, ctx, ctx.p1(),
2364 stats = mergemod.graft(repo, ctx, ctx.p1(),
2365 ['local', 'graft'])
2365 ['local', 'graft'])
2366 finally:
2366 finally:
2367 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2367 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2368 # report any conflicts
2368 # report any conflicts
2369 if stats and stats[3] > 0:
2369 if stats and stats[3] > 0:
2370 # write out state for --continue
2370 # write out state for --continue
2371 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2371 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2372 repo.vfs.write('graftstate', ''.join(nodelines))
2372 repo.vfs.write('graftstate', ''.join(nodelines))
2373 extra = ''
2373 extra = ''
2374 if opts.get('user'):
2374 if opts.get('user'):
2375 extra += ' --user %s' % util.shellquote(opts['user'])
2375 extra += ' --user %s' % util.shellquote(opts['user'])
2376 if opts.get('date'):
2376 if opts.get('date'):
2377 extra += ' --date %s' % util.shellquote(opts['date'])
2377 extra += ' --date %s' % util.shellquote(opts['date'])
2378 if opts.get('log'):
2378 if opts.get('log'):
2379 extra += ' --log'
2379 extra += ' --log'
2380 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2380 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2381 raise error.Abort(
2381 raise error.Abort(
2382 _("unresolved conflicts, can't continue"),
2382 _("unresolved conflicts, can't continue"),
2383 hint=hint)
2383 hint=hint)
2384 else:
2384 else:
2385 cont = False
2385 cont = False
2386
2386
2387 # commit
2387 # commit
2388 node = repo.commit(text=message, user=user,
2388 node = repo.commit(text=message, user=user,
2389 date=date, extra=extra, editor=editor)
2389 date=date, extra=extra, editor=editor)
2390 if node is None:
2390 if node is None:
2391 ui.warn(
2391 ui.warn(
2392 _('note: graft of %d:%s created no changes to commit\n') %
2392 _('note: graft of %d:%s created no changes to commit\n') %
2393 (ctx.rev(), ctx))
2393 (ctx.rev(), ctx))
2394
2394
2395 # remove state when we complete successfully
2395 # remove state when we complete successfully
2396 if not opts.get('dry_run'):
2396 if not opts.get('dry_run'):
2397 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2397 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2398
2398
2399 return 0
2399 return 0
2400
2400
2401 @command('grep',
2401 @command('grep',
2402 [('0', 'print0', None, _('end fields with NUL')),
2402 [('0', 'print0', None, _('end fields with NUL')),
2403 ('', 'all', None, _('print all revisions that match')),
2403 ('', 'all', None, _('print all revisions that match')),
2404 ('a', 'text', None, _('treat all files as text')),
2404 ('a', 'text', None, _('treat all files as text')),
2405 ('f', 'follow', None,
2405 ('f', 'follow', None,
2406 _('follow changeset history,'
2406 _('follow changeset history,'
2407 ' or file history across copies and renames')),
2407 ' or file history across copies and renames')),
2408 ('i', 'ignore-case', None, _('ignore case when matching')),
2408 ('i', 'ignore-case', None, _('ignore case when matching')),
2409 ('l', 'files-with-matches', None,
2409 ('l', 'files-with-matches', None,
2410 _('print only filenames and revisions that match')),
2410 _('print only filenames and revisions that match')),
2411 ('n', 'line-number', None, _('print matching line numbers')),
2411 ('n', 'line-number', None, _('print matching line numbers')),
2412 ('r', 'rev', [],
2412 ('r', 'rev', [],
2413 _('only search files changed within revision range'), _('REV')),
2413 _('only search files changed within revision range'), _('REV')),
2414 ('u', 'user', None, _('list the author (long with -v)')),
2414 ('u', 'user', None, _('list the author (long with -v)')),
2415 ('d', 'date', None, _('list the date (short with -q)')),
2415 ('d', 'date', None, _('list the date (short with -q)')),
2416 ] + formatteropts + walkopts,
2416 ] + formatteropts + walkopts,
2417 _('[OPTION]... PATTERN [FILE]...'),
2417 _('[OPTION]... PATTERN [FILE]...'),
2418 inferrepo=True)
2418 inferrepo=True)
2419 def grep(ui, repo, pattern, *pats, **opts):
2419 def grep(ui, repo, pattern, *pats, **opts):
2420 """search revision history for a pattern in specified files
2420 """search revision history for a pattern in specified files
2421
2421
2422 Search revision history for a regular expression in the specified
2422 Search revision history for a regular expression in the specified
2423 files or the entire project.
2423 files or the entire project.
2424
2424
2425 By default, grep prints the most recent revision number for each
2425 By default, grep prints the most recent revision number for each
2426 file in which it finds a match. To get it to print every revision
2426 file in which it finds a match. To get it to print every revision
2427 that contains a change in match status ("-" for a match that becomes
2427 that contains a change in match status ("-" for a match that becomes
2428 a non-match, or "+" for a non-match that becomes a match), use the
2428 a non-match, or "+" for a non-match that becomes a match), use the
2429 --all flag.
2429 --all flag.
2430
2430
2431 PATTERN can be any Python (roughly Perl-compatible) regular
2431 PATTERN can be any Python (roughly Perl-compatible) regular
2432 expression.
2432 expression.
2433
2433
2434 If no FILEs are specified (and -f/--follow isn't set), all files in
2434 If no FILEs are specified (and -f/--follow isn't set), all files in
2435 the repository are searched, including those that don't exist in the
2435 the repository are searched, including those that don't exist in the
2436 current branch or have been deleted in a prior changeset.
2436 current branch or have been deleted in a prior changeset.
2437
2437
2438 Returns 0 if a match is found, 1 otherwise.
2438 Returns 0 if a match is found, 1 otherwise.
2439 """
2439 """
2440 reflags = re.M
2440 reflags = re.M
2441 if opts.get('ignore_case'):
2441 if opts.get('ignore_case'):
2442 reflags |= re.I
2442 reflags |= re.I
2443 try:
2443 try:
2444 regexp = util.re.compile(pattern, reflags)
2444 regexp = util.re.compile(pattern, reflags)
2445 except re.error as inst:
2445 except re.error as inst:
2446 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2446 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2447 return 1
2447 return 1
2448 sep, eol = ':', '\n'
2448 sep, eol = ':', '\n'
2449 if opts.get('print0'):
2449 if opts.get('print0'):
2450 sep = eol = '\0'
2450 sep = eol = '\0'
2451
2451
2452 getfile = util.lrucachefunc(repo.file)
2452 getfile = util.lrucachefunc(repo.file)
2453
2453
2454 def matchlines(body):
2454 def matchlines(body):
2455 begin = 0
2455 begin = 0
2456 linenum = 0
2456 linenum = 0
2457 while begin < len(body):
2457 while begin < len(body):
2458 match = regexp.search(body, begin)
2458 match = regexp.search(body, begin)
2459 if not match:
2459 if not match:
2460 break
2460 break
2461 mstart, mend = match.span()
2461 mstart, mend = match.span()
2462 linenum += body.count('\n', begin, mstart) + 1
2462 linenum += body.count('\n', begin, mstart) + 1
2463 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2463 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2464 begin = body.find('\n', mend) + 1 or len(body) + 1
2464 begin = body.find('\n', mend) + 1 or len(body) + 1
2465 lend = begin - 1
2465 lend = begin - 1
2466 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2466 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2467
2467
2468 class linestate(object):
2468 class linestate(object):
2469 def __init__(self, line, linenum, colstart, colend):
2469 def __init__(self, line, linenum, colstart, colend):
2470 self.line = line
2470 self.line = line
2471 self.linenum = linenum
2471 self.linenum = linenum
2472 self.colstart = colstart
2472 self.colstart = colstart
2473 self.colend = colend
2473 self.colend = colend
2474
2474
2475 def __hash__(self):
2475 def __hash__(self):
2476 return hash((self.linenum, self.line))
2476 return hash((self.linenum, self.line))
2477
2477
2478 def __eq__(self, other):
2478 def __eq__(self, other):
2479 return self.line == other.line
2479 return self.line == other.line
2480
2480
2481 def findpos(self):
2481 def findpos(self):
2482 """Iterate all (start, end) indices of matches"""
2482 """Iterate all (start, end) indices of matches"""
2483 yield self.colstart, self.colend
2483 yield self.colstart, self.colend
2484 p = self.colend
2484 p = self.colend
2485 while p < len(self.line):
2485 while p < len(self.line):
2486 m = regexp.search(self.line, p)
2486 m = regexp.search(self.line, p)
2487 if not m:
2487 if not m:
2488 break
2488 break
2489 yield m.span()
2489 yield m.span()
2490 p = m.end()
2490 p = m.end()
2491
2491
2492 matches = {}
2492 matches = {}
2493 copies = {}
2493 copies = {}
2494 def grepbody(fn, rev, body):
2494 def grepbody(fn, rev, body):
2495 matches[rev].setdefault(fn, [])
2495 matches[rev].setdefault(fn, [])
2496 m = matches[rev][fn]
2496 m = matches[rev][fn]
2497 for lnum, cstart, cend, line in matchlines(body):
2497 for lnum, cstart, cend, line in matchlines(body):
2498 s = linestate(line, lnum, cstart, cend)
2498 s = linestate(line, lnum, cstart, cend)
2499 m.append(s)
2499 m.append(s)
2500
2500
2501 def difflinestates(a, b):
2501 def difflinestates(a, b):
2502 sm = difflib.SequenceMatcher(None, a, b)
2502 sm = difflib.SequenceMatcher(None, a, b)
2503 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2503 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2504 if tag == 'insert':
2504 if tag == 'insert':
2505 for i in xrange(blo, bhi):
2505 for i in xrange(blo, bhi):
2506 yield ('+', b[i])
2506 yield ('+', b[i])
2507 elif tag == 'delete':
2507 elif tag == 'delete':
2508 for i in xrange(alo, ahi):
2508 for i in xrange(alo, ahi):
2509 yield ('-', a[i])
2509 yield ('-', a[i])
2510 elif tag == 'replace':
2510 elif tag == 'replace':
2511 for i in xrange(alo, ahi):
2511 for i in xrange(alo, ahi):
2512 yield ('-', a[i])
2512 yield ('-', a[i])
2513 for i in xrange(blo, bhi):
2513 for i in xrange(blo, bhi):
2514 yield ('+', b[i])
2514 yield ('+', b[i])
2515
2515
2516 def display(fm, fn, ctx, pstates, states):
2516 def display(fm, fn, ctx, pstates, states):
2517 rev = ctx.rev()
2517 rev = ctx.rev()
2518 if fm.isplain():
2518 if fm.isplain():
2519 formatuser = ui.shortuser
2519 formatuser = ui.shortuser
2520 else:
2520 else:
2521 formatuser = str
2521 formatuser = str
2522 if ui.quiet:
2522 if ui.quiet:
2523 datefmt = '%Y-%m-%d'
2523 datefmt = '%Y-%m-%d'
2524 else:
2524 else:
2525 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2525 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2526 found = False
2526 found = False
2527 @util.cachefunc
2527 @util.cachefunc
2528 def binary():
2528 def binary():
2529 flog = getfile(fn)
2529 flog = getfile(fn)
2530 return util.binary(flog.read(ctx.filenode(fn)))
2530 return util.binary(flog.read(ctx.filenode(fn)))
2531
2531
2532 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2532 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2533 if opts.get('all'):
2533 if opts.get('all'):
2534 iter = difflinestates(pstates, states)
2534 iter = difflinestates(pstates, states)
2535 else:
2535 else:
2536 iter = [('', l) for l in states]
2536 iter = [('', l) for l in states]
2537 for change, l in iter:
2537 for change, l in iter:
2538 fm.startitem()
2538 fm.startitem()
2539 fm.data(node=fm.hexfunc(ctx.node()))
2539 fm.data(node=fm.hexfunc(ctx.node()))
2540 cols = [
2540 cols = [
2541 ('filename', fn, True),
2541 ('filename', fn, True),
2542 ('rev', rev, True),
2542 ('rev', rev, True),
2543 ('linenumber', l.linenum, opts.get('line_number')),
2543 ('linenumber', l.linenum, opts.get('line_number')),
2544 ]
2544 ]
2545 if opts.get('all'):
2545 if opts.get('all'):
2546 cols.append(('change', change, True))
2546 cols.append(('change', change, True))
2547 cols.extend([
2547 cols.extend([
2548 ('user', formatuser(ctx.user()), opts.get('user')),
2548 ('user', formatuser(ctx.user()), opts.get('user')),
2549 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2549 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2550 ])
2550 ])
2551 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2551 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2552 for name, data, cond in cols:
2552 for name, data, cond in cols:
2553 field = fieldnamemap.get(name, name)
2553 field = fieldnamemap.get(name, name)
2554 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2554 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2555 if cond and name != lastcol:
2555 if cond and name != lastcol:
2556 fm.plain(sep, label='grep.sep')
2556 fm.plain(sep, label='grep.sep')
2557 if not opts.get('files_with_matches'):
2557 if not opts.get('files_with_matches'):
2558 fm.plain(sep, label='grep.sep')
2558 fm.plain(sep, label='grep.sep')
2559 if not opts.get('text') and binary():
2559 if not opts.get('text') and binary():
2560 fm.plain(_(" Binary file matches"))
2560 fm.plain(_(" Binary file matches"))
2561 else:
2561 else:
2562 displaymatches(fm.nested('texts'), l)
2562 displaymatches(fm.nested('texts'), l)
2563 fm.plain(eol)
2563 fm.plain(eol)
2564 found = True
2564 found = True
2565 if opts.get('files_with_matches'):
2565 if opts.get('files_with_matches'):
2566 break
2566 break
2567 return found
2567 return found
2568
2568
2569 def displaymatches(fm, l):
2569 def displaymatches(fm, l):
2570 p = 0
2570 p = 0
2571 for s, e in l.findpos():
2571 for s, e in l.findpos():
2572 if p < s:
2572 if p < s:
2573 fm.startitem()
2573 fm.startitem()
2574 fm.write('text', '%s', l.line[p:s])
2574 fm.write('text', '%s', l.line[p:s])
2575 fm.data(matched=False)
2575 fm.data(matched=False)
2576 fm.startitem()
2576 fm.startitem()
2577 fm.write('text', '%s', l.line[s:e], label='grep.match')
2577 fm.write('text', '%s', l.line[s:e], label='grep.match')
2578 fm.data(matched=True)
2578 fm.data(matched=True)
2579 p = e
2579 p = e
2580 if p < len(l.line):
2580 if p < len(l.line):
2581 fm.startitem()
2581 fm.startitem()
2582 fm.write('text', '%s', l.line[p:])
2582 fm.write('text', '%s', l.line[p:])
2583 fm.data(matched=False)
2583 fm.data(matched=False)
2584 fm.end()
2584 fm.end()
2585
2585
2586 skip = {}
2586 skip = {}
2587 revfiles = {}
2587 revfiles = {}
2588 matchfn = scmutil.match(repo[None], pats, opts)
2588 matchfn = scmutil.match(repo[None], pats, opts)
2589 found = False
2589 found = False
2590 follow = opts.get('follow')
2590 follow = opts.get('follow')
2591
2591
2592 def prep(ctx, fns):
2592 def prep(ctx, fns):
2593 rev = ctx.rev()
2593 rev = ctx.rev()
2594 pctx = ctx.p1()
2594 pctx = ctx.p1()
2595 parent = pctx.rev()
2595 parent = pctx.rev()
2596 matches.setdefault(rev, {})
2596 matches.setdefault(rev, {})
2597 matches.setdefault(parent, {})
2597 matches.setdefault(parent, {})
2598 files = revfiles.setdefault(rev, [])
2598 files = revfiles.setdefault(rev, [])
2599 for fn in fns:
2599 for fn in fns:
2600 flog = getfile(fn)
2600 flog = getfile(fn)
2601 try:
2601 try:
2602 fnode = ctx.filenode(fn)
2602 fnode = ctx.filenode(fn)
2603 except error.LookupError:
2603 except error.LookupError:
2604 continue
2604 continue
2605
2605
2606 copied = flog.renamed(fnode)
2606 copied = flog.renamed(fnode)
2607 copy = follow and copied and copied[0]
2607 copy = follow and copied and copied[0]
2608 if copy:
2608 if copy:
2609 copies.setdefault(rev, {})[fn] = copy
2609 copies.setdefault(rev, {})[fn] = copy
2610 if fn in skip:
2610 if fn in skip:
2611 if copy:
2611 if copy:
2612 skip[copy] = True
2612 skip[copy] = True
2613 continue
2613 continue
2614 files.append(fn)
2614 files.append(fn)
2615
2615
2616 if fn not in matches[rev]:
2616 if fn not in matches[rev]:
2617 grepbody(fn, rev, flog.read(fnode))
2617 grepbody(fn, rev, flog.read(fnode))
2618
2618
2619 pfn = copy or fn
2619 pfn = copy or fn
2620 if pfn not in matches[parent]:
2620 if pfn not in matches[parent]:
2621 try:
2621 try:
2622 fnode = pctx.filenode(pfn)
2622 fnode = pctx.filenode(pfn)
2623 grepbody(pfn, parent, flog.read(fnode))
2623 grepbody(pfn, parent, flog.read(fnode))
2624 except error.LookupError:
2624 except error.LookupError:
2625 pass
2625 pass
2626
2626
2627 ui.pager('grep')
2627 ui.pager('grep')
2628 fm = ui.formatter('grep', opts)
2628 fm = ui.formatter('grep', opts)
2629 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2629 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2630 rev = ctx.rev()
2630 rev = ctx.rev()
2631 parent = ctx.p1().rev()
2631 parent = ctx.p1().rev()
2632 for fn in sorted(revfiles.get(rev, [])):
2632 for fn in sorted(revfiles.get(rev, [])):
2633 states = matches[rev][fn]
2633 states = matches[rev][fn]
2634 copy = copies.get(rev, {}).get(fn)
2634 copy = copies.get(rev, {}).get(fn)
2635 if fn in skip:
2635 if fn in skip:
2636 if copy:
2636 if copy:
2637 skip[copy] = True
2637 skip[copy] = True
2638 continue
2638 continue
2639 pstates = matches.get(parent, {}).get(copy or fn, [])
2639 pstates = matches.get(parent, {}).get(copy or fn, [])
2640 if pstates or states:
2640 if pstates or states:
2641 r = display(fm, fn, ctx, pstates, states)
2641 r = display(fm, fn, ctx, pstates, states)
2642 found = found or r
2642 found = found or r
2643 if r and not opts.get('all'):
2643 if r and not opts.get('all'):
2644 skip[fn] = True
2644 skip[fn] = True
2645 if copy:
2645 if copy:
2646 skip[copy] = True
2646 skip[copy] = True
2647 del matches[rev]
2647 del matches[rev]
2648 del revfiles[rev]
2648 del revfiles[rev]
2649 fm.end()
2649 fm.end()
2650
2650
2651 return not found
2651 return not found
2652
2652
2653 @command('heads',
2653 @command('heads',
2654 [('r', 'rev', '',
2654 [('r', 'rev', '',
2655 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2655 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2656 ('t', 'topo', False, _('show topological heads only')),
2656 ('t', 'topo', False, _('show topological heads only')),
2657 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2657 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2658 ('c', 'closed', False, _('show normal and closed branch heads')),
2658 ('c', 'closed', False, _('show normal and closed branch heads')),
2659 ] + templateopts,
2659 ] + templateopts,
2660 _('[-ct] [-r STARTREV] [REV]...'))
2660 _('[-ct] [-r STARTREV] [REV]...'))
2661 def heads(ui, repo, *branchrevs, **opts):
2661 def heads(ui, repo, *branchrevs, **opts):
2662 """show branch heads
2662 """show branch heads
2663
2663
2664 With no arguments, show all open branch heads in the repository.
2664 With no arguments, show all open branch heads in the repository.
2665 Branch heads are changesets that have no descendants on the
2665 Branch heads are changesets that have no descendants on the
2666 same branch. They are where development generally takes place and
2666 same branch. They are where development generally takes place and
2667 are the usual targets for update and merge operations.
2667 are the usual targets for update and merge operations.
2668
2668
2669 If one or more REVs are given, only open branch heads on the
2669 If one or more REVs are given, only open branch heads on the
2670 branches associated with the specified changesets are shown. This
2670 branches associated with the specified changesets are shown. This
2671 means that you can use :hg:`heads .` to see the heads on the
2671 means that you can use :hg:`heads .` to see the heads on the
2672 currently checked-out branch.
2672 currently checked-out branch.
2673
2673
2674 If -c/--closed is specified, also show branch heads marked closed
2674 If -c/--closed is specified, also show branch heads marked closed
2675 (see :hg:`commit --close-branch`).
2675 (see :hg:`commit --close-branch`).
2676
2676
2677 If STARTREV is specified, only those heads that are descendants of
2677 If STARTREV is specified, only those heads that are descendants of
2678 STARTREV will be displayed.
2678 STARTREV will be displayed.
2679
2679
2680 If -t/--topo is specified, named branch mechanics will be ignored and only
2680 If -t/--topo is specified, named branch mechanics will be ignored and only
2681 topological heads (changesets with no children) will be shown.
2681 topological heads (changesets with no children) will be shown.
2682
2682
2683 Returns 0 if matching heads are found, 1 if not.
2683 Returns 0 if matching heads are found, 1 if not.
2684 """
2684 """
2685
2685
2686 start = None
2686 start = None
2687 if 'rev' in opts:
2687 if 'rev' in opts:
2688 start = scmutil.revsingle(repo, opts['rev'], None).node()
2688 start = scmutil.revsingle(repo, opts['rev'], None).node()
2689
2689
2690 if opts.get('topo'):
2690 if opts.get('topo'):
2691 heads = [repo[h] for h in repo.heads(start)]
2691 heads = [repo[h] for h in repo.heads(start)]
2692 else:
2692 else:
2693 heads = []
2693 heads = []
2694 for branch in repo.branchmap():
2694 for branch in repo.branchmap():
2695 heads += repo.branchheads(branch, start, opts.get('closed'))
2695 heads += repo.branchheads(branch, start, opts.get('closed'))
2696 heads = [repo[h] for h in heads]
2696 heads = [repo[h] for h in heads]
2697
2697
2698 if branchrevs:
2698 if branchrevs:
2699 branches = set(repo[br].branch() for br in branchrevs)
2699 branches = set(repo[br].branch() for br in branchrevs)
2700 heads = [h for h in heads if h.branch() in branches]
2700 heads = [h for h in heads if h.branch() in branches]
2701
2701
2702 if opts.get('active') and branchrevs:
2702 if opts.get('active') and branchrevs:
2703 dagheads = repo.heads(start)
2703 dagheads = repo.heads(start)
2704 heads = [h for h in heads if h.node() in dagheads]
2704 heads = [h for h in heads if h.node() in dagheads]
2705
2705
2706 if branchrevs:
2706 if branchrevs:
2707 haveheads = set(h.branch() for h in heads)
2707 haveheads = set(h.branch() for h in heads)
2708 if branches - haveheads:
2708 if branches - haveheads:
2709 headless = ', '.join(b for b in branches - haveheads)
2709 headless = ', '.join(b for b in branches - haveheads)
2710 msg = _('no open branch heads found on branches %s')
2710 msg = _('no open branch heads found on branches %s')
2711 if opts.get('rev'):
2711 if opts.get('rev'):
2712 msg += _(' (started at %s)') % opts['rev']
2712 msg += _(' (started at %s)') % opts['rev']
2713 ui.warn((msg + '\n') % headless)
2713 ui.warn((msg + '\n') % headless)
2714
2714
2715 if not heads:
2715 if not heads:
2716 return 1
2716 return 1
2717
2717
2718 ui.pager('heads')
2718 ui.pager('heads')
2719 heads = sorted(heads, key=lambda x: -x.rev())
2719 heads = sorted(heads, key=lambda x: -x.rev())
2720 displayer = cmdutil.show_changeset(ui, repo, opts)
2720 displayer = cmdutil.show_changeset(ui, repo, opts)
2721 for ctx in heads:
2721 for ctx in heads:
2722 displayer.show(ctx)
2722 displayer.show(ctx)
2723 displayer.close()
2723 displayer.close()
2724
2724
2725 @command('help',
2725 @command('help',
2726 [('e', 'extension', None, _('show only help for extensions')),
2726 [('e', 'extension', None, _('show only help for extensions')),
2727 ('c', 'command', None, _('show only help for commands')),
2727 ('c', 'command', None, _('show only help for commands')),
2728 ('k', 'keyword', None, _('show topics matching keyword')),
2728 ('k', 'keyword', None, _('show topics matching keyword')),
2729 ('s', 'system', [], _('show help for specific platform(s)')),
2729 ('s', 'system', [], _('show help for specific platform(s)')),
2730 ],
2730 ],
2731 _('[-ecks] [TOPIC]'),
2731 _('[-ecks] [TOPIC]'),
2732 norepo=True)
2732 norepo=True)
2733 def help_(ui, name=None, **opts):
2733 def help_(ui, name=None, **opts):
2734 """show help for a given topic or a help overview
2734 """show help for a given topic or a help overview
2735
2735
2736 With no arguments, print a list of commands with short help messages.
2736 With no arguments, print a list of commands with short help messages.
2737
2737
2738 Given a topic, extension, or command name, print help for that
2738 Given a topic, extension, or command name, print help for that
2739 topic.
2739 topic.
2740
2740
2741 Returns 0 if successful.
2741 Returns 0 if successful.
2742 """
2742 """
2743
2743
2744 keep = opts.get('system') or []
2744 keep = opts.get('system') or []
2745 if len(keep) == 0:
2745 if len(keep) == 0:
2746 if pycompat.sysplatform.startswith('win'):
2746 if pycompat.sysplatform.startswith('win'):
2747 keep.append('windows')
2747 keep.append('windows')
2748 elif pycompat.sysplatform == 'OpenVMS':
2748 elif pycompat.sysplatform == 'OpenVMS':
2749 keep.append('vms')
2749 keep.append('vms')
2750 elif pycompat.sysplatform == 'plan9':
2750 elif pycompat.sysplatform == 'plan9':
2751 keep.append('plan9')
2751 keep.append('plan9')
2752 else:
2752 else:
2753 keep.append('unix')
2753 keep.append('unix')
2754 keep.append(pycompat.sysplatform.lower())
2754 keep.append(pycompat.sysplatform.lower())
2755 if ui.verbose:
2755 if ui.verbose:
2756 keep.append('verbose')
2756 keep.append('verbose')
2757
2757
2758 formatted = help.formattedhelp(ui, name, keep=keep, **opts)
2758 formatted = help.formattedhelp(ui, name, keep=keep, **opts)
2759 ui.pager('help')
2759 ui.pager('help')
2760 ui.write(formatted)
2760 ui.write(formatted)
2761
2761
2762
2762
2763 @command('identify|id',
2763 @command('identify|id',
2764 [('r', 'rev', '',
2764 [('r', 'rev', '',
2765 _('identify the specified revision'), _('REV')),
2765 _('identify the specified revision'), _('REV')),
2766 ('n', 'num', None, _('show local revision number')),
2766 ('n', 'num', None, _('show local revision number')),
2767 ('i', 'id', None, _('show global revision id')),
2767 ('i', 'id', None, _('show global revision id')),
2768 ('b', 'branch', None, _('show branch')),
2768 ('b', 'branch', None, _('show branch')),
2769 ('t', 'tags', None, _('show tags')),
2769 ('t', 'tags', None, _('show tags')),
2770 ('B', 'bookmarks', None, _('show bookmarks')),
2770 ('B', 'bookmarks', None, _('show bookmarks')),
2771 ] + remoteopts,
2771 ] + remoteopts,
2772 _('[-nibtB] [-r REV] [SOURCE]'),
2772 _('[-nibtB] [-r REV] [SOURCE]'),
2773 optionalrepo=True)
2773 optionalrepo=True)
2774 def identify(ui, repo, source=None, rev=None,
2774 def identify(ui, repo, source=None, rev=None,
2775 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2775 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2776 """identify the working directory or specified revision
2776 """identify the working directory or specified revision
2777
2777
2778 Print a summary identifying the repository state at REV using one or
2778 Print a summary identifying the repository state at REV using one or
2779 two parent hash identifiers, followed by a "+" if the working
2779 two parent hash identifiers, followed by a "+" if the working
2780 directory has uncommitted changes, the branch name (if not default),
2780 directory has uncommitted changes, the branch name (if not default),
2781 a list of tags, and a list of bookmarks.
2781 a list of tags, and a list of bookmarks.
2782
2782
2783 When REV is not given, print a summary of the current state of the
2783 When REV is not given, print a summary of the current state of the
2784 repository.
2784 repository.
2785
2785
2786 Specifying a path to a repository root or Mercurial bundle will
2786 Specifying a path to a repository root or Mercurial bundle will
2787 cause lookup to operate on that repository/bundle.
2787 cause lookup to operate on that repository/bundle.
2788
2788
2789 .. container:: verbose
2789 .. container:: verbose
2790
2790
2791 Examples:
2791 Examples:
2792
2792
2793 - generate a build identifier for the working directory::
2793 - generate a build identifier for the working directory::
2794
2794
2795 hg id --id > build-id.dat
2795 hg id --id > build-id.dat
2796
2796
2797 - find the revision corresponding to a tag::
2797 - find the revision corresponding to a tag::
2798
2798
2799 hg id -n -r 1.3
2799 hg id -n -r 1.3
2800
2800
2801 - check the most recent revision of a remote repository::
2801 - check the most recent revision of a remote repository::
2802
2802
2803 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2803 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2804
2804
2805 See :hg:`log` for generating more information about specific revisions,
2805 See :hg:`log` for generating more information about specific revisions,
2806 including full hash identifiers.
2806 including full hash identifiers.
2807
2807
2808 Returns 0 if successful.
2808 Returns 0 if successful.
2809 """
2809 """
2810
2810
2811 if not repo and not source:
2811 if not repo and not source:
2812 raise error.Abort(_("there is no Mercurial repository here "
2812 raise error.Abort(_("there is no Mercurial repository here "
2813 "(.hg not found)"))
2813 "(.hg not found)"))
2814
2814
2815 if ui.debugflag:
2815 if ui.debugflag:
2816 hexfunc = hex
2816 hexfunc = hex
2817 else:
2817 else:
2818 hexfunc = short
2818 hexfunc = short
2819 default = not (num or id or branch or tags or bookmarks)
2819 default = not (num or id or branch or tags or bookmarks)
2820 output = []
2820 output = []
2821 revs = []
2821 revs = []
2822
2822
2823 if source:
2823 if source:
2824 source, branches = hg.parseurl(ui.expandpath(source))
2824 source, branches = hg.parseurl(ui.expandpath(source))
2825 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2825 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2826 repo = peer.local()
2826 repo = peer.local()
2827 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2827 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2828
2828
2829 if not repo:
2829 if not repo:
2830 if num or branch or tags:
2830 if num or branch or tags:
2831 raise error.Abort(
2831 raise error.Abort(
2832 _("can't query remote revision number, branch, or tags"))
2832 _("can't query remote revision number, branch, or tags"))
2833 if not rev and revs:
2833 if not rev and revs:
2834 rev = revs[0]
2834 rev = revs[0]
2835 if not rev:
2835 if not rev:
2836 rev = "tip"
2836 rev = "tip"
2837
2837
2838 remoterev = peer.lookup(rev)
2838 remoterev = peer.lookup(rev)
2839 if default or id:
2839 if default or id:
2840 output = [hexfunc(remoterev)]
2840 output = [hexfunc(remoterev)]
2841
2841
2842 def getbms():
2842 def getbms():
2843 bms = []
2843 bms = []
2844
2844
2845 if 'bookmarks' in peer.listkeys('namespaces'):
2845 if 'bookmarks' in peer.listkeys('namespaces'):
2846 hexremoterev = hex(remoterev)
2846 hexremoterev = hex(remoterev)
2847 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2847 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2848 if bmr == hexremoterev]
2848 if bmr == hexremoterev]
2849
2849
2850 return sorted(bms)
2850 return sorted(bms)
2851
2851
2852 if bookmarks:
2852 if bookmarks:
2853 output.extend(getbms())
2853 output.extend(getbms())
2854 elif default and not ui.quiet:
2854 elif default and not ui.quiet:
2855 # multiple bookmarks for a single parent separated by '/'
2855 # multiple bookmarks for a single parent separated by '/'
2856 bm = '/'.join(getbms())
2856 bm = '/'.join(getbms())
2857 if bm:
2857 if bm:
2858 output.append(bm)
2858 output.append(bm)
2859 else:
2859 else:
2860 ctx = scmutil.revsingle(repo, rev, None)
2860 ctx = scmutil.revsingle(repo, rev, None)
2861
2861
2862 if ctx.rev() is None:
2862 if ctx.rev() is None:
2863 ctx = repo[None]
2863 ctx = repo[None]
2864 parents = ctx.parents()
2864 parents = ctx.parents()
2865 taglist = []
2865 taglist = []
2866 for p in parents:
2866 for p in parents:
2867 taglist.extend(p.tags())
2867 taglist.extend(p.tags())
2868
2868
2869 changed = ""
2869 changed = ""
2870 if default or id or num:
2870 if default or id or num:
2871 if (any(repo.status())
2871 if (any(repo.status())
2872 or any(ctx.sub(s).dirty() for s in ctx.substate)):
2872 or any(ctx.sub(s).dirty() for s in ctx.substate)):
2873 changed = '+'
2873 changed = '+'
2874 if default or id:
2874 if default or id:
2875 output = ["%s%s" %
2875 output = ["%s%s" %
2876 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2876 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2877 if num:
2877 if num:
2878 output.append("%s%s" %
2878 output.append("%s%s" %
2879 ('+'.join([str(p.rev()) for p in parents]), changed))
2879 ('+'.join([str(p.rev()) for p in parents]), changed))
2880 else:
2880 else:
2881 if default or id:
2881 if default or id:
2882 output = [hexfunc(ctx.node())]
2882 output = [hexfunc(ctx.node())]
2883 if num:
2883 if num:
2884 output.append(str(ctx.rev()))
2884 output.append(str(ctx.rev()))
2885 taglist = ctx.tags()
2885 taglist = ctx.tags()
2886
2886
2887 if default and not ui.quiet:
2887 if default and not ui.quiet:
2888 b = ctx.branch()
2888 b = ctx.branch()
2889 if b != 'default':
2889 if b != 'default':
2890 output.append("(%s)" % b)
2890 output.append("(%s)" % b)
2891
2891
2892 # multiple tags for a single parent separated by '/'
2892 # multiple tags for a single parent separated by '/'
2893 t = '/'.join(taglist)
2893 t = '/'.join(taglist)
2894 if t:
2894 if t:
2895 output.append(t)
2895 output.append(t)
2896
2896
2897 # multiple bookmarks for a single parent separated by '/'
2897 # multiple bookmarks for a single parent separated by '/'
2898 bm = '/'.join(ctx.bookmarks())
2898 bm = '/'.join(ctx.bookmarks())
2899 if bm:
2899 if bm:
2900 output.append(bm)
2900 output.append(bm)
2901 else:
2901 else:
2902 if branch:
2902 if branch:
2903 output.append(ctx.branch())
2903 output.append(ctx.branch())
2904
2904
2905 if tags:
2905 if tags:
2906 output.extend(taglist)
2906 output.extend(taglist)
2907
2907
2908 if bookmarks:
2908 if bookmarks:
2909 output.extend(ctx.bookmarks())
2909 output.extend(ctx.bookmarks())
2910
2910
2911 ui.write("%s\n" % ' '.join(output))
2911 ui.write("%s\n" % ' '.join(output))
2912
2912
2913 @command('import|patch',
2913 @command('import|patch',
2914 [('p', 'strip', 1,
2914 [('p', 'strip', 1,
2915 _('directory strip option for patch. This has the same '
2915 _('directory strip option for patch. This has the same '
2916 'meaning as the corresponding patch option'), _('NUM')),
2916 'meaning as the corresponding patch option'), _('NUM')),
2917 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2917 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2918 ('e', 'edit', False, _('invoke editor on commit messages')),
2918 ('e', 'edit', False, _('invoke editor on commit messages')),
2919 ('f', 'force', None,
2919 ('f', 'force', None,
2920 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2920 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2921 ('', 'no-commit', None,
2921 ('', 'no-commit', None,
2922 _("don't commit, just update the working directory")),
2922 _("don't commit, just update the working directory")),
2923 ('', 'bypass', None,
2923 ('', 'bypass', None,
2924 _("apply patch without touching the working directory")),
2924 _("apply patch without touching the working directory")),
2925 ('', 'partial', None,
2925 ('', 'partial', None,
2926 _('commit even if some hunks fail')),
2926 _('commit even if some hunks fail')),
2927 ('', 'exact', None,
2927 ('', 'exact', None,
2928 _('abort if patch would apply lossily')),
2928 _('abort if patch would apply lossily')),
2929 ('', 'prefix', '',
2929 ('', 'prefix', '',
2930 _('apply patch to subdirectory'), _('DIR')),
2930 _('apply patch to subdirectory'), _('DIR')),
2931 ('', 'import-branch', None,
2931 ('', 'import-branch', None,
2932 _('use any branch information in patch (implied by --exact)'))] +
2932 _('use any branch information in patch (implied by --exact)'))] +
2933 commitopts + commitopts2 + similarityopts,
2933 commitopts + commitopts2 + similarityopts,
2934 _('[OPTION]... PATCH...'))
2934 _('[OPTION]... PATCH...'))
2935 def import_(ui, repo, patch1=None, *patches, **opts):
2935 def import_(ui, repo, patch1=None, *patches, **opts):
2936 """import an ordered set of patches
2936 """import an ordered set of patches
2937
2937
2938 Import a list of patches and commit them individually (unless
2938 Import a list of patches and commit them individually (unless
2939 --no-commit is specified).
2939 --no-commit is specified).
2940
2940
2941 To read a patch from standard input (stdin), use "-" as the patch
2941 To read a patch from standard input (stdin), use "-" as the patch
2942 name. If a URL is specified, the patch will be downloaded from
2942 name. If a URL is specified, the patch will be downloaded from
2943 there.
2943 there.
2944
2944
2945 Import first applies changes to the working directory (unless
2945 Import first applies changes to the working directory (unless
2946 --bypass is specified), import will abort if there are outstanding
2946 --bypass is specified), import will abort if there are outstanding
2947 changes.
2947 changes.
2948
2948
2949 Use --bypass to apply and commit patches directly to the
2949 Use --bypass to apply and commit patches directly to the
2950 repository, without affecting the working directory. Without
2950 repository, without affecting the working directory. Without
2951 --exact, patches will be applied on top of the working directory
2951 --exact, patches will be applied on top of the working directory
2952 parent revision.
2952 parent revision.
2953
2953
2954 You can import a patch straight from a mail message. Even patches
2954 You can import a patch straight from a mail message. Even patches
2955 as attachments work (to use the body part, it must have type
2955 as attachments work (to use the body part, it must have type
2956 text/plain or text/x-patch). From and Subject headers of email
2956 text/plain or text/x-patch). From and Subject headers of email
2957 message are used as default committer and commit message. All
2957 message are used as default committer and commit message. All
2958 text/plain body parts before first diff are added to the commit
2958 text/plain body parts before first diff are added to the commit
2959 message.
2959 message.
2960
2960
2961 If the imported patch was generated by :hg:`export`, user and
2961 If the imported patch was generated by :hg:`export`, user and
2962 description from patch override values from message headers and
2962 description from patch override values from message headers and
2963 body. Values given on command line with -m/--message and -u/--user
2963 body. Values given on command line with -m/--message and -u/--user
2964 override these.
2964 override these.
2965
2965
2966 If --exact is specified, import will set the working directory to
2966 If --exact is specified, import will set the working directory to
2967 the parent of each patch before applying it, and will abort if the
2967 the parent of each patch before applying it, and will abort if the
2968 resulting changeset has a different ID than the one recorded in
2968 resulting changeset has a different ID than the one recorded in
2969 the patch. This will guard against various ways that portable
2969 the patch. This will guard against various ways that portable
2970 patch formats and mail systems might fail to transfer Mercurial
2970 patch formats and mail systems might fail to transfer Mercurial
2971 data or metadata. See :hg:`bundle` for lossless transmission.
2971 data or metadata. See :hg:`bundle` for lossless transmission.
2972
2972
2973 Use --partial to ensure a changeset will be created from the patch
2973 Use --partial to ensure a changeset will be created from the patch
2974 even if some hunks fail to apply. Hunks that fail to apply will be
2974 even if some hunks fail to apply. Hunks that fail to apply will be
2975 written to a <target-file>.rej file. Conflicts can then be resolved
2975 written to a <target-file>.rej file. Conflicts can then be resolved
2976 by hand before :hg:`commit --amend` is run to update the created
2976 by hand before :hg:`commit --amend` is run to update the created
2977 changeset. This flag exists to let people import patches that
2977 changeset. This flag exists to let people import patches that
2978 partially apply without losing the associated metadata (author,
2978 partially apply without losing the associated metadata (author,
2979 date, description, ...).
2979 date, description, ...).
2980
2980
2981 .. note::
2981 .. note::
2982
2982
2983 When no hunks apply cleanly, :hg:`import --partial` will create
2983 When no hunks apply cleanly, :hg:`import --partial` will create
2984 an empty changeset, importing only the patch metadata.
2984 an empty changeset, importing only the patch metadata.
2985
2985
2986 With -s/--similarity, hg will attempt to discover renames and
2986 With -s/--similarity, hg will attempt to discover renames and
2987 copies in the patch in the same way as :hg:`addremove`.
2987 copies in the patch in the same way as :hg:`addremove`.
2988
2988
2989 It is possible to use external patch programs to perform the patch
2989 It is possible to use external patch programs to perform the patch
2990 by setting the ``ui.patch`` configuration option. For the default
2990 by setting the ``ui.patch`` configuration option. For the default
2991 internal tool, the fuzz can also be configured via ``patch.fuzz``.
2991 internal tool, the fuzz can also be configured via ``patch.fuzz``.
2992 See :hg:`help config` for more information about configuration
2992 See :hg:`help config` for more information about configuration
2993 files and how to use these options.
2993 files and how to use these options.
2994
2994
2995 See :hg:`help dates` for a list of formats valid for -d/--date.
2995 See :hg:`help dates` for a list of formats valid for -d/--date.
2996
2996
2997 .. container:: verbose
2997 .. container:: verbose
2998
2998
2999 Examples:
2999 Examples:
3000
3000
3001 - import a traditional patch from a website and detect renames::
3001 - import a traditional patch from a website and detect renames::
3002
3002
3003 hg import -s 80 http://example.com/bugfix.patch
3003 hg import -s 80 http://example.com/bugfix.patch
3004
3004
3005 - import a changeset from an hgweb server::
3005 - import a changeset from an hgweb server::
3006
3006
3007 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3007 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3008
3008
3009 - import all the patches in an Unix-style mbox::
3009 - import all the patches in an Unix-style mbox::
3010
3010
3011 hg import incoming-patches.mbox
3011 hg import incoming-patches.mbox
3012
3012
3013 - import patches from stdin::
3013 - import patches from stdin::
3014
3014
3015 hg import -
3015 hg import -
3016
3016
3017 - attempt to exactly restore an exported changeset (not always
3017 - attempt to exactly restore an exported changeset (not always
3018 possible)::
3018 possible)::
3019
3019
3020 hg import --exact proposed-fix.patch
3020 hg import --exact proposed-fix.patch
3021
3021
3022 - use an external tool to apply a patch which is too fuzzy for
3022 - use an external tool to apply a patch which is too fuzzy for
3023 the default internal tool.
3023 the default internal tool.
3024
3024
3025 hg import --config ui.patch="patch --merge" fuzzy.patch
3025 hg import --config ui.patch="patch --merge" fuzzy.patch
3026
3026
3027 - change the default fuzzing from 2 to a less strict 7
3027 - change the default fuzzing from 2 to a less strict 7
3028
3028
3029 hg import --config ui.fuzz=7 fuzz.patch
3029 hg import --config ui.fuzz=7 fuzz.patch
3030
3030
3031 Returns 0 on success, 1 on partial success (see --partial).
3031 Returns 0 on success, 1 on partial success (see --partial).
3032 """
3032 """
3033
3033
3034 if not patch1:
3034 if not patch1:
3035 raise error.Abort(_('need at least one patch to import'))
3035 raise error.Abort(_('need at least one patch to import'))
3036
3036
3037 patches = (patch1,) + patches
3037 patches = (patch1,) + patches
3038
3038
3039 date = opts.get('date')
3039 date = opts.get('date')
3040 if date:
3040 if date:
3041 opts['date'] = util.parsedate(date)
3041 opts['date'] = util.parsedate(date)
3042
3042
3043 exact = opts.get('exact')
3043 exact = opts.get('exact')
3044 update = not opts.get('bypass')
3044 update = not opts.get('bypass')
3045 if not update and opts.get('no_commit'):
3045 if not update and opts.get('no_commit'):
3046 raise error.Abort(_('cannot use --no-commit with --bypass'))
3046 raise error.Abort(_('cannot use --no-commit with --bypass'))
3047 try:
3047 try:
3048 sim = float(opts.get('similarity') or 0)
3048 sim = float(opts.get('similarity') or 0)
3049 except ValueError:
3049 except ValueError:
3050 raise error.Abort(_('similarity must be a number'))
3050 raise error.Abort(_('similarity must be a number'))
3051 if sim < 0 or sim > 100:
3051 if sim < 0 or sim > 100:
3052 raise error.Abort(_('similarity must be between 0 and 100'))
3052 raise error.Abort(_('similarity must be between 0 and 100'))
3053 if sim and not update:
3053 if sim and not update:
3054 raise error.Abort(_('cannot use --similarity with --bypass'))
3054 raise error.Abort(_('cannot use --similarity with --bypass'))
3055 if exact:
3055 if exact:
3056 if opts.get('edit'):
3056 if opts.get('edit'):
3057 raise error.Abort(_('cannot use --exact with --edit'))
3057 raise error.Abort(_('cannot use --exact with --edit'))
3058 if opts.get('prefix'):
3058 if opts.get('prefix'):
3059 raise error.Abort(_('cannot use --exact with --prefix'))
3059 raise error.Abort(_('cannot use --exact with --prefix'))
3060
3060
3061 base = opts["base"]
3061 base = opts["base"]
3062 wlock = dsguard = lock = tr = None
3062 wlock = dsguard = lock = tr = None
3063 msgs = []
3063 msgs = []
3064 ret = 0
3064 ret = 0
3065
3065
3066
3066
3067 try:
3067 try:
3068 wlock = repo.wlock()
3068 wlock = repo.wlock()
3069
3069
3070 if update:
3070 if update:
3071 cmdutil.checkunfinished(repo)
3071 cmdutil.checkunfinished(repo)
3072 if (exact or not opts.get('force')):
3072 if (exact or not opts.get('force')):
3073 cmdutil.bailifchanged(repo)
3073 cmdutil.bailifchanged(repo)
3074
3074
3075 if not opts.get('no_commit'):
3075 if not opts.get('no_commit'):
3076 lock = repo.lock()
3076 lock = repo.lock()
3077 tr = repo.transaction('import')
3077 tr = repo.transaction('import')
3078 else:
3078 else:
3079 dsguard = dirstateguard.dirstateguard(repo, 'import')
3079 dsguard = dirstateguard.dirstateguard(repo, 'import')
3080 parents = repo[None].parents()
3080 parents = repo[None].parents()
3081 for patchurl in patches:
3081 for patchurl in patches:
3082 if patchurl == '-':
3082 if patchurl == '-':
3083 ui.status(_('applying patch from stdin\n'))
3083 ui.status(_('applying patch from stdin\n'))
3084 patchfile = ui.fin
3084 patchfile = ui.fin
3085 patchurl = 'stdin' # for error message
3085 patchurl = 'stdin' # for error message
3086 else:
3086 else:
3087 patchurl = os.path.join(base, patchurl)
3087 patchurl = os.path.join(base, patchurl)
3088 ui.status(_('applying %s\n') % patchurl)
3088 ui.status(_('applying %s\n') % patchurl)
3089 patchfile = hg.openpath(ui, patchurl)
3089 patchfile = hg.openpath(ui, patchurl)
3090
3090
3091 haspatch = False
3091 haspatch = False
3092 for hunk in patch.split(patchfile):
3092 for hunk in patch.split(patchfile):
3093 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3093 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3094 parents, opts,
3094 parents, opts,
3095 msgs, hg.clean)
3095 msgs, hg.clean)
3096 if msg:
3096 if msg:
3097 haspatch = True
3097 haspatch = True
3098 ui.note(msg + '\n')
3098 ui.note(msg + '\n')
3099 if update or exact:
3099 if update or exact:
3100 parents = repo[None].parents()
3100 parents = repo[None].parents()
3101 else:
3101 else:
3102 parents = [repo[node]]
3102 parents = [repo[node]]
3103 if rej:
3103 if rej:
3104 ui.write_err(_("patch applied partially\n"))
3104 ui.write_err(_("patch applied partially\n"))
3105 ui.write_err(_("(fix the .rej files and run "
3105 ui.write_err(_("(fix the .rej files and run "
3106 "`hg commit --amend`)\n"))
3106 "`hg commit --amend`)\n"))
3107 ret = 1
3107 ret = 1
3108 break
3108 break
3109
3109
3110 if not haspatch:
3110 if not haspatch:
3111 raise error.Abort(_('%s: no diffs found') % patchurl)
3111 raise error.Abort(_('%s: no diffs found') % patchurl)
3112
3112
3113 if tr:
3113 if tr:
3114 tr.close()
3114 tr.close()
3115 if msgs:
3115 if msgs:
3116 repo.savecommitmessage('\n* * *\n'.join(msgs))
3116 repo.savecommitmessage('\n* * *\n'.join(msgs))
3117 if dsguard:
3117 if dsguard:
3118 dsguard.close()
3118 dsguard.close()
3119 return ret
3119 return ret
3120 finally:
3120 finally:
3121 if tr:
3121 if tr:
3122 tr.release()
3122 tr.release()
3123 release(lock, dsguard, wlock)
3123 release(lock, dsguard, wlock)
3124
3124
3125 @command('incoming|in',
3125 @command('incoming|in',
3126 [('f', 'force', None,
3126 [('f', 'force', None,
3127 _('run even if remote repository is unrelated')),
3127 _('run even if remote repository is unrelated')),
3128 ('n', 'newest-first', None, _('show newest record first')),
3128 ('n', 'newest-first', None, _('show newest record first')),
3129 ('', 'bundle', '',
3129 ('', 'bundle', '',
3130 _('file to store the bundles into'), _('FILE')),
3130 _('file to store the bundles into'), _('FILE')),
3131 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3131 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3132 ('B', 'bookmarks', False, _("compare bookmarks")),
3132 ('B', 'bookmarks', False, _("compare bookmarks")),
3133 ('b', 'branch', [],
3133 ('b', 'branch', [],
3134 _('a specific branch you would like to pull'), _('BRANCH')),
3134 _('a specific branch you would like to pull'), _('BRANCH')),
3135 ] + logopts + remoteopts + subrepoopts,
3135 ] + logopts + remoteopts + subrepoopts,
3136 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3136 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3137 def incoming(ui, repo, source="default", **opts):
3137 def incoming(ui, repo, source="default", **opts):
3138 """show new changesets found in source
3138 """show new changesets found in source
3139
3139
3140 Show new changesets found in the specified path/URL or the default
3140 Show new changesets found in the specified path/URL or the default
3141 pull location. These are the changesets that would have been pulled
3141 pull location. These are the changesets that would have been pulled
3142 if a pull at the time you issued this command.
3142 if a pull at the time you issued this command.
3143
3143
3144 See pull for valid source format details.
3144 See pull for valid source format details.
3145
3145
3146 .. container:: verbose
3146 .. container:: verbose
3147
3147
3148 With -B/--bookmarks, the result of bookmark comparison between
3148 With -B/--bookmarks, the result of bookmark comparison between
3149 local and remote repositories is displayed. With -v/--verbose,
3149 local and remote repositories is displayed. With -v/--verbose,
3150 status is also displayed for each bookmark like below::
3150 status is also displayed for each bookmark like below::
3151
3151
3152 BM1 01234567890a added
3152 BM1 01234567890a added
3153 BM2 1234567890ab advanced
3153 BM2 1234567890ab advanced
3154 BM3 234567890abc diverged
3154 BM3 234567890abc diverged
3155 BM4 34567890abcd changed
3155 BM4 34567890abcd changed
3156
3156
3157 The action taken locally when pulling depends on the
3157 The action taken locally when pulling depends on the
3158 status of each bookmark:
3158 status of each bookmark:
3159
3159
3160 :``added``: pull will create it
3160 :``added``: pull will create it
3161 :``advanced``: pull will update it
3161 :``advanced``: pull will update it
3162 :``diverged``: pull will create a divergent bookmark
3162 :``diverged``: pull will create a divergent bookmark
3163 :``changed``: result depends on remote changesets
3163 :``changed``: result depends on remote changesets
3164
3164
3165 From the point of view of pulling behavior, bookmark
3165 From the point of view of pulling behavior, bookmark
3166 existing only in the remote repository are treated as ``added``,
3166 existing only in the remote repository are treated as ``added``,
3167 even if it is in fact locally deleted.
3167 even if it is in fact locally deleted.
3168
3168
3169 .. container:: verbose
3169 .. container:: verbose
3170
3170
3171 For remote repository, using --bundle avoids downloading the
3171 For remote repository, using --bundle avoids downloading the
3172 changesets twice if the incoming is followed by a pull.
3172 changesets twice if the incoming is followed by a pull.
3173
3173
3174 Examples:
3174 Examples:
3175
3175
3176 - show incoming changes with patches and full description::
3176 - show incoming changes with patches and full description::
3177
3177
3178 hg incoming -vp
3178 hg incoming -vp
3179
3179
3180 - show incoming changes excluding merges, store a bundle::
3180 - show incoming changes excluding merges, store a bundle::
3181
3181
3182 hg in -vpM --bundle incoming.hg
3182 hg in -vpM --bundle incoming.hg
3183 hg pull incoming.hg
3183 hg pull incoming.hg
3184
3184
3185 - briefly list changes inside a bundle::
3185 - briefly list changes inside a bundle::
3186
3186
3187 hg in changes.hg -T "{desc|firstline}\\n"
3187 hg in changes.hg -T "{desc|firstline}\\n"
3188
3188
3189 Returns 0 if there are incoming changes, 1 otherwise.
3189 Returns 0 if there are incoming changes, 1 otherwise.
3190 """
3190 """
3191 opts = pycompat.byteskwargs(opts)
3191 opts = pycompat.byteskwargs(opts)
3192 if opts.get('graph'):
3192 if opts.get('graph'):
3193 cmdutil.checkunsupportedgraphflags([], opts)
3193 cmdutil.checkunsupportedgraphflags([], opts)
3194 def display(other, chlist, displayer):
3194 def display(other, chlist, displayer):
3195 revdag = cmdutil.graphrevs(other, chlist, opts)
3195 revdag = cmdutil.graphrevs(other, chlist, opts)
3196 cmdutil.displaygraph(ui, repo, revdag, displayer,
3196 cmdutil.displaygraph(ui, repo, revdag, displayer,
3197 graphmod.asciiedges)
3197 graphmod.asciiedges)
3198
3198
3199 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3199 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3200 return 0
3200 return 0
3201
3201
3202 if opts.get('bundle') and opts.get('subrepos'):
3202 if opts.get('bundle') and opts.get('subrepos'):
3203 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3203 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3204
3204
3205 if opts.get('bookmarks'):
3205 if opts.get('bookmarks'):
3206 source, branches = hg.parseurl(ui.expandpath(source),
3206 source, branches = hg.parseurl(ui.expandpath(source),
3207 opts.get('branch'))
3207 opts.get('branch'))
3208 other = hg.peer(repo, opts, source)
3208 other = hg.peer(repo, opts, source)
3209 if 'bookmarks' not in other.listkeys('namespaces'):
3209 if 'bookmarks' not in other.listkeys('namespaces'):
3210 ui.warn(_("remote doesn't support bookmarks\n"))
3210 ui.warn(_("remote doesn't support bookmarks\n"))
3211 return 0
3211 return 0
3212 ui.pager('incoming')
3212 ui.pager('incoming')
3213 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3213 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3214 return bookmarks.incoming(ui, repo, other)
3214 return bookmarks.incoming(ui, repo, other)
3215
3215
3216 repo._subtoppath = ui.expandpath(source)
3216 repo._subtoppath = ui.expandpath(source)
3217 try:
3217 try:
3218 return hg.incoming(ui, repo, source, opts)
3218 return hg.incoming(ui, repo, source, opts)
3219 finally:
3219 finally:
3220 del repo._subtoppath
3220 del repo._subtoppath
3221
3221
3222
3222
3223 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3223 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3224 norepo=True)
3224 norepo=True)
3225 def init(ui, dest=".", **opts):
3225 def init(ui, dest=".", **opts):
3226 """create a new repository in the given directory
3226 """create a new repository in the given directory
3227
3227
3228 Initialize a new repository in the given directory. If the given
3228 Initialize a new repository in the given directory. If the given
3229 directory does not exist, it will be created.
3229 directory does not exist, it will be created.
3230
3230
3231 If no directory is given, the current directory is used.
3231 If no directory is given, the current directory is used.
3232
3232
3233 It is possible to specify an ``ssh://`` URL as the destination.
3233 It is possible to specify an ``ssh://`` URL as the destination.
3234 See :hg:`help urls` for more information.
3234 See :hg:`help urls` for more information.
3235
3235
3236 Returns 0 on success.
3236 Returns 0 on success.
3237 """
3237 """
3238 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3238 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3239
3239
3240 @command('locate',
3240 @command('locate',
3241 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3241 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3242 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3242 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3243 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3243 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3244 ] + walkopts,
3244 ] + walkopts,
3245 _('[OPTION]... [PATTERN]...'))
3245 _('[OPTION]... [PATTERN]...'))
3246 def locate(ui, repo, *pats, **opts):
3246 def locate(ui, repo, *pats, **opts):
3247 """locate files matching specific patterns (DEPRECATED)
3247 """locate files matching specific patterns (DEPRECATED)
3248
3248
3249 Print files under Mercurial control in the working directory whose
3249 Print files under Mercurial control in the working directory whose
3250 names match the given patterns.
3250 names match the given patterns.
3251
3251
3252 By default, this command searches all directories in the working
3252 By default, this command searches all directories in the working
3253 directory. To search just the current directory and its
3253 directory. To search just the current directory and its
3254 subdirectories, use "--include .".
3254 subdirectories, use "--include .".
3255
3255
3256 If no patterns are given to match, this command prints the names
3256 If no patterns are given to match, this command prints the names
3257 of all files under Mercurial control in the working directory.
3257 of all files under Mercurial control in the working directory.
3258
3258
3259 If you want to feed the output of this command into the "xargs"
3259 If you want to feed the output of this command into the "xargs"
3260 command, use the -0 option to both this command and "xargs". This
3260 command, use the -0 option to both this command and "xargs". This
3261 will avoid the problem of "xargs" treating single filenames that
3261 will avoid the problem of "xargs" treating single filenames that
3262 contain whitespace as multiple filenames.
3262 contain whitespace as multiple filenames.
3263
3263
3264 See :hg:`help files` for a more versatile command.
3264 See :hg:`help files` for a more versatile command.
3265
3265
3266 Returns 0 if a match is found, 1 otherwise.
3266 Returns 0 if a match is found, 1 otherwise.
3267 """
3267 """
3268 if opts.get('print0'):
3268 if opts.get('print0'):
3269 end = '\0'
3269 end = '\0'
3270 else:
3270 else:
3271 end = '\n'
3271 end = '\n'
3272 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3272 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3273
3273
3274 ret = 1
3274 ret = 1
3275 ctx = repo[rev]
3275 ctx = repo[rev]
3276 m = scmutil.match(ctx, pats, opts, default='relglob',
3276 m = scmutil.match(ctx, pats, opts, default='relglob',
3277 badfn=lambda x, y: False)
3277 badfn=lambda x, y: False)
3278
3278
3279 ui.pager('locate')
3279 ui.pager('locate')
3280 for abs in ctx.matches(m):
3280 for abs in ctx.matches(m):
3281 if opts.get('fullpath'):
3281 if opts.get('fullpath'):
3282 ui.write(repo.wjoin(abs), end)
3282 ui.write(repo.wjoin(abs), end)
3283 else:
3283 else:
3284 ui.write(((pats and m.rel(abs)) or abs), end)
3284 ui.write(((pats and m.rel(abs)) or abs), end)
3285 ret = 0
3285 ret = 0
3286
3286
3287 return ret
3287 return ret
3288
3288
3289 @command('^log|history',
3289 @command('^log|history',
3290 [('f', 'follow', None,
3290 [('f', 'follow', None,
3291 _('follow changeset history, or file history across copies and renames')),
3291 _('follow changeset history, or file history across copies and renames')),
3292 ('', 'follow-first', None,
3292 ('', 'follow-first', None,
3293 _('only follow the first parent of merge changesets (DEPRECATED)')),
3293 _('only follow the first parent of merge changesets (DEPRECATED)')),
3294 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3294 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3295 ('C', 'copies', None, _('show copied files')),
3295 ('C', 'copies', None, _('show copied files')),
3296 ('k', 'keyword', [],
3296 ('k', 'keyword', [],
3297 _('do case-insensitive search for a given text'), _('TEXT')),
3297 _('do case-insensitive search for a given text'), _('TEXT')),
3298 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3298 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3299 ('', 'removed', None, _('include revisions where files were removed')),
3299 ('', 'removed', None, _('include revisions where files were removed')),
3300 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3300 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3301 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3301 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3302 ('', 'only-branch', [],
3302 ('', 'only-branch', [],
3303 _('show only changesets within the given named branch (DEPRECATED)'),
3303 _('show only changesets within the given named branch (DEPRECATED)'),
3304 _('BRANCH')),
3304 _('BRANCH')),
3305 ('b', 'branch', [],
3305 ('b', 'branch', [],
3306 _('show changesets within the given named branch'), _('BRANCH')),
3306 _('show changesets within the given named branch'), _('BRANCH')),
3307 ('P', 'prune', [],
3307 ('P', 'prune', [],
3308 _('do not display revision or any of its ancestors'), _('REV')),
3308 _('do not display revision or any of its ancestors'), _('REV')),
3309 ] + logopts + walkopts,
3309 ] + logopts + walkopts,
3310 _('[OPTION]... [FILE]'),
3310 _('[OPTION]... [FILE]'),
3311 inferrepo=True)
3311 inferrepo=True)
3312 def log(ui, repo, *pats, **opts):
3312 def log(ui, repo, *pats, **opts):
3313 """show revision history of entire repository or files
3313 """show revision history of entire repository or files
3314
3314
3315 Print the revision history of the specified files or the entire
3315 Print the revision history of the specified files or the entire
3316 project.
3316 project.
3317
3317
3318 If no revision range is specified, the default is ``tip:0`` unless
3318 If no revision range is specified, the default is ``tip:0`` unless
3319 --follow is set, in which case the working directory parent is
3319 --follow is set, in which case the working directory parent is
3320 used as the starting revision.
3320 used as the starting revision.
3321
3321
3322 File history is shown without following rename or copy history of
3322 File history is shown without following rename or copy history of
3323 files. Use -f/--follow with a filename to follow history across
3323 files. Use -f/--follow with a filename to follow history across
3324 renames and copies. --follow without a filename will only show
3324 renames and copies. --follow without a filename will only show
3325 ancestors or descendants of the starting revision.
3325 ancestors or descendants of the starting revision.
3326
3326
3327 By default this command prints revision number and changeset id,
3327 By default this command prints revision number and changeset id,
3328 tags, non-trivial parents, user, date and time, and a summary for
3328 tags, non-trivial parents, user, date and time, and a summary for
3329 each commit. When the -v/--verbose switch is used, the list of
3329 each commit. When the -v/--verbose switch is used, the list of
3330 changed files and full commit message are shown.
3330 changed files and full commit message are shown.
3331
3331
3332 With --graph the revisions are shown as an ASCII art DAG with the most
3332 With --graph the revisions are shown as an ASCII art DAG with the most
3333 recent changeset at the top.
3333 recent changeset at the top.
3334 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
3334 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
3335 and '+' represents a fork where the changeset from the lines below is a
3335 and '+' represents a fork where the changeset from the lines below is a
3336 parent of the 'o' merge on the same line.
3336 parent of the 'o' merge on the same line.
3337
3337
3338 .. note::
3338 .. note::
3339
3339
3340 :hg:`log --patch` may generate unexpected diff output for merge
3340 :hg:`log --patch` may generate unexpected diff output for merge
3341 changesets, as it will only compare the merge changeset against
3341 changesets, as it will only compare the merge changeset against
3342 its first parent. Also, only files different from BOTH parents
3342 its first parent. Also, only files different from BOTH parents
3343 will appear in files:.
3343 will appear in files:.
3344
3344
3345 .. note::
3345 .. note::
3346
3346
3347 For performance reasons, :hg:`log FILE` may omit duplicate changes
3347 For performance reasons, :hg:`log FILE` may omit duplicate changes
3348 made on branches and will not show removals or mode changes. To
3348 made on branches and will not show removals or mode changes. To
3349 see all such changes, use the --removed switch.
3349 see all such changes, use the --removed switch.
3350
3350
3351 .. container:: verbose
3351 .. container:: verbose
3352
3352
3353 Some examples:
3353 Some examples:
3354
3354
3355 - changesets with full descriptions and file lists::
3355 - changesets with full descriptions and file lists::
3356
3356
3357 hg log -v
3357 hg log -v
3358
3358
3359 - changesets ancestral to the working directory::
3359 - changesets ancestral to the working directory::
3360
3360
3361 hg log -f
3361 hg log -f
3362
3362
3363 - last 10 commits on the current branch::
3363 - last 10 commits on the current branch::
3364
3364
3365 hg log -l 10 -b .
3365 hg log -l 10 -b .
3366
3366
3367 - changesets showing all modifications of a file, including removals::
3367 - changesets showing all modifications of a file, including removals::
3368
3368
3369 hg log --removed file.c
3369 hg log --removed file.c
3370
3370
3371 - all changesets that touch a directory, with diffs, excluding merges::
3371 - all changesets that touch a directory, with diffs, excluding merges::
3372
3372
3373 hg log -Mp lib/
3373 hg log -Mp lib/
3374
3374
3375 - all revision numbers that match a keyword::
3375 - all revision numbers that match a keyword::
3376
3376
3377 hg log -k bug --template "{rev}\\n"
3377 hg log -k bug --template "{rev}\\n"
3378
3378
3379 - the full hash identifier of the working directory parent::
3379 - the full hash identifier of the working directory parent::
3380
3380
3381 hg log -r . --template "{node}\\n"
3381 hg log -r . --template "{node}\\n"
3382
3382
3383 - list available log templates::
3383 - list available log templates::
3384
3384
3385 hg log -T list
3385 hg log -T list
3386
3386
3387 - check if a given changeset is included in a tagged release::
3387 - check if a given changeset is included in a tagged release::
3388
3388
3389 hg log -r "a21ccf and ancestor(1.9)"
3389 hg log -r "a21ccf and ancestor(1.9)"
3390
3390
3391 - find all changesets by some user in a date range::
3391 - find all changesets by some user in a date range::
3392
3392
3393 hg log -k alice -d "may 2008 to jul 2008"
3393 hg log -k alice -d "may 2008 to jul 2008"
3394
3394
3395 - summary of all changesets after the last tag::
3395 - summary of all changesets after the last tag::
3396
3396
3397 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3397 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3398
3398
3399 See :hg:`help dates` for a list of formats valid for -d/--date.
3399 See :hg:`help dates` for a list of formats valid for -d/--date.
3400
3400
3401 See :hg:`help revisions` for more about specifying and ordering
3401 See :hg:`help revisions` for more about specifying and ordering
3402 revisions.
3402 revisions.
3403
3403
3404 See :hg:`help templates` for more about pre-packaged styles and
3404 See :hg:`help templates` for more about pre-packaged styles and
3405 specifying custom templates.
3405 specifying custom templates.
3406
3406
3407 Returns 0 on success.
3407 Returns 0 on success.
3408
3408
3409 """
3409 """
3410 opts = pycompat.byteskwargs(opts)
3410 opts = pycompat.byteskwargs(opts)
3411 if opts.get('follow') and opts.get('rev'):
3411 if opts.get('follow') and opts.get('rev'):
3412 opts['rev'] = [revsetlang.formatspec('reverse(::%lr)', opts.get('rev'))]
3412 opts['rev'] = [revsetlang.formatspec('reverse(::%lr)', opts.get('rev'))]
3413 del opts['follow']
3413 del opts['follow']
3414
3414
3415 if opts.get('graph'):
3415 if opts.get('graph'):
3416 return cmdutil.graphlog(ui, repo, pats, opts)
3416 return cmdutil.graphlog(ui, repo, pats, opts)
3417
3417
3418 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
3418 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
3419 limit = cmdutil.loglimit(opts)
3419 limit = cmdutil.loglimit(opts)
3420 count = 0
3420 count = 0
3421
3421
3422 getrenamed = None
3422 getrenamed = None
3423 if opts.get('copies'):
3423 if opts.get('copies'):
3424 endrev = None
3424 endrev = None
3425 if opts.get('rev'):
3425 if opts.get('rev'):
3426 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3426 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3427 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3427 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3428
3428
3429 ui.pager('log')
3429 ui.pager('log')
3430 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3430 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3431 for rev in revs:
3431 for rev in revs:
3432 if count == limit:
3432 if count == limit:
3433 break
3433 break
3434 ctx = repo[rev]
3434 ctx = repo[rev]
3435 copies = None
3435 copies = None
3436 if getrenamed is not None and rev:
3436 if getrenamed is not None and rev:
3437 copies = []
3437 copies = []
3438 for fn in ctx.files():
3438 for fn in ctx.files():
3439 rename = getrenamed(fn, rev)
3439 rename = getrenamed(fn, rev)
3440 if rename:
3440 if rename:
3441 copies.append((fn, rename[0]))
3441 copies.append((fn, rename[0]))
3442 if filematcher:
3442 if filematcher:
3443 revmatchfn = filematcher(ctx.rev())
3443 revmatchfn = filematcher(ctx.rev())
3444 else:
3444 else:
3445 revmatchfn = None
3445 revmatchfn = None
3446 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3446 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3447 if displayer.flush(ctx):
3447 if displayer.flush(ctx):
3448 count += 1
3448 count += 1
3449
3449
3450 displayer.close()
3450 displayer.close()
3451
3451
3452 @command('manifest',
3452 @command('manifest',
3453 [('r', 'rev', '', _('revision to display'), _('REV')),
3453 [('r', 'rev', '', _('revision to display'), _('REV')),
3454 ('', 'all', False, _("list files from all revisions"))]
3454 ('', 'all', False, _("list files from all revisions"))]
3455 + formatteropts,
3455 + formatteropts,
3456 _('[-r REV]'))
3456 _('[-r REV]'))
3457 def manifest(ui, repo, node=None, rev=None, **opts):
3457 def manifest(ui, repo, node=None, rev=None, **opts):
3458 """output the current or given revision of the project manifest
3458 """output the current or given revision of the project manifest
3459
3459
3460 Print a list of version controlled files for the given revision.
3460 Print a list of version controlled files for the given revision.
3461 If no revision is given, the first parent of the working directory
3461 If no revision is given, the first parent of the working directory
3462 is used, or the null revision if no revision is checked out.
3462 is used, or the null revision if no revision is checked out.
3463
3463
3464 With -v, print file permissions, symlink and executable bits.
3464 With -v, print file permissions, symlink and executable bits.
3465 With --debug, print file revision hashes.
3465 With --debug, print file revision hashes.
3466
3466
3467 If option --all is specified, the list of all files from all revisions
3467 If option --all is specified, the list of all files from all revisions
3468 is printed. This includes deleted and renamed files.
3468 is printed. This includes deleted and renamed files.
3469
3469
3470 Returns 0 on success.
3470 Returns 0 on success.
3471 """
3471 """
3472 fm = ui.formatter('manifest', opts)
3472 fm = ui.formatter('manifest', opts)
3473
3473
3474 if opts.get('all'):
3474 if opts.get('all'):
3475 if rev or node:
3475 if rev or node:
3476 raise error.Abort(_("can't specify a revision with --all"))
3476 raise error.Abort(_("can't specify a revision with --all"))
3477
3477
3478 res = []
3478 res = []
3479 prefix = "data/"
3479 prefix = "data/"
3480 suffix = ".i"
3480 suffix = ".i"
3481 plen = len(prefix)
3481 plen = len(prefix)
3482 slen = len(suffix)
3482 slen = len(suffix)
3483 with repo.lock():
3483 with repo.lock():
3484 for fn, b, size in repo.store.datafiles():
3484 for fn, b, size in repo.store.datafiles():
3485 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3485 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3486 res.append(fn[plen:-slen])
3486 res.append(fn[plen:-slen])
3487 ui.pager('manifest')
3487 ui.pager('manifest')
3488 for f in res:
3488 for f in res:
3489 fm.startitem()
3489 fm.startitem()
3490 fm.write("path", '%s\n', f)
3490 fm.write("path", '%s\n', f)
3491 fm.end()
3491 fm.end()
3492 return
3492 return
3493
3493
3494 if rev and node:
3494 if rev and node:
3495 raise error.Abort(_("please specify just one revision"))
3495 raise error.Abort(_("please specify just one revision"))
3496
3496
3497 if not node:
3497 if not node:
3498 node = rev
3498 node = rev
3499
3499
3500 char = {'l': '@', 'x': '*', '': ''}
3500 char = {'l': '@', 'x': '*', '': ''}
3501 mode = {'l': '644', 'x': '755', '': '644'}
3501 mode = {'l': '644', 'x': '755', '': '644'}
3502 ctx = scmutil.revsingle(repo, node)
3502 ctx = scmutil.revsingle(repo, node)
3503 mf = ctx.manifest()
3503 mf = ctx.manifest()
3504 ui.pager('manifest')
3504 ui.pager('manifest')
3505 for f in ctx:
3505 for f in ctx:
3506 fm.startitem()
3506 fm.startitem()
3507 fl = ctx[f].flags()
3507 fl = ctx[f].flags()
3508 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3508 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3509 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3509 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3510 fm.write('path', '%s\n', f)
3510 fm.write('path', '%s\n', f)
3511 fm.end()
3511 fm.end()
3512
3512
3513 @command('^merge',
3513 @command('^merge',
3514 [('f', 'force', None,
3514 [('f', 'force', None,
3515 _('force a merge including outstanding changes (DEPRECATED)')),
3515 _('force a merge including outstanding changes (DEPRECATED)')),
3516 ('r', 'rev', '', _('revision to merge'), _('REV')),
3516 ('r', 'rev', '', _('revision to merge'), _('REV')),
3517 ('P', 'preview', None,
3517 ('P', 'preview', None,
3518 _('review revisions to merge (no merge is performed)'))
3518 _('review revisions to merge (no merge is performed)'))
3519 ] + mergetoolopts,
3519 ] + mergetoolopts,
3520 _('[-P] [[-r] REV]'))
3520 _('[-P] [[-r] REV]'))
3521 def merge(ui, repo, node=None, **opts):
3521 def merge(ui, repo, node=None, **opts):
3522 """merge another revision into working directory
3522 """merge another revision into working directory
3523
3523
3524 The current working directory is updated with all changes made in
3524 The current working directory is updated with all changes made in
3525 the requested revision since the last common predecessor revision.
3525 the requested revision since the last common predecessor revision.
3526
3526
3527 Files that changed between either parent are marked as changed for
3527 Files that changed between either parent are marked as changed for
3528 the next commit and a commit must be performed before any further
3528 the next commit and a commit must be performed before any further
3529 updates to the repository are allowed. The next commit will have
3529 updates to the repository are allowed. The next commit will have
3530 two parents.
3530 two parents.
3531
3531
3532 ``--tool`` can be used to specify the merge tool used for file
3532 ``--tool`` can be used to specify the merge tool used for file
3533 merges. It overrides the HGMERGE environment variable and your
3533 merges. It overrides the HGMERGE environment variable and your
3534 configuration files. See :hg:`help merge-tools` for options.
3534 configuration files. See :hg:`help merge-tools` for options.
3535
3535
3536 If no revision is specified, the working directory's parent is a
3536 If no revision is specified, the working directory's parent is a
3537 head revision, and the current branch contains exactly one other
3537 head revision, and the current branch contains exactly one other
3538 head, the other head is merged with by default. Otherwise, an
3538 head, the other head is merged with by default. Otherwise, an
3539 explicit revision with which to merge with must be provided.
3539 explicit revision with which to merge with must be provided.
3540
3540
3541 See :hg:`help resolve` for information on handling file conflicts.
3541 See :hg:`help resolve` for information on handling file conflicts.
3542
3542
3543 To undo an uncommitted merge, use :hg:`update --clean .` which
3543 To undo an uncommitted merge, use :hg:`update --clean .` which
3544 will check out a clean copy of the original merge parent, losing
3544 will check out a clean copy of the original merge parent, losing
3545 all changes.
3545 all changes.
3546
3546
3547 Returns 0 on success, 1 if there are unresolved files.
3547 Returns 0 on success, 1 if there are unresolved files.
3548 """
3548 """
3549
3549
3550 if opts.get('rev') and node:
3550 if opts.get('rev') and node:
3551 raise error.Abort(_("please specify just one revision"))
3551 raise error.Abort(_("please specify just one revision"))
3552 if not node:
3552 if not node:
3553 node = opts.get('rev')
3553 node = opts.get('rev')
3554
3554
3555 if node:
3555 if node:
3556 node = scmutil.revsingle(repo, node).node()
3556 node = scmutil.revsingle(repo, node).node()
3557
3557
3558 if not node:
3558 if not node:
3559 node = repo[destutil.destmerge(repo)].node()
3559 node = repo[destutil.destmerge(repo)].node()
3560
3560
3561 if opts.get('preview'):
3561 if opts.get('preview'):
3562 # find nodes that are ancestors of p2 but not of p1
3562 # find nodes that are ancestors of p2 but not of p1
3563 p1 = repo.lookup('.')
3563 p1 = repo.lookup('.')
3564 p2 = repo.lookup(node)
3564 p2 = repo.lookup(node)
3565 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3565 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3566
3566
3567 displayer = cmdutil.show_changeset(ui, repo, opts)
3567 displayer = cmdutil.show_changeset(ui, repo, opts)
3568 for node in nodes:
3568 for node in nodes:
3569 displayer.show(repo[node])
3569 displayer.show(repo[node])
3570 displayer.close()
3570 displayer.close()
3571 return 0
3571 return 0
3572
3572
3573 try:
3573 try:
3574 # ui.forcemerge is an internal variable, do not document
3574 # ui.forcemerge is an internal variable, do not document
3575 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3575 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3576 force = opts.get('force')
3576 force = opts.get('force')
3577 labels = ['working copy', 'merge rev']
3577 labels = ['working copy', 'merge rev']
3578 return hg.merge(repo, node, force=force, mergeforce=force,
3578 return hg.merge(repo, node, force=force, mergeforce=force,
3579 labels=labels)
3579 labels=labels)
3580 finally:
3580 finally:
3581 ui.setconfig('ui', 'forcemerge', '', 'merge')
3581 ui.setconfig('ui', 'forcemerge', '', 'merge')
3582
3582
3583 @command('outgoing|out',
3583 @command('outgoing|out',
3584 [('f', 'force', None, _('run even when the destination is unrelated')),
3584 [('f', 'force', None, _('run even when the destination is unrelated')),
3585 ('r', 'rev', [],
3585 ('r', 'rev', [],
3586 _('a changeset intended to be included in the destination'), _('REV')),
3586 _('a changeset intended to be included in the destination'), _('REV')),
3587 ('n', 'newest-first', None, _('show newest record first')),
3587 ('n', 'newest-first', None, _('show newest record first')),
3588 ('B', 'bookmarks', False, _('compare bookmarks')),
3588 ('B', 'bookmarks', False, _('compare bookmarks')),
3589 ('b', 'branch', [], _('a specific branch you would like to push'),
3589 ('b', 'branch', [], _('a specific branch you would like to push'),
3590 _('BRANCH')),
3590 _('BRANCH')),
3591 ] + logopts + remoteopts + subrepoopts,
3591 ] + logopts + remoteopts + subrepoopts,
3592 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3592 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3593 def outgoing(ui, repo, dest=None, **opts):
3593 def outgoing(ui, repo, dest=None, **opts):
3594 """show changesets not found in the destination
3594 """show changesets not found in the destination
3595
3595
3596 Show changesets not found in the specified destination repository
3596 Show changesets not found in the specified destination repository
3597 or the default push location. These are the changesets that would
3597 or the default push location. These are the changesets that would
3598 be pushed if a push was requested.
3598 be pushed if a push was requested.
3599
3599
3600 See pull for details of valid destination formats.
3600 See pull for details of valid destination formats.
3601
3601
3602 .. container:: verbose
3602 .. container:: verbose
3603
3603
3604 With -B/--bookmarks, the result of bookmark comparison between
3604 With -B/--bookmarks, the result of bookmark comparison between
3605 local and remote repositories is displayed. With -v/--verbose,
3605 local and remote repositories is displayed. With -v/--verbose,
3606 status is also displayed for each bookmark like below::
3606 status is also displayed for each bookmark like below::
3607
3607
3608 BM1 01234567890a added
3608 BM1 01234567890a added
3609 BM2 deleted
3609 BM2 deleted
3610 BM3 234567890abc advanced
3610 BM3 234567890abc advanced
3611 BM4 34567890abcd diverged
3611 BM4 34567890abcd diverged
3612 BM5 4567890abcde changed
3612 BM5 4567890abcde changed
3613
3613
3614 The action taken when pushing depends on the
3614 The action taken when pushing depends on the
3615 status of each bookmark:
3615 status of each bookmark:
3616
3616
3617 :``added``: push with ``-B`` will create it
3617 :``added``: push with ``-B`` will create it
3618 :``deleted``: push with ``-B`` will delete it
3618 :``deleted``: push with ``-B`` will delete it
3619 :``advanced``: push will update it
3619 :``advanced``: push will update it
3620 :``diverged``: push with ``-B`` will update it
3620 :``diverged``: push with ``-B`` will update it
3621 :``changed``: push with ``-B`` will update it
3621 :``changed``: push with ``-B`` will update it
3622
3622
3623 From the point of view of pushing behavior, bookmarks
3623 From the point of view of pushing behavior, bookmarks
3624 existing only in the remote repository are treated as
3624 existing only in the remote repository are treated as
3625 ``deleted``, even if it is in fact added remotely.
3625 ``deleted``, even if it is in fact added remotely.
3626
3626
3627 Returns 0 if there are outgoing changes, 1 otherwise.
3627 Returns 0 if there are outgoing changes, 1 otherwise.
3628 """
3628 """
3629 if opts.get('graph'):
3629 if opts.get('graph'):
3630 cmdutil.checkunsupportedgraphflags([], opts)
3630 cmdutil.checkunsupportedgraphflags([], opts)
3631 o, other = hg._outgoing(ui, repo, dest, opts)
3631 o, other = hg._outgoing(ui, repo, dest, opts)
3632 if not o:
3632 if not o:
3633 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3633 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3634 return
3634 return
3635
3635
3636 revdag = cmdutil.graphrevs(repo, o, opts)
3636 revdag = cmdutil.graphrevs(repo, o, opts)
3637 ui.pager('outgoing')
3637 ui.pager('outgoing')
3638 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3638 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3639 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
3639 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
3640 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3640 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3641 return 0
3641 return 0
3642
3642
3643 if opts.get('bookmarks'):
3643 if opts.get('bookmarks'):
3644 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3644 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3645 dest, branches = hg.parseurl(dest, opts.get('branch'))
3645 dest, branches = hg.parseurl(dest, opts.get('branch'))
3646 other = hg.peer(repo, opts, dest)
3646 other = hg.peer(repo, opts, dest)
3647 if 'bookmarks' not in other.listkeys('namespaces'):
3647 if 'bookmarks' not in other.listkeys('namespaces'):
3648 ui.warn(_("remote doesn't support bookmarks\n"))
3648 ui.warn(_("remote doesn't support bookmarks\n"))
3649 return 0
3649 return 0
3650 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3650 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3651 ui.pager('outgoing')
3651 ui.pager('outgoing')
3652 return bookmarks.outgoing(ui, repo, other)
3652 return bookmarks.outgoing(ui, repo, other)
3653
3653
3654 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3654 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3655 try:
3655 try:
3656 return hg.outgoing(ui, repo, dest, opts)
3656 return hg.outgoing(ui, repo, dest, opts)
3657 finally:
3657 finally:
3658 del repo._subtoppath
3658 del repo._subtoppath
3659
3659
3660 @command('parents',
3660 @command('parents',
3661 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3661 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3662 ] + templateopts,
3662 ] + templateopts,
3663 _('[-r REV] [FILE]'),
3663 _('[-r REV] [FILE]'),
3664 inferrepo=True)
3664 inferrepo=True)
3665 def parents(ui, repo, file_=None, **opts):
3665 def parents(ui, repo, file_=None, **opts):
3666 """show the parents of the working directory or revision (DEPRECATED)
3666 """show the parents of the working directory or revision (DEPRECATED)
3667
3667
3668 Print the working directory's parent revisions. If a revision is
3668 Print the working directory's parent revisions. If a revision is
3669 given via -r/--rev, the parent of that revision will be printed.
3669 given via -r/--rev, the parent of that revision will be printed.
3670 If a file argument is given, the revision in which the file was
3670 If a file argument is given, the revision in which the file was
3671 last changed (before the working directory revision or the
3671 last changed (before the working directory revision or the
3672 argument to --rev if given) is printed.
3672 argument to --rev if given) is printed.
3673
3673
3674 This command is equivalent to::
3674 This command is equivalent to::
3675
3675
3676 hg log -r "p1()+p2()" or
3676 hg log -r "p1()+p2()" or
3677 hg log -r "p1(REV)+p2(REV)" or
3677 hg log -r "p1(REV)+p2(REV)" or
3678 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3678 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3679 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3679 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3680
3680
3681 See :hg:`summary` and :hg:`help revsets` for related information.
3681 See :hg:`summary` and :hg:`help revsets` for related information.
3682
3682
3683 Returns 0 on success.
3683 Returns 0 on success.
3684 """
3684 """
3685
3685
3686 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3686 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3687
3687
3688 if file_:
3688 if file_:
3689 m = scmutil.match(ctx, (file_,), opts)
3689 m = scmutil.match(ctx, (file_,), opts)
3690 if m.anypats() or len(m.files()) != 1:
3690 if m.anypats() or len(m.files()) != 1:
3691 raise error.Abort(_('can only specify an explicit filename'))
3691 raise error.Abort(_('can only specify an explicit filename'))
3692 file_ = m.files()[0]
3692 file_ = m.files()[0]
3693 filenodes = []
3693 filenodes = []
3694 for cp in ctx.parents():
3694 for cp in ctx.parents():
3695 if not cp:
3695 if not cp:
3696 continue
3696 continue
3697 try:
3697 try:
3698 filenodes.append(cp.filenode(file_))
3698 filenodes.append(cp.filenode(file_))
3699 except error.LookupError:
3699 except error.LookupError:
3700 pass
3700 pass
3701 if not filenodes:
3701 if not filenodes:
3702 raise error.Abort(_("'%s' not found in manifest!") % file_)
3702 raise error.Abort(_("'%s' not found in manifest!") % file_)
3703 p = []
3703 p = []
3704 for fn in filenodes:
3704 for fn in filenodes:
3705 fctx = repo.filectx(file_, fileid=fn)
3705 fctx = repo.filectx(file_, fileid=fn)
3706 p.append(fctx.node())
3706 p.append(fctx.node())
3707 else:
3707 else:
3708 p = [cp.node() for cp in ctx.parents()]
3708 p = [cp.node() for cp in ctx.parents()]
3709
3709
3710 displayer = cmdutil.show_changeset(ui, repo, opts)
3710 displayer = cmdutil.show_changeset(ui, repo, opts)
3711 for n in p:
3711 for n in p:
3712 if n != nullid:
3712 if n != nullid:
3713 displayer.show(repo[n])
3713 displayer.show(repo[n])
3714 displayer.close()
3714 displayer.close()
3715
3715
3716 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
3716 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
3717 def paths(ui, repo, search=None, **opts):
3717 def paths(ui, repo, search=None, **opts):
3718 """show aliases for remote repositories
3718 """show aliases for remote repositories
3719
3719
3720 Show definition of symbolic path name NAME. If no name is given,
3720 Show definition of symbolic path name NAME. If no name is given,
3721 show definition of all available names.
3721 show definition of all available names.
3722
3722
3723 Option -q/--quiet suppresses all output when searching for NAME
3723 Option -q/--quiet suppresses all output when searching for NAME
3724 and shows only the path names when listing all definitions.
3724 and shows only the path names when listing all definitions.
3725
3725
3726 Path names are defined in the [paths] section of your
3726 Path names are defined in the [paths] section of your
3727 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3727 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3728 repository, ``.hg/hgrc`` is used, too.
3728 repository, ``.hg/hgrc`` is used, too.
3729
3729
3730 The path names ``default`` and ``default-push`` have a special
3730 The path names ``default`` and ``default-push`` have a special
3731 meaning. When performing a push or pull operation, they are used
3731 meaning. When performing a push or pull operation, they are used
3732 as fallbacks if no location is specified on the command-line.
3732 as fallbacks if no location is specified on the command-line.
3733 When ``default-push`` is set, it will be used for push and
3733 When ``default-push`` is set, it will be used for push and
3734 ``default`` will be used for pull; otherwise ``default`` is used
3734 ``default`` will be used for pull; otherwise ``default`` is used
3735 as the fallback for both. When cloning a repository, the clone
3735 as the fallback for both. When cloning a repository, the clone
3736 source is written as ``default`` in ``.hg/hgrc``.
3736 source is written as ``default`` in ``.hg/hgrc``.
3737
3737
3738 .. note::
3738 .. note::
3739
3739
3740 ``default`` and ``default-push`` apply to all inbound (e.g.
3740 ``default`` and ``default-push`` apply to all inbound (e.g.
3741 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3741 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3742 and :hg:`bundle`) operations.
3742 and :hg:`bundle`) operations.
3743
3743
3744 See :hg:`help urls` for more information.
3744 See :hg:`help urls` for more information.
3745
3745
3746 Returns 0 on success.
3746 Returns 0 on success.
3747 """
3747 """
3748 ui.pager('paths')
3748 ui.pager('paths')
3749 if search:
3749 if search:
3750 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3750 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3751 if name == search]
3751 if name == search]
3752 else:
3752 else:
3753 pathitems = sorted(ui.paths.iteritems())
3753 pathitems = sorted(ui.paths.iteritems())
3754
3754
3755 fm = ui.formatter('paths', opts)
3755 fm = ui.formatter('paths', opts)
3756 if fm.isplain():
3756 if fm.isplain():
3757 hidepassword = util.hidepassword
3757 hidepassword = util.hidepassword
3758 else:
3758 else:
3759 hidepassword = str
3759 hidepassword = str
3760 if ui.quiet:
3760 if ui.quiet:
3761 namefmt = '%s\n'
3761 namefmt = '%s\n'
3762 else:
3762 else:
3763 namefmt = '%s = '
3763 namefmt = '%s = '
3764 showsubopts = not search and not ui.quiet
3764 showsubopts = not search and not ui.quiet
3765
3765
3766 for name, path in pathitems:
3766 for name, path in pathitems:
3767 fm.startitem()
3767 fm.startitem()
3768 fm.condwrite(not search, 'name', namefmt, name)
3768 fm.condwrite(not search, 'name', namefmt, name)
3769 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3769 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3770 for subopt, value in sorted(path.suboptions.items()):
3770 for subopt, value in sorted(path.suboptions.items()):
3771 assert subopt not in ('name', 'url')
3771 assert subopt not in ('name', 'url')
3772 if showsubopts:
3772 if showsubopts:
3773 fm.plain('%s:%s = ' % (name, subopt))
3773 fm.plain('%s:%s = ' % (name, subopt))
3774 fm.condwrite(showsubopts, subopt, '%s\n', value)
3774 fm.condwrite(showsubopts, subopt, '%s\n', value)
3775
3775
3776 fm.end()
3776 fm.end()
3777
3777
3778 if search and not pathitems:
3778 if search and not pathitems:
3779 if not ui.quiet:
3779 if not ui.quiet:
3780 ui.warn(_("not found!\n"))
3780 ui.warn(_("not found!\n"))
3781 return 1
3781 return 1
3782 else:
3782 else:
3783 return 0
3783 return 0
3784
3784
3785 @command('phase',
3785 @command('phase',
3786 [('p', 'public', False, _('set changeset phase to public')),
3786 [('p', 'public', False, _('set changeset phase to public')),
3787 ('d', 'draft', False, _('set changeset phase to draft')),
3787 ('d', 'draft', False, _('set changeset phase to draft')),
3788 ('s', 'secret', False, _('set changeset phase to secret')),
3788 ('s', 'secret', False, _('set changeset phase to secret')),
3789 ('f', 'force', False, _('allow to move boundary backward')),
3789 ('f', 'force', False, _('allow to move boundary backward')),
3790 ('r', 'rev', [], _('target revision'), _('REV')),
3790 ('r', 'rev', [], _('target revision'), _('REV')),
3791 ],
3791 ],
3792 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3792 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3793 def phase(ui, repo, *revs, **opts):
3793 def phase(ui, repo, *revs, **opts):
3794 """set or show the current phase name
3794 """set or show the current phase name
3795
3795
3796 With no argument, show the phase name of the current revision(s).
3796 With no argument, show the phase name of the current revision(s).
3797
3797
3798 With one of -p/--public, -d/--draft or -s/--secret, change the
3798 With one of -p/--public, -d/--draft or -s/--secret, change the
3799 phase value of the specified revisions.
3799 phase value of the specified revisions.
3800
3800
3801 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
3801 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
3802 lower phase to an higher phase. Phases are ordered as follows::
3802 lower phase to an higher phase. Phases are ordered as follows::
3803
3803
3804 public < draft < secret
3804 public < draft < secret
3805
3805
3806 Returns 0 on success, 1 if some phases could not be changed.
3806 Returns 0 on success, 1 if some phases could not be changed.
3807
3807
3808 (For more information about the phases concept, see :hg:`help phases`.)
3808 (For more information about the phases concept, see :hg:`help phases`.)
3809 """
3809 """
3810 # search for a unique phase argument
3810 # search for a unique phase argument
3811 targetphase = None
3811 targetphase = None
3812 for idx, name in enumerate(phases.phasenames):
3812 for idx, name in enumerate(phases.phasenames):
3813 if opts[name]:
3813 if opts[name]:
3814 if targetphase is not None:
3814 if targetphase is not None:
3815 raise error.Abort(_('only one phase can be specified'))
3815 raise error.Abort(_('only one phase can be specified'))
3816 targetphase = idx
3816 targetphase = idx
3817
3817
3818 # look for specified revision
3818 # look for specified revision
3819 revs = list(revs)
3819 revs = list(revs)
3820 revs.extend(opts['rev'])
3820 revs.extend(opts['rev'])
3821 if not revs:
3821 if not revs:
3822 # display both parents as the second parent phase can influence
3822 # display both parents as the second parent phase can influence
3823 # the phase of a merge commit
3823 # the phase of a merge commit
3824 revs = [c.rev() for c in repo[None].parents()]
3824 revs = [c.rev() for c in repo[None].parents()]
3825
3825
3826 revs = scmutil.revrange(repo, revs)
3826 revs = scmutil.revrange(repo, revs)
3827
3827
3828 lock = None
3828 lock = None
3829 ret = 0
3829 ret = 0
3830 if targetphase is None:
3830 if targetphase is None:
3831 # display
3831 # display
3832 for r in revs:
3832 for r in revs:
3833 ctx = repo[r]
3833 ctx = repo[r]
3834 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3834 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3835 else:
3835 else:
3836 tr = None
3836 tr = None
3837 lock = repo.lock()
3837 lock = repo.lock()
3838 try:
3838 try:
3839 tr = repo.transaction("phase")
3839 tr = repo.transaction("phase")
3840 # set phase
3840 # set phase
3841 if not revs:
3841 if not revs:
3842 raise error.Abort(_('empty revision set'))
3842 raise error.Abort(_('empty revision set'))
3843 nodes = [repo[r].node() for r in revs]
3843 nodes = [repo[r].node() for r in revs]
3844 # moving revision from public to draft may hide them
3844 # moving revision from public to draft may hide them
3845 # We have to check result on an unfiltered repository
3845 # We have to check result on an unfiltered repository
3846 unfi = repo.unfiltered()
3846 unfi = repo.unfiltered()
3847 getphase = unfi._phasecache.phase
3847 getphase = unfi._phasecache.phase
3848 olddata = [getphase(unfi, r) for r in unfi]
3848 olddata = [getphase(unfi, r) for r in unfi]
3849 phases.advanceboundary(repo, tr, targetphase, nodes)
3849 phases.advanceboundary(repo, tr, targetphase, nodes)
3850 if opts['force']:
3850 if opts['force']:
3851 phases.retractboundary(repo, tr, targetphase, nodes)
3851 phases.retractboundary(repo, tr, targetphase, nodes)
3852 tr.close()
3852 tr.close()
3853 finally:
3853 finally:
3854 if tr is not None:
3854 if tr is not None:
3855 tr.release()
3855 tr.release()
3856 lock.release()
3856 lock.release()
3857 getphase = unfi._phasecache.phase
3857 getphase = unfi._phasecache.phase
3858 newdata = [getphase(unfi, r) for r in unfi]
3858 newdata = [getphase(unfi, r) for r in unfi]
3859 changes = sum(newdata[r] != olddata[r] for r in unfi)
3859 changes = sum(newdata[r] != olddata[r] for r in unfi)
3860 cl = unfi.changelog
3860 cl = unfi.changelog
3861 rejected = [n for n in nodes
3861 rejected = [n for n in nodes
3862 if newdata[cl.rev(n)] < targetphase]
3862 if newdata[cl.rev(n)] < targetphase]
3863 if rejected:
3863 if rejected:
3864 ui.warn(_('cannot move %i changesets to a higher '
3864 ui.warn(_('cannot move %i changesets to a higher '
3865 'phase, use --force\n') % len(rejected))
3865 'phase, use --force\n') % len(rejected))
3866 ret = 1
3866 ret = 1
3867 if changes:
3867 if changes:
3868 msg = _('phase changed for %i changesets\n') % changes
3868 msg = _('phase changed for %i changesets\n') % changes
3869 if ret:
3869 if ret:
3870 ui.status(msg)
3870 ui.status(msg)
3871 else:
3871 else:
3872 ui.note(msg)
3872 ui.note(msg)
3873 else:
3873 else:
3874 ui.warn(_('no phases changed\n'))
3874 ui.warn(_('no phases changed\n'))
3875 return ret
3875 return ret
3876
3876
3877 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3877 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3878 """Run after a changegroup has been added via pull/unbundle
3878 """Run after a changegroup has been added via pull/unbundle
3879
3879
3880 This takes arguments below:
3880 This takes arguments below:
3881
3881
3882 :modheads: change of heads by pull/unbundle
3882 :modheads: change of heads by pull/unbundle
3883 :optupdate: updating working directory is needed or not
3883 :optupdate: updating working directory is needed or not
3884 :checkout: update destination revision (or None to default destination)
3884 :checkout: update destination revision (or None to default destination)
3885 :brev: a name, which might be a bookmark to be activated after updating
3885 :brev: a name, which might be a bookmark to be activated after updating
3886 """
3886 """
3887 if modheads == 0:
3887 if modheads == 0:
3888 return
3888 return
3889 if optupdate:
3889 if optupdate:
3890 try:
3890 try:
3891 return hg.updatetotally(ui, repo, checkout, brev)
3891 return hg.updatetotally(ui, repo, checkout, brev)
3892 except error.UpdateAbort as inst:
3892 except error.UpdateAbort as inst:
3893 msg = _("not updating: %s") % str(inst)
3893 msg = _("not updating: %s") % str(inst)
3894 hint = inst.hint
3894 hint = inst.hint
3895 raise error.UpdateAbort(msg, hint=hint)
3895 raise error.UpdateAbort(msg, hint=hint)
3896 if modheads > 1:
3896 if modheads > 1:
3897 currentbranchheads = len(repo.branchheads())
3897 currentbranchheads = len(repo.branchheads())
3898 if currentbranchheads == modheads:
3898 if currentbranchheads == modheads:
3899 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3899 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3900 elif currentbranchheads > 1:
3900 elif currentbranchheads > 1:
3901 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3901 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3902 "merge)\n"))
3902 "merge)\n"))
3903 else:
3903 else:
3904 ui.status(_("(run 'hg heads' to see heads)\n"))
3904 ui.status(_("(run 'hg heads' to see heads)\n"))
3905 else:
3905 else:
3906 ui.status(_("(run 'hg update' to get a working copy)\n"))
3906 ui.status(_("(run 'hg update' to get a working copy)\n"))
3907
3907
3908 @command('^pull',
3908 @command('^pull',
3909 [('u', 'update', None,
3909 [('u', 'update', None,
3910 _('update to new branch head if changesets were pulled')),
3910 _('update to new branch head if changesets were pulled')),
3911 ('f', 'force', None, _('run even when remote repository is unrelated')),
3911 ('f', 'force', None, _('run even when remote repository is unrelated')),
3912 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3912 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3913 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3913 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3914 ('b', 'branch', [], _('a specific branch you would like to pull'),
3914 ('b', 'branch', [], _('a specific branch you would like to pull'),
3915 _('BRANCH')),
3915 _('BRANCH')),
3916 ] + remoteopts,
3916 ] + remoteopts,
3917 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3917 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3918 def pull(ui, repo, source="default", **opts):
3918 def pull(ui, repo, source="default", **opts):
3919 """pull changes from the specified source
3919 """pull changes from the specified source
3920
3920
3921 Pull changes from a remote repository to a local one.
3921 Pull changes from a remote repository to a local one.
3922
3922
3923 This finds all changes from the repository at the specified path
3923 This finds all changes from the repository at the specified path
3924 or URL and adds them to a local repository (the current one unless
3924 or URL and adds them to a local repository (the current one unless
3925 -R is specified). By default, this does not update the copy of the
3925 -R is specified). By default, this does not update the copy of the
3926 project in the working directory.
3926 project in the working directory.
3927
3927
3928 Use :hg:`incoming` if you want to see what would have been added
3928 Use :hg:`incoming` if you want to see what would have been added
3929 by a pull at the time you issued this command. If you then decide
3929 by a pull at the time you issued this command. If you then decide
3930 to add those changes to the repository, you should use :hg:`pull
3930 to add those changes to the repository, you should use :hg:`pull
3931 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3931 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3932
3932
3933 If SOURCE is omitted, the 'default' path will be used.
3933 If SOURCE is omitted, the 'default' path will be used.
3934 See :hg:`help urls` for more information.
3934 See :hg:`help urls` for more information.
3935
3935
3936 Specifying bookmark as ``.`` is equivalent to specifying the active
3936 Specifying bookmark as ``.`` is equivalent to specifying the active
3937 bookmark's name.
3937 bookmark's name.
3938
3938
3939 Returns 0 on success, 1 if an update had unresolved files.
3939 Returns 0 on success, 1 if an update had unresolved files.
3940 """
3940 """
3941
3941
3942 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3942 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3943 msg = _('update destination required by configuration')
3943 msg = _('update destination required by configuration')
3944 hint = _('use hg pull followed by hg update DEST')
3944 hint = _('use hg pull followed by hg update DEST')
3945 raise error.Abort(msg, hint=hint)
3945 raise error.Abort(msg, hint=hint)
3946
3946
3947 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3947 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3948 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3948 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3949 other = hg.peer(repo, opts, source)
3949 other = hg.peer(repo, opts, source)
3950 try:
3950 try:
3951 revs, checkout = hg.addbranchrevs(repo, other, branches,
3951 revs, checkout = hg.addbranchrevs(repo, other, branches,
3952 opts.get('rev'))
3952 opts.get('rev'))
3953
3953
3954
3954
3955 pullopargs = {}
3955 pullopargs = {}
3956 if opts.get('bookmark'):
3956 if opts.get('bookmark'):
3957 if not revs:
3957 if not revs:
3958 revs = []
3958 revs = []
3959 # The list of bookmark used here is not the one used to actually
3959 # The list of bookmark used here is not the one used to actually
3960 # update the bookmark name. This can result in the revision pulled
3960 # update the bookmark name. This can result in the revision pulled
3961 # not ending up with the name of the bookmark because of a race
3961 # not ending up with the name of the bookmark because of a race
3962 # condition on the server. (See issue 4689 for details)
3962 # condition on the server. (See issue 4689 for details)
3963 remotebookmarks = other.listkeys('bookmarks')
3963 remotebookmarks = other.listkeys('bookmarks')
3964 pullopargs['remotebookmarks'] = remotebookmarks
3964 pullopargs['remotebookmarks'] = remotebookmarks
3965 for b in opts['bookmark']:
3965 for b in opts['bookmark']:
3966 b = repo._bookmarks.expandname(b)
3966 b = repo._bookmarks.expandname(b)
3967 if b not in remotebookmarks:
3967 if b not in remotebookmarks:
3968 raise error.Abort(_('remote bookmark %s not found!') % b)
3968 raise error.Abort(_('remote bookmark %s not found!') % b)
3969 revs.append(remotebookmarks[b])
3969 revs.append(remotebookmarks[b])
3970
3970
3971 if revs:
3971 if revs:
3972 try:
3972 try:
3973 # When 'rev' is a bookmark name, we cannot guarantee that it
3973 # When 'rev' is a bookmark name, we cannot guarantee that it
3974 # will be updated with that name because of a race condition
3974 # will be updated with that name because of a race condition
3975 # server side. (See issue 4689 for details)
3975 # server side. (See issue 4689 for details)
3976 oldrevs = revs
3976 oldrevs = revs
3977 revs = [] # actually, nodes
3977 revs = [] # actually, nodes
3978 for r in oldrevs:
3978 for r in oldrevs:
3979 node = other.lookup(r)
3979 node = other.lookup(r)
3980 revs.append(node)
3980 revs.append(node)
3981 if r == checkout:
3981 if r == checkout:
3982 checkout = node
3982 checkout = node
3983 except error.CapabilityError:
3983 except error.CapabilityError:
3984 err = _("other repository doesn't support revision lookup, "
3984 err = _("other repository doesn't support revision lookup, "
3985 "so a rev cannot be specified.")
3985 "so a rev cannot be specified.")
3986 raise error.Abort(err)
3986 raise error.Abort(err)
3987
3987
3988 pullopargs.update(opts.get('opargs', {}))
3988 pullopargs.update(opts.get('opargs', {}))
3989 modheads = exchange.pull(repo, other, heads=revs,
3989 modheads = exchange.pull(repo, other, heads=revs,
3990 force=opts.get('force'),
3990 force=opts.get('force'),
3991 bookmarks=opts.get('bookmark', ()),
3991 bookmarks=opts.get('bookmark', ()),
3992 opargs=pullopargs).cgresult
3992 opargs=pullopargs).cgresult
3993
3993
3994 # brev is a name, which might be a bookmark to be activated at
3994 # brev is a name, which might be a bookmark to be activated at
3995 # the end of the update. In other words, it is an explicit
3995 # the end of the update. In other words, it is an explicit
3996 # destination of the update
3996 # destination of the update
3997 brev = None
3997 brev = None
3998
3998
3999 if checkout:
3999 if checkout:
4000 checkout = str(repo.changelog.rev(checkout))
4000 checkout = str(repo.changelog.rev(checkout))
4001
4001
4002 # order below depends on implementation of
4002 # order below depends on implementation of
4003 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4003 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4004 # because 'checkout' is determined without it.
4004 # because 'checkout' is determined without it.
4005 if opts.get('rev'):
4005 if opts.get('rev'):
4006 brev = opts['rev'][0]
4006 brev = opts['rev'][0]
4007 elif opts.get('branch'):
4007 elif opts.get('branch'):
4008 brev = opts['branch'][0]
4008 brev = opts['branch'][0]
4009 else:
4009 else:
4010 brev = branches[0]
4010 brev = branches[0]
4011 repo._subtoppath = source
4011 repo._subtoppath = source
4012 try:
4012 try:
4013 ret = postincoming(ui, repo, modheads, opts.get('update'),
4013 ret = postincoming(ui, repo, modheads, opts.get('update'),
4014 checkout, brev)
4014 checkout, brev)
4015
4015
4016 finally:
4016 finally:
4017 del repo._subtoppath
4017 del repo._subtoppath
4018
4018
4019 finally:
4019 finally:
4020 other.close()
4020 other.close()
4021 return ret
4021 return ret
4022
4022
4023 @command('^push',
4023 @command('^push',
4024 [('f', 'force', None, _('force push')),
4024 [('f', 'force', None, _('force push')),
4025 ('r', 'rev', [],
4025 ('r', 'rev', [],
4026 _('a changeset intended to be included in the destination'),
4026 _('a changeset intended to be included in the destination'),
4027 _('REV')),
4027 _('REV')),
4028 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4028 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4029 ('b', 'branch', [],
4029 ('b', 'branch', [],
4030 _('a specific branch you would like to push'), _('BRANCH')),
4030 _('a specific branch you would like to push'), _('BRANCH')),
4031 ('', 'new-branch', False, _('allow pushing a new branch')),
4031 ('', 'new-branch', False, _('allow pushing a new branch')),
4032 ] + remoteopts,
4032 ] + remoteopts,
4033 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4033 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4034 def push(ui, repo, dest=None, **opts):
4034 def push(ui, repo, dest=None, **opts):
4035 """push changes to the specified destination
4035 """push changes to the specified destination
4036
4036
4037 Push changesets from the local repository to the specified
4037 Push changesets from the local repository to the specified
4038 destination.
4038 destination.
4039
4039
4040 This operation is symmetrical to pull: it is identical to a pull
4040 This operation is symmetrical to pull: it is identical to a pull
4041 in the destination repository from the current one.
4041 in the destination repository from the current one.
4042
4042
4043 By default, push will not allow creation of new heads at the
4043 By default, push will not allow creation of new heads at the
4044 destination, since multiple heads would make it unclear which head
4044 destination, since multiple heads would make it unclear which head
4045 to use. In this situation, it is recommended to pull and merge
4045 to use. In this situation, it is recommended to pull and merge
4046 before pushing.
4046 before pushing.
4047
4047
4048 Use --new-branch if you want to allow push to create a new named
4048 Use --new-branch if you want to allow push to create a new named
4049 branch that is not present at the destination. This allows you to
4049 branch that is not present at the destination. This allows you to
4050 only create a new branch without forcing other changes.
4050 only create a new branch without forcing other changes.
4051
4051
4052 .. note::
4052 .. note::
4053
4053
4054 Extra care should be taken with the -f/--force option,
4054 Extra care should be taken with the -f/--force option,
4055 which will push all new heads on all branches, an action which will
4055 which will push all new heads on all branches, an action which will
4056 almost always cause confusion for collaborators.
4056 almost always cause confusion for collaborators.
4057
4057
4058 If -r/--rev is used, the specified revision and all its ancestors
4058 If -r/--rev is used, the specified revision and all its ancestors
4059 will be pushed to the remote repository.
4059 will be pushed to the remote repository.
4060
4060
4061 If -B/--bookmark is used, the specified bookmarked revision, its
4061 If -B/--bookmark is used, the specified bookmarked revision, its
4062 ancestors, and the bookmark will be pushed to the remote
4062 ancestors, and the bookmark will be pushed to the remote
4063 repository. Specifying ``.`` is equivalent to specifying the active
4063 repository. Specifying ``.`` is equivalent to specifying the active
4064 bookmark's name.
4064 bookmark's name.
4065
4065
4066 Please see :hg:`help urls` for important details about ``ssh://``
4066 Please see :hg:`help urls` for important details about ``ssh://``
4067 URLs. If DESTINATION is omitted, a default path will be used.
4067 URLs. If DESTINATION is omitted, a default path will be used.
4068
4068
4069 Returns 0 if push was successful, 1 if nothing to push.
4069 Returns 0 if push was successful, 1 if nothing to push.
4070 """
4070 """
4071
4071
4072 if opts.get('bookmark'):
4072 if opts.get('bookmark'):
4073 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4073 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4074 for b in opts['bookmark']:
4074 for b in opts['bookmark']:
4075 # translate -B options to -r so changesets get pushed
4075 # translate -B options to -r so changesets get pushed
4076 b = repo._bookmarks.expandname(b)
4076 b = repo._bookmarks.expandname(b)
4077 if b in repo._bookmarks:
4077 if b in repo._bookmarks:
4078 opts.setdefault('rev', []).append(b)
4078 opts.setdefault('rev', []).append(b)
4079 else:
4079 else:
4080 # if we try to push a deleted bookmark, translate it to null
4080 # if we try to push a deleted bookmark, translate it to null
4081 # this lets simultaneous -r, -b options continue working
4081 # this lets simultaneous -r, -b options continue working
4082 opts.setdefault('rev', []).append("null")
4082 opts.setdefault('rev', []).append("null")
4083
4083
4084 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4084 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4085 if not path:
4085 if not path:
4086 raise error.Abort(_('default repository not configured!'),
4086 raise error.Abort(_('default repository not configured!'),
4087 hint=_("see 'hg help config.paths'"))
4087 hint=_("see 'hg help config.paths'"))
4088 dest = path.pushloc or path.loc
4088 dest = path.pushloc or path.loc
4089 branches = (path.branch, opts.get('branch') or [])
4089 branches = (path.branch, opts.get('branch') or [])
4090 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4090 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4091 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4091 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4092 other = hg.peer(repo, opts, dest)
4092 other = hg.peer(repo, opts, dest)
4093
4093
4094 if revs:
4094 if revs:
4095 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4095 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4096 if not revs:
4096 if not revs:
4097 raise error.Abort(_("specified revisions evaluate to an empty set"),
4097 raise error.Abort(_("specified revisions evaluate to an empty set"),
4098 hint=_("use different revision arguments"))
4098 hint=_("use different revision arguments"))
4099 elif path.pushrev:
4099 elif path.pushrev:
4100 # It doesn't make any sense to specify ancestor revisions. So limit
4100 # It doesn't make any sense to specify ancestor revisions. So limit
4101 # to DAG heads to make discovery simpler.
4101 # to DAG heads to make discovery simpler.
4102 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4102 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4103 revs = scmutil.revrange(repo, [expr])
4103 revs = scmutil.revrange(repo, [expr])
4104 revs = [repo[rev].node() for rev in revs]
4104 revs = [repo[rev].node() for rev in revs]
4105 if not revs:
4105 if not revs:
4106 raise error.Abort(_('default push revset for path evaluates to an '
4106 raise error.Abort(_('default push revset for path evaluates to an '
4107 'empty set'))
4107 'empty set'))
4108
4108
4109 repo._subtoppath = dest
4109 repo._subtoppath = dest
4110 try:
4110 try:
4111 # push subrepos depth-first for coherent ordering
4111 # push subrepos depth-first for coherent ordering
4112 c = repo['']
4112 c = repo['']
4113 subs = c.substate # only repos that are committed
4113 subs = c.substate # only repos that are committed
4114 for s in sorted(subs):
4114 for s in sorted(subs):
4115 result = c.sub(s).push(opts)
4115 result = c.sub(s).push(opts)
4116 if result == 0:
4116 if result == 0:
4117 return not result
4117 return not result
4118 finally:
4118 finally:
4119 del repo._subtoppath
4119 del repo._subtoppath
4120 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4120 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4121 newbranch=opts.get('new_branch'),
4121 newbranch=opts.get('new_branch'),
4122 bookmarks=opts.get('bookmark', ()),
4122 bookmarks=opts.get('bookmark', ()),
4123 opargs=opts.get('opargs'))
4123 opargs=opts.get('opargs'))
4124
4124
4125 result = not pushop.cgresult
4125 result = not pushop.cgresult
4126
4126
4127 if pushop.bkresult is not None:
4127 if pushop.bkresult is not None:
4128 if pushop.bkresult == 2:
4128 if pushop.bkresult == 2:
4129 result = 2
4129 result = 2
4130 elif not result and pushop.bkresult:
4130 elif not result and pushop.bkresult:
4131 result = 2
4131 result = 2
4132
4132
4133 return result
4133 return result
4134
4134
4135 @command('recover', [])
4135 @command('recover', [])
4136 def recover(ui, repo):
4136 def recover(ui, repo):
4137 """roll back an interrupted transaction
4137 """roll back an interrupted transaction
4138
4138
4139 Recover from an interrupted commit or pull.
4139 Recover from an interrupted commit or pull.
4140
4140
4141 This command tries to fix the repository status after an
4141 This command tries to fix the repository status after an
4142 interrupted operation. It should only be necessary when Mercurial
4142 interrupted operation. It should only be necessary when Mercurial
4143 suggests it.
4143 suggests it.
4144
4144
4145 Returns 0 if successful, 1 if nothing to recover or verify fails.
4145 Returns 0 if successful, 1 if nothing to recover or verify fails.
4146 """
4146 """
4147 if repo.recover():
4147 if repo.recover():
4148 return hg.verify(repo)
4148 return hg.verify(repo)
4149 return 1
4149 return 1
4150
4150
4151 @command('^remove|rm',
4151 @command('^remove|rm',
4152 [('A', 'after', None, _('record delete for missing files')),
4152 [('A', 'after', None, _('record delete for missing files')),
4153 ('f', 'force', None,
4153 ('f', 'force', None,
4154 _('forget added files, delete modified files')),
4154 _('forget added files, delete modified files')),
4155 ] + subrepoopts + walkopts,
4155 ] + subrepoopts + walkopts,
4156 _('[OPTION]... FILE...'),
4156 _('[OPTION]... FILE...'),
4157 inferrepo=True)
4157 inferrepo=True)
4158 def remove(ui, repo, *pats, **opts):
4158 def remove(ui, repo, *pats, **opts):
4159 """remove the specified files on the next commit
4159 """remove the specified files on the next commit
4160
4160
4161 Schedule the indicated files for removal from the current branch.
4161 Schedule the indicated files for removal from the current branch.
4162
4162
4163 This command schedules the files to be removed at the next commit.
4163 This command schedules the files to be removed at the next commit.
4164 To undo a remove before that, see :hg:`revert`. To undo added
4164 To undo a remove before that, see :hg:`revert`. To undo added
4165 files, see :hg:`forget`.
4165 files, see :hg:`forget`.
4166
4166
4167 .. container:: verbose
4167 .. container:: verbose
4168
4168
4169 -A/--after can be used to remove only files that have already
4169 -A/--after can be used to remove only files that have already
4170 been deleted, -f/--force can be used to force deletion, and -Af
4170 been deleted, -f/--force can be used to force deletion, and -Af
4171 can be used to remove files from the next revision without
4171 can be used to remove files from the next revision without
4172 deleting them from the working directory.
4172 deleting them from the working directory.
4173
4173
4174 The following table details the behavior of remove for different
4174 The following table details the behavior of remove for different
4175 file states (columns) and option combinations (rows). The file
4175 file states (columns) and option combinations (rows). The file
4176 states are Added [A], Clean [C], Modified [M] and Missing [!]
4176 states are Added [A], Clean [C], Modified [M] and Missing [!]
4177 (as reported by :hg:`status`). The actions are Warn, Remove
4177 (as reported by :hg:`status`). The actions are Warn, Remove
4178 (from branch) and Delete (from disk):
4178 (from branch) and Delete (from disk):
4179
4179
4180 ========= == == == ==
4180 ========= == == == ==
4181 opt/state A C M !
4181 opt/state A C M !
4182 ========= == == == ==
4182 ========= == == == ==
4183 none W RD W R
4183 none W RD W R
4184 -f R RD RD R
4184 -f R RD RD R
4185 -A W W W R
4185 -A W W W R
4186 -Af R R R R
4186 -Af R R R R
4187 ========= == == == ==
4187 ========= == == == ==
4188
4188
4189 .. note::
4189 .. note::
4190
4190
4191 :hg:`remove` never deletes files in Added [A] state from the
4191 :hg:`remove` never deletes files in Added [A] state from the
4192 working directory, not even if ``--force`` is specified.
4192 working directory, not even if ``--force`` is specified.
4193
4193
4194 Returns 0 on success, 1 if any warnings encountered.
4194 Returns 0 on success, 1 if any warnings encountered.
4195 """
4195 """
4196
4196
4197 after, force = opts.get('after'), opts.get('force')
4197 after, force = opts.get('after'), opts.get('force')
4198 if not pats and not after:
4198 if not pats and not after:
4199 raise error.Abort(_('no files specified'))
4199 raise error.Abort(_('no files specified'))
4200
4200
4201 m = scmutil.match(repo[None], pats, opts)
4201 m = scmutil.match(repo[None], pats, opts)
4202 subrepos = opts.get('subrepos')
4202 subrepos = opts.get('subrepos')
4203 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
4203 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
4204
4204
4205 @command('rename|move|mv',
4205 @command('rename|move|mv',
4206 [('A', 'after', None, _('record a rename that has already occurred')),
4206 [('A', 'after', None, _('record a rename that has already occurred')),
4207 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4207 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4208 ] + walkopts + dryrunopts,
4208 ] + walkopts + dryrunopts,
4209 _('[OPTION]... SOURCE... DEST'))
4209 _('[OPTION]... SOURCE... DEST'))
4210 def rename(ui, repo, *pats, **opts):
4210 def rename(ui, repo, *pats, **opts):
4211 """rename files; equivalent of copy + remove
4211 """rename files; equivalent of copy + remove
4212
4212
4213 Mark dest as copies of sources; mark sources for deletion. If dest
4213 Mark dest as copies of sources; mark sources for deletion. If dest
4214 is a directory, copies are put in that directory. If dest is a
4214 is a directory, copies are put in that directory. If dest is a
4215 file, there can only be one source.
4215 file, there can only be one source.
4216
4216
4217 By default, this command copies the contents of files as they
4217 By default, this command copies the contents of files as they
4218 exist in the working directory. If invoked with -A/--after, the
4218 exist in the working directory. If invoked with -A/--after, the
4219 operation is recorded, but no copying is performed.
4219 operation is recorded, but no copying is performed.
4220
4220
4221 This command takes effect at the next commit. To undo a rename
4221 This command takes effect at the next commit. To undo a rename
4222 before that, see :hg:`revert`.
4222 before that, see :hg:`revert`.
4223
4223
4224 Returns 0 on success, 1 if errors are encountered.
4224 Returns 0 on success, 1 if errors are encountered.
4225 """
4225 """
4226 with repo.wlock(False):
4226 with repo.wlock(False):
4227 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4227 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4228
4228
4229 @command('resolve',
4229 @command('resolve',
4230 [('a', 'all', None, _('select all unresolved files')),
4230 [('a', 'all', None, _('select all unresolved files')),
4231 ('l', 'list', None, _('list state of files needing merge')),
4231 ('l', 'list', None, _('list state of files needing merge')),
4232 ('m', 'mark', None, _('mark files as resolved')),
4232 ('m', 'mark', None, _('mark files as resolved')),
4233 ('u', 'unmark', None, _('mark files as unresolved')),
4233 ('u', 'unmark', None, _('mark files as unresolved')),
4234 ('n', 'no-status', None, _('hide status prefix'))]
4234 ('n', 'no-status', None, _('hide status prefix'))]
4235 + mergetoolopts + walkopts + formatteropts,
4235 + mergetoolopts + walkopts + formatteropts,
4236 _('[OPTION]... [FILE]...'),
4236 _('[OPTION]... [FILE]...'),
4237 inferrepo=True)
4237 inferrepo=True)
4238 def resolve(ui, repo, *pats, **opts):
4238 def resolve(ui, repo, *pats, **opts):
4239 """redo merges or set/view the merge status of files
4239 """redo merges or set/view the merge status of files
4240
4240
4241 Merges with unresolved conflicts are often the result of
4241 Merges with unresolved conflicts are often the result of
4242 non-interactive merging using the ``internal:merge`` configuration
4242 non-interactive merging using the ``internal:merge`` configuration
4243 setting, or a command-line merge tool like ``diff3``. The resolve
4243 setting, or a command-line merge tool like ``diff3``. The resolve
4244 command is used to manage the files involved in a merge, after
4244 command is used to manage the files involved in a merge, after
4245 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4245 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4246 working directory must have two parents). See :hg:`help
4246 working directory must have two parents). See :hg:`help
4247 merge-tools` for information on configuring merge tools.
4247 merge-tools` for information on configuring merge tools.
4248
4248
4249 The resolve command can be used in the following ways:
4249 The resolve command can be used in the following ways:
4250
4250
4251 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4251 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4252 files, discarding any previous merge attempts. Re-merging is not
4252 files, discarding any previous merge attempts. Re-merging is not
4253 performed for files already marked as resolved. Use ``--all/-a``
4253 performed for files already marked as resolved. Use ``--all/-a``
4254 to select all unresolved files. ``--tool`` can be used to specify
4254 to select all unresolved files. ``--tool`` can be used to specify
4255 the merge tool used for the given files. It overrides the HGMERGE
4255 the merge tool used for the given files. It overrides the HGMERGE
4256 environment variable and your configuration files. Previous file
4256 environment variable and your configuration files. Previous file
4257 contents are saved with a ``.orig`` suffix.
4257 contents are saved with a ``.orig`` suffix.
4258
4258
4259 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4259 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4260 (e.g. after having manually fixed-up the files). The default is
4260 (e.g. after having manually fixed-up the files). The default is
4261 to mark all unresolved files.
4261 to mark all unresolved files.
4262
4262
4263 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4263 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4264 default is to mark all resolved files.
4264 default is to mark all resolved files.
4265
4265
4266 - :hg:`resolve -l`: list files which had or still have conflicts.
4266 - :hg:`resolve -l`: list files which had or still have conflicts.
4267 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4267 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4268 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4268 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4269 the list. See :hg:`help filesets` for details.
4269 the list. See :hg:`help filesets` for details.
4270
4270
4271 .. note::
4271 .. note::
4272
4272
4273 Mercurial will not let you commit files with unresolved merge
4273 Mercurial will not let you commit files with unresolved merge
4274 conflicts. You must use :hg:`resolve -m ...` before you can
4274 conflicts. You must use :hg:`resolve -m ...` before you can
4275 commit after a conflicting merge.
4275 commit after a conflicting merge.
4276
4276
4277 Returns 0 on success, 1 if any files fail a resolve attempt.
4277 Returns 0 on success, 1 if any files fail a resolve attempt.
4278 """
4278 """
4279
4279
4280 flaglist = 'all mark unmark list no_status'.split()
4280 flaglist = 'all mark unmark list no_status'.split()
4281 all, mark, unmark, show, nostatus = \
4281 all, mark, unmark, show, nostatus = \
4282 [opts.get(o) for o in flaglist]
4282 [opts.get(o) for o in flaglist]
4283
4283
4284 if (show and (mark or unmark)) or (mark and unmark):
4284 if (show and (mark or unmark)) or (mark and unmark):
4285 raise error.Abort(_("too many options specified"))
4285 raise error.Abort(_("too many options specified"))
4286 if pats and all:
4286 if pats and all:
4287 raise error.Abort(_("can't specify --all and patterns"))
4287 raise error.Abort(_("can't specify --all and patterns"))
4288 if not (all or pats or show or mark or unmark):
4288 if not (all or pats or show or mark or unmark):
4289 raise error.Abort(_('no files or directories specified'),
4289 raise error.Abort(_('no files or directories specified'),
4290 hint=('use --all to re-merge all unresolved files'))
4290 hint=('use --all to re-merge all unresolved files'))
4291
4291
4292 if show:
4292 if show:
4293 ui.pager('resolve')
4293 ui.pager('resolve')
4294 fm = ui.formatter('resolve', opts)
4294 fm = ui.formatter('resolve', opts)
4295 ms = mergemod.mergestate.read(repo)
4295 ms = mergemod.mergestate.read(repo)
4296 m = scmutil.match(repo[None], pats, opts)
4296 m = scmutil.match(repo[None], pats, opts)
4297 for f in ms:
4297 for f in ms:
4298 if not m(f):
4298 if not m(f):
4299 continue
4299 continue
4300 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
4300 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
4301 'd': 'driverresolved'}[ms[f]]
4301 'd': 'driverresolved'}[ms[f]]
4302 fm.startitem()
4302 fm.startitem()
4303 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
4303 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
4304 fm.write('path', '%s\n', f, label=l)
4304 fm.write('path', '%s\n', f, label=l)
4305 fm.end()
4305 fm.end()
4306 return 0
4306 return 0
4307
4307
4308 with repo.wlock():
4308 with repo.wlock():
4309 ms = mergemod.mergestate.read(repo)
4309 ms = mergemod.mergestate.read(repo)
4310
4310
4311 if not (ms.active() or repo.dirstate.p2() != nullid):
4311 if not (ms.active() or repo.dirstate.p2() != nullid):
4312 raise error.Abort(
4312 raise error.Abort(
4313 _('resolve command not applicable when not merging'))
4313 _('resolve command not applicable when not merging'))
4314
4314
4315 wctx = repo[None]
4315 wctx = repo[None]
4316
4316
4317 if ms.mergedriver and ms.mdstate() == 'u':
4317 if ms.mergedriver and ms.mdstate() == 'u':
4318 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4318 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4319 ms.commit()
4319 ms.commit()
4320 # allow mark and unmark to go through
4320 # allow mark and unmark to go through
4321 if not mark and not unmark and not proceed:
4321 if not mark and not unmark and not proceed:
4322 return 1
4322 return 1
4323
4323
4324 m = scmutil.match(wctx, pats, opts)
4324 m = scmutil.match(wctx, pats, opts)
4325 ret = 0
4325 ret = 0
4326 didwork = False
4326 didwork = False
4327 runconclude = False
4327 runconclude = False
4328
4328
4329 tocomplete = []
4329 tocomplete = []
4330 for f in ms:
4330 for f in ms:
4331 if not m(f):
4331 if not m(f):
4332 continue
4332 continue
4333
4333
4334 didwork = True
4334 didwork = True
4335
4335
4336 # don't let driver-resolved files be marked, and run the conclude
4336 # don't let driver-resolved files be marked, and run the conclude
4337 # step if asked to resolve
4337 # step if asked to resolve
4338 if ms[f] == "d":
4338 if ms[f] == "d":
4339 exact = m.exact(f)
4339 exact = m.exact(f)
4340 if mark:
4340 if mark:
4341 if exact:
4341 if exact:
4342 ui.warn(_('not marking %s as it is driver-resolved\n')
4342 ui.warn(_('not marking %s as it is driver-resolved\n')
4343 % f)
4343 % f)
4344 elif unmark:
4344 elif unmark:
4345 if exact:
4345 if exact:
4346 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4346 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4347 % f)
4347 % f)
4348 else:
4348 else:
4349 runconclude = True
4349 runconclude = True
4350 continue
4350 continue
4351
4351
4352 if mark:
4352 if mark:
4353 ms.mark(f, "r")
4353 ms.mark(f, "r")
4354 elif unmark:
4354 elif unmark:
4355 ms.mark(f, "u")
4355 ms.mark(f, "u")
4356 else:
4356 else:
4357 # backup pre-resolve (merge uses .orig for its own purposes)
4357 # backup pre-resolve (merge uses .orig for its own purposes)
4358 a = repo.wjoin(f)
4358 a = repo.wjoin(f)
4359 try:
4359 try:
4360 util.copyfile(a, a + ".resolve")
4360 util.copyfile(a, a + ".resolve")
4361 except (IOError, OSError) as inst:
4361 except (IOError, OSError) as inst:
4362 if inst.errno != errno.ENOENT:
4362 if inst.errno != errno.ENOENT:
4363 raise
4363 raise
4364
4364
4365 try:
4365 try:
4366 # preresolve file
4366 # preresolve file
4367 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4367 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4368 'resolve')
4368 'resolve')
4369 complete, r = ms.preresolve(f, wctx)
4369 complete, r = ms.preresolve(f, wctx)
4370 if not complete:
4370 if not complete:
4371 tocomplete.append(f)
4371 tocomplete.append(f)
4372 elif r:
4372 elif r:
4373 ret = 1
4373 ret = 1
4374 finally:
4374 finally:
4375 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4375 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4376 ms.commit()
4376 ms.commit()
4377
4377
4378 # replace filemerge's .orig file with our resolve file, but only
4378 # replace filemerge's .orig file with our resolve file, but only
4379 # for merges that are complete
4379 # for merges that are complete
4380 if complete:
4380 if complete:
4381 try:
4381 try:
4382 util.rename(a + ".resolve",
4382 util.rename(a + ".resolve",
4383 scmutil.origpath(ui, repo, a))
4383 scmutil.origpath(ui, repo, a))
4384 except OSError as inst:
4384 except OSError as inst:
4385 if inst.errno != errno.ENOENT:
4385 if inst.errno != errno.ENOENT:
4386 raise
4386 raise
4387
4387
4388 for f in tocomplete:
4388 for f in tocomplete:
4389 try:
4389 try:
4390 # resolve file
4390 # resolve file
4391 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4391 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4392 'resolve')
4392 'resolve')
4393 r = ms.resolve(f, wctx)
4393 r = ms.resolve(f, wctx)
4394 if r:
4394 if r:
4395 ret = 1
4395 ret = 1
4396 finally:
4396 finally:
4397 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4397 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4398 ms.commit()
4398 ms.commit()
4399
4399
4400 # replace filemerge's .orig file with our resolve file
4400 # replace filemerge's .orig file with our resolve file
4401 a = repo.wjoin(f)
4401 a = repo.wjoin(f)
4402 try:
4402 try:
4403 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4403 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4404 except OSError as inst:
4404 except OSError as inst:
4405 if inst.errno != errno.ENOENT:
4405 if inst.errno != errno.ENOENT:
4406 raise
4406 raise
4407
4407
4408 ms.commit()
4408 ms.commit()
4409 ms.recordactions()
4409 ms.recordactions()
4410
4410
4411 if not didwork and pats:
4411 if not didwork and pats:
4412 hint = None
4412 hint = None
4413 if not any([p for p in pats if p.find(':') >= 0]):
4413 if not any([p for p in pats if p.find(':') >= 0]):
4414 pats = ['path:%s' % p for p in pats]
4414 pats = ['path:%s' % p for p in pats]
4415 m = scmutil.match(wctx, pats, opts)
4415 m = scmutil.match(wctx, pats, opts)
4416 for f in ms:
4416 for f in ms:
4417 if not m(f):
4417 if not m(f):
4418 continue
4418 continue
4419 flags = ''.join(['-%s ' % o[0] for o in flaglist
4419 flags = ''.join(['-%s ' % o[0] for o in flaglist
4420 if opts.get(o)])
4420 if opts.get(o)])
4421 hint = _("(try: hg resolve %s%s)\n") % (
4421 hint = _("(try: hg resolve %s%s)\n") % (
4422 flags,
4422 flags,
4423 ' '.join(pats))
4423 ' '.join(pats))
4424 break
4424 break
4425 ui.warn(_("arguments do not match paths that need resolving\n"))
4425 ui.warn(_("arguments do not match paths that need resolving\n"))
4426 if hint:
4426 if hint:
4427 ui.warn(hint)
4427 ui.warn(hint)
4428 elif ms.mergedriver and ms.mdstate() != 's':
4428 elif ms.mergedriver and ms.mdstate() != 's':
4429 # run conclude step when either a driver-resolved file is requested
4429 # run conclude step when either a driver-resolved file is requested
4430 # or there are no driver-resolved files
4430 # or there are no driver-resolved files
4431 # we can't use 'ret' to determine whether any files are unresolved
4431 # we can't use 'ret' to determine whether any files are unresolved
4432 # because we might not have tried to resolve some
4432 # because we might not have tried to resolve some
4433 if ((runconclude or not list(ms.driverresolved()))
4433 if ((runconclude or not list(ms.driverresolved()))
4434 and not list(ms.unresolved())):
4434 and not list(ms.unresolved())):
4435 proceed = mergemod.driverconclude(repo, ms, wctx)
4435 proceed = mergemod.driverconclude(repo, ms, wctx)
4436 ms.commit()
4436 ms.commit()
4437 if not proceed:
4437 if not proceed:
4438 return 1
4438 return 1
4439
4439
4440 # Nudge users into finishing an unfinished operation
4440 # Nudge users into finishing an unfinished operation
4441 unresolvedf = list(ms.unresolved())
4441 unresolvedf = list(ms.unresolved())
4442 driverresolvedf = list(ms.driverresolved())
4442 driverresolvedf = list(ms.driverresolved())
4443 if not unresolvedf and not driverresolvedf:
4443 if not unresolvedf and not driverresolvedf:
4444 ui.status(_('(no more unresolved files)\n'))
4444 ui.status(_('(no more unresolved files)\n'))
4445 cmdutil.checkafterresolved(repo)
4445 cmdutil.checkafterresolved(repo)
4446 elif not unresolvedf:
4446 elif not unresolvedf:
4447 ui.status(_('(no more unresolved files -- '
4447 ui.status(_('(no more unresolved files -- '
4448 'run "hg resolve --all" to conclude)\n'))
4448 'run "hg resolve --all" to conclude)\n'))
4449
4449
4450 return ret
4450 return ret
4451
4451
4452 @command('revert',
4452 @command('revert',
4453 [('a', 'all', None, _('revert all changes when no arguments given')),
4453 [('a', 'all', None, _('revert all changes when no arguments given')),
4454 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4454 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4455 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4455 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4456 ('C', 'no-backup', None, _('do not save backup copies of files')),
4456 ('C', 'no-backup', None, _('do not save backup copies of files')),
4457 ('i', 'interactive', None,
4457 ('i', 'interactive', None,
4458 _('interactively select the changes (EXPERIMENTAL)')),
4458 _('interactively select the changes (EXPERIMENTAL)')),
4459 ] + walkopts + dryrunopts,
4459 ] + walkopts + dryrunopts,
4460 _('[OPTION]... [-r REV] [NAME]...'))
4460 _('[OPTION]... [-r REV] [NAME]...'))
4461 def revert(ui, repo, *pats, **opts):
4461 def revert(ui, repo, *pats, **opts):
4462 """restore files to their checkout state
4462 """restore files to their checkout state
4463
4463
4464 .. note::
4464 .. note::
4465
4465
4466 To check out earlier revisions, you should use :hg:`update REV`.
4466 To check out earlier revisions, you should use :hg:`update REV`.
4467 To cancel an uncommitted merge (and lose your changes),
4467 To cancel an uncommitted merge (and lose your changes),
4468 use :hg:`update --clean .`.
4468 use :hg:`update --clean .`.
4469
4469
4470 With no revision specified, revert the specified files or directories
4470 With no revision specified, revert the specified files or directories
4471 to the contents they had in the parent of the working directory.
4471 to the contents they had in the parent of the working directory.
4472 This restores the contents of files to an unmodified
4472 This restores the contents of files to an unmodified
4473 state and unschedules adds, removes, copies, and renames. If the
4473 state and unschedules adds, removes, copies, and renames. If the
4474 working directory has two parents, you must explicitly specify a
4474 working directory has two parents, you must explicitly specify a
4475 revision.
4475 revision.
4476
4476
4477 Using the -r/--rev or -d/--date options, revert the given files or
4477 Using the -r/--rev or -d/--date options, revert the given files or
4478 directories to their states as of a specific revision. Because
4478 directories to their states as of a specific revision. Because
4479 revert does not change the working directory parents, this will
4479 revert does not change the working directory parents, this will
4480 cause these files to appear modified. This can be helpful to "back
4480 cause these files to appear modified. This can be helpful to "back
4481 out" some or all of an earlier change. See :hg:`backout` for a
4481 out" some or all of an earlier change. See :hg:`backout` for a
4482 related method.
4482 related method.
4483
4483
4484 Modified files are saved with a .orig suffix before reverting.
4484 Modified files are saved with a .orig suffix before reverting.
4485 To disable these backups, use --no-backup. It is possible to store
4485 To disable these backups, use --no-backup. It is possible to store
4486 the backup files in a custom directory relative to the root of the
4486 the backup files in a custom directory relative to the root of the
4487 repository by setting the ``ui.origbackuppath`` configuration
4487 repository by setting the ``ui.origbackuppath`` configuration
4488 option.
4488 option.
4489
4489
4490 See :hg:`help dates` for a list of formats valid for -d/--date.
4490 See :hg:`help dates` for a list of formats valid for -d/--date.
4491
4491
4492 See :hg:`help backout` for a way to reverse the effect of an
4492 See :hg:`help backout` for a way to reverse the effect of an
4493 earlier changeset.
4493 earlier changeset.
4494
4494
4495 Returns 0 on success.
4495 Returns 0 on success.
4496 """
4496 """
4497
4497
4498 if opts.get("date"):
4498 if opts.get("date"):
4499 if opts.get("rev"):
4499 if opts.get("rev"):
4500 raise error.Abort(_("you can't specify a revision and a date"))
4500 raise error.Abort(_("you can't specify a revision and a date"))
4501 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4501 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4502
4502
4503 parent, p2 = repo.dirstate.parents()
4503 parent, p2 = repo.dirstate.parents()
4504 if not opts.get('rev') and p2 != nullid:
4504 if not opts.get('rev') and p2 != nullid:
4505 # revert after merge is a trap for new users (issue2915)
4505 # revert after merge is a trap for new users (issue2915)
4506 raise error.Abort(_('uncommitted merge with no revision specified'),
4506 raise error.Abort(_('uncommitted merge with no revision specified'),
4507 hint=_("use 'hg update' or see 'hg help revert'"))
4507 hint=_("use 'hg update' or see 'hg help revert'"))
4508
4508
4509 ctx = scmutil.revsingle(repo, opts.get('rev'))
4509 ctx = scmutil.revsingle(repo, opts.get('rev'))
4510
4510
4511 if (not (pats or opts.get('include') or opts.get('exclude') or
4511 if (not (pats or opts.get('include') or opts.get('exclude') or
4512 opts.get('all') or opts.get('interactive'))):
4512 opts.get('all') or opts.get('interactive'))):
4513 msg = _("no files or directories specified")
4513 msg = _("no files or directories specified")
4514 if p2 != nullid:
4514 if p2 != nullid:
4515 hint = _("uncommitted merge, use --all to discard all changes,"
4515 hint = _("uncommitted merge, use --all to discard all changes,"
4516 " or 'hg update -C .' to abort the merge")
4516 " or 'hg update -C .' to abort the merge")
4517 raise error.Abort(msg, hint=hint)
4517 raise error.Abort(msg, hint=hint)
4518 dirty = any(repo.status())
4518 dirty = any(repo.status())
4519 node = ctx.node()
4519 node = ctx.node()
4520 if node != parent:
4520 if node != parent:
4521 if dirty:
4521 if dirty:
4522 hint = _("uncommitted changes, use --all to discard all"
4522 hint = _("uncommitted changes, use --all to discard all"
4523 " changes, or 'hg update %s' to update") % ctx.rev()
4523 " changes, or 'hg update %s' to update") % ctx.rev()
4524 else:
4524 else:
4525 hint = _("use --all to revert all files,"
4525 hint = _("use --all to revert all files,"
4526 " or 'hg update %s' to update") % ctx.rev()
4526 " or 'hg update %s' to update") % ctx.rev()
4527 elif dirty:
4527 elif dirty:
4528 hint = _("uncommitted changes, use --all to discard all changes")
4528 hint = _("uncommitted changes, use --all to discard all changes")
4529 else:
4529 else:
4530 hint = _("use --all to revert all files")
4530 hint = _("use --all to revert all files")
4531 raise error.Abort(msg, hint=hint)
4531 raise error.Abort(msg, hint=hint)
4532
4532
4533 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4533 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4534
4534
4535 @command('rollback', dryrunopts +
4535 @command('rollback', dryrunopts +
4536 [('f', 'force', False, _('ignore safety measures'))])
4536 [('f', 'force', False, _('ignore safety measures'))])
4537 def rollback(ui, repo, **opts):
4537 def rollback(ui, repo, **opts):
4538 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4538 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4539
4539
4540 Please use :hg:`commit --amend` instead of rollback to correct
4540 Please use :hg:`commit --amend` instead of rollback to correct
4541 mistakes in the last commit.
4541 mistakes in the last commit.
4542
4542
4543 This command should be used with care. There is only one level of
4543 This command should be used with care. There is only one level of
4544 rollback, and there is no way to undo a rollback. It will also
4544 rollback, and there is no way to undo a rollback. It will also
4545 restore the dirstate at the time of the last transaction, losing
4545 restore the dirstate at the time of the last transaction, losing
4546 any dirstate changes since that time. This command does not alter
4546 any dirstate changes since that time. This command does not alter
4547 the working directory.
4547 the working directory.
4548
4548
4549 Transactions are used to encapsulate the effects of all commands
4549 Transactions are used to encapsulate the effects of all commands
4550 that create new changesets or propagate existing changesets into a
4550 that create new changesets or propagate existing changesets into a
4551 repository.
4551 repository.
4552
4552
4553 .. container:: verbose
4553 .. container:: verbose
4554
4554
4555 For example, the following commands are transactional, and their
4555 For example, the following commands are transactional, and their
4556 effects can be rolled back:
4556 effects can be rolled back:
4557
4557
4558 - commit
4558 - commit
4559 - import
4559 - import
4560 - pull
4560 - pull
4561 - push (with this repository as the destination)
4561 - push (with this repository as the destination)
4562 - unbundle
4562 - unbundle
4563
4563
4564 To avoid permanent data loss, rollback will refuse to rollback a
4564 To avoid permanent data loss, rollback will refuse to rollback a
4565 commit transaction if it isn't checked out. Use --force to
4565 commit transaction if it isn't checked out. Use --force to
4566 override this protection.
4566 override this protection.
4567
4567
4568 The rollback command can be entirely disabled by setting the
4568 The rollback command can be entirely disabled by setting the
4569 ``ui.rollback`` configuration setting to false. If you're here
4569 ``ui.rollback`` configuration setting to false. If you're here
4570 because you want to use rollback and it's disabled, you can
4570 because you want to use rollback and it's disabled, you can
4571 re-enable the command by setting ``ui.rollback`` to true.
4571 re-enable the command by setting ``ui.rollback`` to true.
4572
4572
4573 This command is not intended for use on public repositories. Once
4573 This command is not intended for use on public repositories. Once
4574 changes are visible for pull by other users, rolling a transaction
4574 changes are visible for pull by other users, rolling a transaction
4575 back locally is ineffective (someone else may already have pulled
4575 back locally is ineffective (someone else may already have pulled
4576 the changes). Furthermore, a race is possible with readers of the
4576 the changes). Furthermore, a race is possible with readers of the
4577 repository; for example an in-progress pull from the repository
4577 repository; for example an in-progress pull from the repository
4578 may fail if a rollback is performed.
4578 may fail if a rollback is performed.
4579
4579
4580 Returns 0 on success, 1 if no rollback data is available.
4580 Returns 0 on success, 1 if no rollback data is available.
4581 """
4581 """
4582 if not ui.configbool('ui', 'rollback', True):
4582 if not ui.configbool('ui', 'rollback', True):
4583 raise error.Abort(_('rollback is disabled because it is unsafe'),
4583 raise error.Abort(_('rollback is disabled because it is unsafe'),
4584 hint=('see `hg help -v rollback` for information'))
4584 hint=('see `hg help -v rollback` for information'))
4585 return repo.rollback(dryrun=opts.get('dry_run'),
4585 return repo.rollback(dryrun=opts.get('dry_run'),
4586 force=opts.get('force'))
4586 force=opts.get('force'))
4587
4587
4588 @command('root', [])
4588 @command('root', [])
4589 def root(ui, repo):
4589 def root(ui, repo):
4590 """print the root (top) of the current working directory
4590 """print the root (top) of the current working directory
4591
4591
4592 Print the root directory of the current repository.
4592 Print the root directory of the current repository.
4593
4593
4594 Returns 0 on success.
4594 Returns 0 on success.
4595 """
4595 """
4596 ui.write(repo.root + "\n")
4596 ui.write(repo.root + "\n")
4597
4597
4598 @command('^serve',
4598 @command('^serve',
4599 [('A', 'accesslog', '', _('name of access log file to write to'),
4599 [('A', 'accesslog', '', _('name of access log file to write to'),
4600 _('FILE')),
4600 _('FILE')),
4601 ('d', 'daemon', None, _('run server in background')),
4601 ('d', 'daemon', None, _('run server in background')),
4602 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4602 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4603 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4603 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4604 # use string type, then we can check if something was passed
4604 # use string type, then we can check if something was passed
4605 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4605 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4606 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4606 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4607 _('ADDR')),
4607 _('ADDR')),
4608 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4608 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4609 _('PREFIX')),
4609 _('PREFIX')),
4610 ('n', 'name', '',
4610 ('n', 'name', '',
4611 _('name to show in web pages (default: working directory)'), _('NAME')),
4611 _('name to show in web pages (default: working directory)'), _('NAME')),
4612 ('', 'web-conf', '',
4612 ('', 'web-conf', '',
4613 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4613 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4614 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4614 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4615 _('FILE')),
4615 _('FILE')),
4616 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4616 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4617 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4617 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4618 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4618 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4619 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4619 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4620 ('', 'style', '', _('template style to use'), _('STYLE')),
4620 ('', 'style', '', _('template style to use'), _('STYLE')),
4621 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4621 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4622 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
4622 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4623 + subrepoopts,
4623 _('[OPTION]...'),
4624 _('[OPTION]...'),
4624 optionalrepo=True)
4625 optionalrepo=True)
4625 def serve(ui, repo, **opts):
4626 def serve(ui, repo, **opts):
4626 """start stand-alone webserver
4627 """start stand-alone webserver
4627
4628
4628 Start a local HTTP repository browser and pull server. You can use
4629 Start a local HTTP repository browser and pull server. You can use
4629 this for ad-hoc sharing and browsing of repositories. It is
4630 this for ad-hoc sharing and browsing of repositories. It is
4630 recommended to use a real web server to serve a repository for
4631 recommended to use a real web server to serve a repository for
4631 longer periods of time.
4632 longer periods of time.
4632
4633
4633 Please note that the server does not implement access control.
4634 Please note that the server does not implement access control.
4634 This means that, by default, anybody can read from the server and
4635 This means that, by default, anybody can read from the server and
4635 nobody can write to it by default. Set the ``web.allow_push``
4636 nobody can write to it by default. Set the ``web.allow_push``
4636 option to ``*`` to allow everybody to push to the server. You
4637 option to ``*`` to allow everybody to push to the server. You
4637 should use a real web server if you need to authenticate users.
4638 should use a real web server if you need to authenticate users.
4638
4639
4639 By default, the server logs accesses to stdout and errors to
4640 By default, the server logs accesses to stdout and errors to
4640 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4641 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4641 files.
4642 files.
4642
4643
4643 To have the server choose a free port number to listen on, specify
4644 To have the server choose a free port number to listen on, specify
4644 a port number of 0; in this case, the server will print the port
4645 a port number of 0; in this case, the server will print the port
4645 number it uses.
4646 number it uses.
4646
4647
4647 Returns 0 on success.
4648 Returns 0 on success.
4648 """
4649 """
4649
4650
4650 if opts["stdio"] and opts["cmdserver"]:
4651 if opts["stdio"] and opts["cmdserver"]:
4651 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4652 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4652
4653
4653 if opts["stdio"]:
4654 if opts["stdio"]:
4654 if repo is None:
4655 if repo is None:
4655 raise error.RepoError(_("there is no Mercurial repository here"
4656 raise error.RepoError(_("there is no Mercurial repository here"
4656 " (.hg not found)"))
4657 " (.hg not found)"))
4657 s = sshserver.sshserver(ui, repo)
4658 s = sshserver.sshserver(ui, repo)
4658 s.serve_forever()
4659 s.serve_forever()
4659
4660
4660 service = server.createservice(ui, repo, opts)
4661 service = server.createservice(ui, repo, opts)
4661 return server.runservice(opts, initfn=service.init, runfn=service.run)
4662 return server.runservice(opts, initfn=service.init, runfn=service.run)
4662
4663
4663 @command('^status|st',
4664 @command('^status|st',
4664 [('A', 'all', None, _('show status of all files')),
4665 [('A', 'all', None, _('show status of all files')),
4665 ('m', 'modified', None, _('show only modified files')),
4666 ('m', 'modified', None, _('show only modified files')),
4666 ('a', 'added', None, _('show only added files')),
4667 ('a', 'added', None, _('show only added files')),
4667 ('r', 'removed', None, _('show only removed files')),
4668 ('r', 'removed', None, _('show only removed files')),
4668 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4669 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4669 ('c', 'clean', None, _('show only files without changes')),
4670 ('c', 'clean', None, _('show only files without changes')),
4670 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4671 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4671 ('i', 'ignored', None, _('show only ignored files')),
4672 ('i', 'ignored', None, _('show only ignored files')),
4672 ('n', 'no-status', None, _('hide status prefix')),
4673 ('n', 'no-status', None, _('hide status prefix')),
4673 ('C', 'copies', None, _('show source of copied files')),
4674 ('C', 'copies', None, _('show source of copied files')),
4674 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4675 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4675 ('', 'rev', [], _('show difference from revision'), _('REV')),
4676 ('', 'rev', [], _('show difference from revision'), _('REV')),
4676 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4677 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4677 ] + walkopts + subrepoopts + formatteropts,
4678 ] + walkopts + subrepoopts + formatteropts,
4678 _('[OPTION]... [FILE]...'),
4679 _('[OPTION]... [FILE]...'),
4679 inferrepo=True)
4680 inferrepo=True)
4680 def status(ui, repo, *pats, **opts):
4681 def status(ui, repo, *pats, **opts):
4681 """show changed files in the working directory
4682 """show changed files in the working directory
4682
4683
4683 Show status of files in the repository. If names are given, only
4684 Show status of files in the repository. If names are given, only
4684 files that match are shown. Files that are clean or ignored or
4685 files that match are shown. Files that are clean or ignored or
4685 the source of a copy/move operation, are not listed unless
4686 the source of a copy/move operation, are not listed unless
4686 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4687 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4687 Unless options described with "show only ..." are given, the
4688 Unless options described with "show only ..." are given, the
4688 options -mardu are used.
4689 options -mardu are used.
4689
4690
4690 Option -q/--quiet hides untracked (unknown and ignored) files
4691 Option -q/--quiet hides untracked (unknown and ignored) files
4691 unless explicitly requested with -u/--unknown or -i/--ignored.
4692 unless explicitly requested with -u/--unknown or -i/--ignored.
4692
4693
4693 .. note::
4694 .. note::
4694
4695
4695 :hg:`status` may appear to disagree with diff if permissions have
4696 :hg:`status` may appear to disagree with diff if permissions have
4696 changed or a merge has occurred. The standard diff format does
4697 changed or a merge has occurred. The standard diff format does
4697 not report permission changes and diff only reports changes
4698 not report permission changes and diff only reports changes
4698 relative to one merge parent.
4699 relative to one merge parent.
4699
4700
4700 If one revision is given, it is used as the base revision.
4701 If one revision is given, it is used as the base revision.
4701 If two revisions are given, the differences between them are
4702 If two revisions are given, the differences between them are
4702 shown. The --change option can also be used as a shortcut to list
4703 shown. The --change option can also be used as a shortcut to list
4703 the changed files of a revision from its first parent.
4704 the changed files of a revision from its first parent.
4704
4705
4705 The codes used to show the status of files are::
4706 The codes used to show the status of files are::
4706
4707
4707 M = modified
4708 M = modified
4708 A = added
4709 A = added
4709 R = removed
4710 R = removed
4710 C = clean
4711 C = clean
4711 ! = missing (deleted by non-hg command, but still tracked)
4712 ! = missing (deleted by non-hg command, but still tracked)
4712 ? = not tracked
4713 ? = not tracked
4713 I = ignored
4714 I = ignored
4714 = origin of the previous file (with --copies)
4715 = origin of the previous file (with --copies)
4715
4716
4716 .. container:: verbose
4717 .. container:: verbose
4717
4718
4718 Examples:
4719 Examples:
4719
4720
4720 - show changes in the working directory relative to a
4721 - show changes in the working directory relative to a
4721 changeset::
4722 changeset::
4722
4723
4723 hg status --rev 9353
4724 hg status --rev 9353
4724
4725
4725 - show changes in the working directory relative to the
4726 - show changes in the working directory relative to the
4726 current directory (see :hg:`help patterns` for more information)::
4727 current directory (see :hg:`help patterns` for more information)::
4727
4728
4728 hg status re:
4729 hg status re:
4729
4730
4730 - show all changes including copies in an existing changeset::
4731 - show all changes including copies in an existing changeset::
4731
4732
4732 hg status --copies --change 9353
4733 hg status --copies --change 9353
4733
4734
4734 - get a NUL separated list of added files, suitable for xargs::
4735 - get a NUL separated list of added files, suitable for xargs::
4735
4736
4736 hg status -an0
4737 hg status -an0
4737
4738
4738 Returns 0 on success.
4739 Returns 0 on success.
4739 """
4740 """
4740
4741
4741 opts = pycompat.byteskwargs(opts)
4742 opts = pycompat.byteskwargs(opts)
4742 revs = opts.get('rev')
4743 revs = opts.get('rev')
4743 change = opts.get('change')
4744 change = opts.get('change')
4744
4745
4745 if revs and change:
4746 if revs and change:
4746 msg = _('cannot specify --rev and --change at the same time')
4747 msg = _('cannot specify --rev and --change at the same time')
4747 raise error.Abort(msg)
4748 raise error.Abort(msg)
4748 elif change:
4749 elif change:
4749 node2 = scmutil.revsingle(repo, change, None).node()
4750 node2 = scmutil.revsingle(repo, change, None).node()
4750 node1 = repo[node2].p1().node()
4751 node1 = repo[node2].p1().node()
4751 else:
4752 else:
4752 node1, node2 = scmutil.revpair(repo, revs)
4753 node1, node2 = scmutil.revpair(repo, revs)
4753
4754
4754 if pats or ui.configbool('commands', 'status.relative'):
4755 if pats or ui.configbool('commands', 'status.relative'):
4755 cwd = repo.getcwd()
4756 cwd = repo.getcwd()
4756 else:
4757 else:
4757 cwd = ''
4758 cwd = ''
4758
4759
4759 if opts.get('print0'):
4760 if opts.get('print0'):
4760 end = '\0'
4761 end = '\0'
4761 else:
4762 else:
4762 end = '\n'
4763 end = '\n'
4763 copy = {}
4764 copy = {}
4764 states = 'modified added removed deleted unknown ignored clean'.split()
4765 states = 'modified added removed deleted unknown ignored clean'.split()
4765 show = [k for k in states if opts.get(k)]
4766 show = [k for k in states if opts.get(k)]
4766 if opts.get('all'):
4767 if opts.get('all'):
4767 show += ui.quiet and (states[:4] + ['clean']) or states
4768 show += ui.quiet and (states[:4] + ['clean']) or states
4768 if not show:
4769 if not show:
4769 if ui.quiet:
4770 if ui.quiet:
4770 show = states[:4]
4771 show = states[:4]
4771 else:
4772 else:
4772 show = states[:5]
4773 show = states[:5]
4773
4774
4774 m = scmutil.match(repo[node2], pats, opts)
4775 m = scmutil.match(repo[node2], pats, opts)
4775 stat = repo.status(node1, node2, m,
4776 stat = repo.status(node1, node2, m,
4776 'ignored' in show, 'clean' in show, 'unknown' in show,
4777 'ignored' in show, 'clean' in show, 'unknown' in show,
4777 opts.get('subrepos'))
4778 opts.get('subrepos'))
4778 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4779 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4779
4780
4780 if (opts.get('all') or opts.get('copies')
4781 if (opts.get('all') or opts.get('copies')
4781 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4782 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4782 copy = copies.pathcopies(repo[node1], repo[node2], m)
4783 copy = copies.pathcopies(repo[node1], repo[node2], m)
4783
4784
4784 ui.pager('status')
4785 ui.pager('status')
4785 fm = ui.formatter('status', opts)
4786 fm = ui.formatter('status', opts)
4786 fmt = '%s' + end
4787 fmt = '%s' + end
4787 showchar = not opts.get('no_status')
4788 showchar = not opts.get('no_status')
4788
4789
4789 for state, char, files in changestates:
4790 for state, char, files in changestates:
4790 if state in show:
4791 if state in show:
4791 label = 'status.' + state
4792 label = 'status.' + state
4792 for f in files:
4793 for f in files:
4793 fm.startitem()
4794 fm.startitem()
4794 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4795 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4795 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4796 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4796 if f in copy:
4797 if f in copy:
4797 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4798 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4798 label='status.copied')
4799 label='status.copied')
4799 fm.end()
4800 fm.end()
4800
4801
4801 @command('^summary|sum',
4802 @command('^summary|sum',
4802 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4803 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4803 def summary(ui, repo, **opts):
4804 def summary(ui, repo, **opts):
4804 """summarize working directory state
4805 """summarize working directory state
4805
4806
4806 This generates a brief summary of the working directory state,
4807 This generates a brief summary of the working directory state,
4807 including parents, branch, commit status, phase and available updates.
4808 including parents, branch, commit status, phase and available updates.
4808
4809
4809 With the --remote option, this will check the default paths for
4810 With the --remote option, this will check the default paths for
4810 incoming and outgoing changes. This can be time-consuming.
4811 incoming and outgoing changes. This can be time-consuming.
4811
4812
4812 Returns 0 on success.
4813 Returns 0 on success.
4813 """
4814 """
4814
4815
4815 ui.pager('summary')
4816 ui.pager('summary')
4816 ctx = repo[None]
4817 ctx = repo[None]
4817 parents = ctx.parents()
4818 parents = ctx.parents()
4818 pnode = parents[0].node()
4819 pnode = parents[0].node()
4819 marks = []
4820 marks = []
4820
4821
4821 ms = None
4822 ms = None
4822 try:
4823 try:
4823 ms = mergemod.mergestate.read(repo)
4824 ms = mergemod.mergestate.read(repo)
4824 except error.UnsupportedMergeRecords as e:
4825 except error.UnsupportedMergeRecords as e:
4825 s = ' '.join(e.recordtypes)
4826 s = ' '.join(e.recordtypes)
4826 ui.warn(
4827 ui.warn(
4827 _('warning: merge state has unsupported record types: %s\n') % s)
4828 _('warning: merge state has unsupported record types: %s\n') % s)
4828 unresolved = 0
4829 unresolved = 0
4829 else:
4830 else:
4830 unresolved = [f for f in ms if ms[f] == 'u']
4831 unresolved = [f for f in ms if ms[f] == 'u']
4831
4832
4832 for p in parents:
4833 for p in parents:
4833 # label with log.changeset (instead of log.parent) since this
4834 # label with log.changeset (instead of log.parent) since this
4834 # shows a working directory parent *changeset*:
4835 # shows a working directory parent *changeset*:
4835 # i18n: column positioning for "hg summary"
4836 # i18n: column positioning for "hg summary"
4836 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4837 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4837 label=cmdutil._changesetlabels(p))
4838 label=cmdutil._changesetlabels(p))
4838 ui.write(' '.join(p.tags()), label='log.tag')
4839 ui.write(' '.join(p.tags()), label='log.tag')
4839 if p.bookmarks():
4840 if p.bookmarks():
4840 marks.extend(p.bookmarks())
4841 marks.extend(p.bookmarks())
4841 if p.rev() == -1:
4842 if p.rev() == -1:
4842 if not len(repo):
4843 if not len(repo):
4843 ui.write(_(' (empty repository)'))
4844 ui.write(_(' (empty repository)'))
4844 else:
4845 else:
4845 ui.write(_(' (no revision checked out)'))
4846 ui.write(_(' (no revision checked out)'))
4846 if p.obsolete():
4847 if p.obsolete():
4847 ui.write(_(' (obsolete)'))
4848 ui.write(_(' (obsolete)'))
4848 if p.troubled():
4849 if p.troubled():
4849 ui.write(' ('
4850 ui.write(' ('
4850 + ', '.join(ui.label(trouble, 'trouble.%s' % trouble)
4851 + ', '.join(ui.label(trouble, 'trouble.%s' % trouble)
4851 for trouble in p.troubles())
4852 for trouble in p.troubles())
4852 + ')')
4853 + ')')
4853 ui.write('\n')
4854 ui.write('\n')
4854 if p.description():
4855 if p.description():
4855 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4856 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4856 label='log.summary')
4857 label='log.summary')
4857
4858
4858 branch = ctx.branch()
4859 branch = ctx.branch()
4859 bheads = repo.branchheads(branch)
4860 bheads = repo.branchheads(branch)
4860 # i18n: column positioning for "hg summary"
4861 # i18n: column positioning for "hg summary"
4861 m = _('branch: %s\n') % branch
4862 m = _('branch: %s\n') % branch
4862 if branch != 'default':
4863 if branch != 'default':
4863 ui.write(m, label='log.branch')
4864 ui.write(m, label='log.branch')
4864 else:
4865 else:
4865 ui.status(m, label='log.branch')
4866 ui.status(m, label='log.branch')
4866
4867
4867 if marks:
4868 if marks:
4868 active = repo._activebookmark
4869 active = repo._activebookmark
4869 # i18n: column positioning for "hg summary"
4870 # i18n: column positioning for "hg summary"
4870 ui.write(_('bookmarks:'), label='log.bookmark')
4871 ui.write(_('bookmarks:'), label='log.bookmark')
4871 if active is not None:
4872 if active is not None:
4872 if active in marks:
4873 if active in marks:
4873 ui.write(' *' + active, label=activebookmarklabel)
4874 ui.write(' *' + active, label=activebookmarklabel)
4874 marks.remove(active)
4875 marks.remove(active)
4875 else:
4876 else:
4876 ui.write(' [%s]' % active, label=activebookmarklabel)
4877 ui.write(' [%s]' % active, label=activebookmarklabel)
4877 for m in marks:
4878 for m in marks:
4878 ui.write(' ' + m, label='log.bookmark')
4879 ui.write(' ' + m, label='log.bookmark')
4879 ui.write('\n', label='log.bookmark')
4880 ui.write('\n', label='log.bookmark')
4880
4881
4881 status = repo.status(unknown=True)
4882 status = repo.status(unknown=True)
4882
4883
4883 c = repo.dirstate.copies()
4884 c = repo.dirstate.copies()
4884 copied, renamed = [], []
4885 copied, renamed = [], []
4885 for d, s in c.iteritems():
4886 for d, s in c.iteritems():
4886 if s in status.removed:
4887 if s in status.removed:
4887 status.removed.remove(s)
4888 status.removed.remove(s)
4888 renamed.append(d)
4889 renamed.append(d)
4889 else:
4890 else:
4890 copied.append(d)
4891 copied.append(d)
4891 if d in status.added:
4892 if d in status.added:
4892 status.added.remove(d)
4893 status.added.remove(d)
4893
4894
4894 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4895 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4895
4896
4896 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
4897 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
4897 (ui.label(_('%d added'), 'status.added'), status.added),
4898 (ui.label(_('%d added'), 'status.added'), status.added),
4898 (ui.label(_('%d removed'), 'status.removed'), status.removed),
4899 (ui.label(_('%d removed'), 'status.removed'), status.removed),
4899 (ui.label(_('%d renamed'), 'status.copied'), renamed),
4900 (ui.label(_('%d renamed'), 'status.copied'), renamed),
4900 (ui.label(_('%d copied'), 'status.copied'), copied),
4901 (ui.label(_('%d copied'), 'status.copied'), copied),
4901 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
4902 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
4902 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
4903 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
4903 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
4904 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
4904 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
4905 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
4905 t = []
4906 t = []
4906 for l, s in labels:
4907 for l, s in labels:
4907 if s:
4908 if s:
4908 t.append(l % len(s))
4909 t.append(l % len(s))
4909
4910
4910 t = ', '.join(t)
4911 t = ', '.join(t)
4911 cleanworkdir = False
4912 cleanworkdir = False
4912
4913
4913 if repo.vfs.exists('graftstate'):
4914 if repo.vfs.exists('graftstate'):
4914 t += _(' (graft in progress)')
4915 t += _(' (graft in progress)')
4915 if repo.vfs.exists('updatestate'):
4916 if repo.vfs.exists('updatestate'):
4916 t += _(' (interrupted update)')
4917 t += _(' (interrupted update)')
4917 elif len(parents) > 1:
4918 elif len(parents) > 1:
4918 t += _(' (merge)')
4919 t += _(' (merge)')
4919 elif branch != parents[0].branch():
4920 elif branch != parents[0].branch():
4920 t += _(' (new branch)')
4921 t += _(' (new branch)')
4921 elif (parents[0].closesbranch() and
4922 elif (parents[0].closesbranch() and
4922 pnode in repo.branchheads(branch, closed=True)):
4923 pnode in repo.branchheads(branch, closed=True)):
4923 t += _(' (head closed)')
4924 t += _(' (head closed)')
4924 elif not (status.modified or status.added or status.removed or renamed or
4925 elif not (status.modified or status.added or status.removed or renamed or
4925 copied or subs):
4926 copied or subs):
4926 t += _(' (clean)')
4927 t += _(' (clean)')
4927 cleanworkdir = True
4928 cleanworkdir = True
4928 elif pnode not in bheads:
4929 elif pnode not in bheads:
4929 t += _(' (new branch head)')
4930 t += _(' (new branch head)')
4930
4931
4931 if parents:
4932 if parents:
4932 pendingphase = max(p.phase() for p in parents)
4933 pendingphase = max(p.phase() for p in parents)
4933 else:
4934 else:
4934 pendingphase = phases.public
4935 pendingphase = phases.public
4935
4936
4936 if pendingphase > phases.newcommitphase(ui):
4937 if pendingphase > phases.newcommitphase(ui):
4937 t += ' (%s)' % phases.phasenames[pendingphase]
4938 t += ' (%s)' % phases.phasenames[pendingphase]
4938
4939
4939 if cleanworkdir:
4940 if cleanworkdir:
4940 # i18n: column positioning for "hg summary"
4941 # i18n: column positioning for "hg summary"
4941 ui.status(_('commit: %s\n') % t.strip())
4942 ui.status(_('commit: %s\n') % t.strip())
4942 else:
4943 else:
4943 # i18n: column positioning for "hg summary"
4944 # i18n: column positioning for "hg summary"
4944 ui.write(_('commit: %s\n') % t.strip())
4945 ui.write(_('commit: %s\n') % t.strip())
4945
4946
4946 # all ancestors of branch heads - all ancestors of parent = new csets
4947 # all ancestors of branch heads - all ancestors of parent = new csets
4947 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
4948 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
4948 bheads))
4949 bheads))
4949
4950
4950 if new == 0:
4951 if new == 0:
4951 # i18n: column positioning for "hg summary"
4952 # i18n: column positioning for "hg summary"
4952 ui.status(_('update: (current)\n'))
4953 ui.status(_('update: (current)\n'))
4953 elif pnode not in bheads:
4954 elif pnode not in bheads:
4954 # i18n: column positioning for "hg summary"
4955 # i18n: column positioning for "hg summary"
4955 ui.write(_('update: %d new changesets (update)\n') % new)
4956 ui.write(_('update: %d new changesets (update)\n') % new)
4956 else:
4957 else:
4957 # i18n: column positioning for "hg summary"
4958 # i18n: column positioning for "hg summary"
4958 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4959 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4959 (new, len(bheads)))
4960 (new, len(bheads)))
4960
4961
4961 t = []
4962 t = []
4962 draft = len(repo.revs('draft()'))
4963 draft = len(repo.revs('draft()'))
4963 if draft:
4964 if draft:
4964 t.append(_('%d draft') % draft)
4965 t.append(_('%d draft') % draft)
4965 secret = len(repo.revs('secret()'))
4966 secret = len(repo.revs('secret()'))
4966 if secret:
4967 if secret:
4967 t.append(_('%d secret') % secret)
4968 t.append(_('%d secret') % secret)
4968
4969
4969 if draft or secret:
4970 if draft or secret:
4970 ui.status(_('phases: %s\n') % ', '.join(t))
4971 ui.status(_('phases: %s\n') % ', '.join(t))
4971
4972
4972 if obsolete.isenabled(repo, obsolete.createmarkersopt):
4973 if obsolete.isenabled(repo, obsolete.createmarkersopt):
4973 for trouble in ("unstable", "divergent", "bumped"):
4974 for trouble in ("unstable", "divergent", "bumped"):
4974 numtrouble = len(repo.revs(trouble + "()"))
4975 numtrouble = len(repo.revs(trouble + "()"))
4975 # We write all the possibilities to ease translation
4976 # We write all the possibilities to ease translation
4976 troublemsg = {
4977 troublemsg = {
4977 "unstable": _("unstable: %d changesets"),
4978 "unstable": _("unstable: %d changesets"),
4978 "divergent": _("divergent: %d changesets"),
4979 "divergent": _("divergent: %d changesets"),
4979 "bumped": _("bumped: %d changesets"),
4980 "bumped": _("bumped: %d changesets"),
4980 }
4981 }
4981 if numtrouble > 0:
4982 if numtrouble > 0:
4982 ui.status(troublemsg[trouble] % numtrouble + "\n")
4983 ui.status(troublemsg[trouble] % numtrouble + "\n")
4983
4984
4984 cmdutil.summaryhooks(ui, repo)
4985 cmdutil.summaryhooks(ui, repo)
4985
4986
4986 if opts.get('remote'):
4987 if opts.get('remote'):
4987 needsincoming, needsoutgoing = True, True
4988 needsincoming, needsoutgoing = True, True
4988 else:
4989 else:
4989 needsincoming, needsoutgoing = False, False
4990 needsincoming, needsoutgoing = False, False
4990 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
4991 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
4991 if i:
4992 if i:
4992 needsincoming = True
4993 needsincoming = True
4993 if o:
4994 if o:
4994 needsoutgoing = True
4995 needsoutgoing = True
4995 if not needsincoming and not needsoutgoing:
4996 if not needsincoming and not needsoutgoing:
4996 return
4997 return
4997
4998
4998 def getincoming():
4999 def getincoming():
4999 source, branches = hg.parseurl(ui.expandpath('default'))
5000 source, branches = hg.parseurl(ui.expandpath('default'))
5000 sbranch = branches[0]
5001 sbranch = branches[0]
5001 try:
5002 try:
5002 other = hg.peer(repo, {}, source)
5003 other = hg.peer(repo, {}, source)
5003 except error.RepoError:
5004 except error.RepoError:
5004 if opts.get('remote'):
5005 if opts.get('remote'):
5005 raise
5006 raise
5006 return source, sbranch, None, None, None
5007 return source, sbranch, None, None, None
5007 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5008 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5008 if revs:
5009 if revs:
5009 revs = [other.lookup(rev) for rev in revs]
5010 revs = [other.lookup(rev) for rev in revs]
5010 ui.debug('comparing with %s\n' % util.hidepassword(source))
5011 ui.debug('comparing with %s\n' % util.hidepassword(source))
5011 repo.ui.pushbuffer()
5012 repo.ui.pushbuffer()
5012 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5013 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5013 repo.ui.popbuffer()
5014 repo.ui.popbuffer()
5014 return source, sbranch, other, commoninc, commoninc[1]
5015 return source, sbranch, other, commoninc, commoninc[1]
5015
5016
5016 if needsincoming:
5017 if needsincoming:
5017 source, sbranch, sother, commoninc, incoming = getincoming()
5018 source, sbranch, sother, commoninc, incoming = getincoming()
5018 else:
5019 else:
5019 source = sbranch = sother = commoninc = incoming = None
5020 source = sbranch = sother = commoninc = incoming = None
5020
5021
5021 def getoutgoing():
5022 def getoutgoing():
5022 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5023 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5023 dbranch = branches[0]
5024 dbranch = branches[0]
5024 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5025 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5025 if source != dest:
5026 if source != dest:
5026 try:
5027 try:
5027 dother = hg.peer(repo, {}, dest)
5028 dother = hg.peer(repo, {}, dest)
5028 except error.RepoError:
5029 except error.RepoError:
5029 if opts.get('remote'):
5030 if opts.get('remote'):
5030 raise
5031 raise
5031 return dest, dbranch, None, None
5032 return dest, dbranch, None, None
5032 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5033 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5033 elif sother is None:
5034 elif sother is None:
5034 # there is no explicit destination peer, but source one is invalid
5035 # there is no explicit destination peer, but source one is invalid
5035 return dest, dbranch, None, None
5036 return dest, dbranch, None, None
5036 else:
5037 else:
5037 dother = sother
5038 dother = sother
5038 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5039 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5039 common = None
5040 common = None
5040 else:
5041 else:
5041 common = commoninc
5042 common = commoninc
5042 if revs:
5043 if revs:
5043 revs = [repo.lookup(rev) for rev in revs]
5044 revs = [repo.lookup(rev) for rev in revs]
5044 repo.ui.pushbuffer()
5045 repo.ui.pushbuffer()
5045 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5046 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5046 commoninc=common)
5047 commoninc=common)
5047 repo.ui.popbuffer()
5048 repo.ui.popbuffer()
5048 return dest, dbranch, dother, outgoing
5049 return dest, dbranch, dother, outgoing
5049
5050
5050 if needsoutgoing:
5051 if needsoutgoing:
5051 dest, dbranch, dother, outgoing = getoutgoing()
5052 dest, dbranch, dother, outgoing = getoutgoing()
5052 else:
5053 else:
5053 dest = dbranch = dother = outgoing = None
5054 dest = dbranch = dother = outgoing = None
5054
5055
5055 if opts.get('remote'):
5056 if opts.get('remote'):
5056 t = []
5057 t = []
5057 if incoming:
5058 if incoming:
5058 t.append(_('1 or more incoming'))
5059 t.append(_('1 or more incoming'))
5059 o = outgoing.missing
5060 o = outgoing.missing
5060 if o:
5061 if o:
5061 t.append(_('%d outgoing') % len(o))
5062 t.append(_('%d outgoing') % len(o))
5062 other = dother or sother
5063 other = dother or sother
5063 if 'bookmarks' in other.listkeys('namespaces'):
5064 if 'bookmarks' in other.listkeys('namespaces'):
5064 counts = bookmarks.summary(repo, other)
5065 counts = bookmarks.summary(repo, other)
5065 if counts[0] > 0:
5066 if counts[0] > 0:
5066 t.append(_('%d incoming bookmarks') % counts[0])
5067 t.append(_('%d incoming bookmarks') % counts[0])
5067 if counts[1] > 0:
5068 if counts[1] > 0:
5068 t.append(_('%d outgoing bookmarks') % counts[1])
5069 t.append(_('%d outgoing bookmarks') % counts[1])
5069
5070
5070 if t:
5071 if t:
5071 # i18n: column positioning for "hg summary"
5072 # i18n: column positioning for "hg summary"
5072 ui.write(_('remote: %s\n') % (', '.join(t)))
5073 ui.write(_('remote: %s\n') % (', '.join(t)))
5073 else:
5074 else:
5074 # i18n: column positioning for "hg summary"
5075 # i18n: column positioning for "hg summary"
5075 ui.status(_('remote: (synced)\n'))
5076 ui.status(_('remote: (synced)\n'))
5076
5077
5077 cmdutil.summaryremotehooks(ui, repo, opts,
5078 cmdutil.summaryremotehooks(ui, repo, opts,
5078 ((source, sbranch, sother, commoninc),
5079 ((source, sbranch, sother, commoninc),
5079 (dest, dbranch, dother, outgoing)))
5080 (dest, dbranch, dother, outgoing)))
5080
5081
5081 @command('tag',
5082 @command('tag',
5082 [('f', 'force', None, _('force tag')),
5083 [('f', 'force', None, _('force tag')),
5083 ('l', 'local', None, _('make the tag local')),
5084 ('l', 'local', None, _('make the tag local')),
5084 ('r', 'rev', '', _('revision to tag'), _('REV')),
5085 ('r', 'rev', '', _('revision to tag'), _('REV')),
5085 ('', 'remove', None, _('remove a tag')),
5086 ('', 'remove', None, _('remove a tag')),
5086 # -l/--local is already there, commitopts cannot be used
5087 # -l/--local is already there, commitopts cannot be used
5087 ('e', 'edit', None, _('invoke editor on commit messages')),
5088 ('e', 'edit', None, _('invoke editor on commit messages')),
5088 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5089 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5089 ] + commitopts2,
5090 ] + commitopts2,
5090 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5091 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5091 def tag(ui, repo, name1, *names, **opts):
5092 def tag(ui, repo, name1, *names, **opts):
5092 """add one or more tags for the current or given revision
5093 """add one or more tags for the current or given revision
5093
5094
5094 Name a particular revision using <name>.
5095 Name a particular revision using <name>.
5095
5096
5096 Tags are used to name particular revisions of the repository and are
5097 Tags are used to name particular revisions of the repository and are
5097 very useful to compare different revisions, to go back to significant
5098 very useful to compare different revisions, to go back to significant
5098 earlier versions or to mark branch points as releases, etc. Changing
5099 earlier versions or to mark branch points as releases, etc. Changing
5099 an existing tag is normally disallowed; use -f/--force to override.
5100 an existing tag is normally disallowed; use -f/--force to override.
5100
5101
5101 If no revision is given, the parent of the working directory is
5102 If no revision is given, the parent of the working directory is
5102 used.
5103 used.
5103
5104
5104 To facilitate version control, distribution, and merging of tags,
5105 To facilitate version control, distribution, and merging of tags,
5105 they are stored as a file named ".hgtags" which is managed similarly
5106 they are stored as a file named ".hgtags" which is managed similarly
5106 to other project files and can be hand-edited if necessary. This
5107 to other project files and can be hand-edited if necessary. This
5107 also means that tagging creates a new commit. The file
5108 also means that tagging creates a new commit. The file
5108 ".hg/localtags" is used for local tags (not shared among
5109 ".hg/localtags" is used for local tags (not shared among
5109 repositories).
5110 repositories).
5110
5111
5111 Tag commits are usually made at the head of a branch. If the parent
5112 Tag commits are usually made at the head of a branch. If the parent
5112 of the working directory is not a branch head, :hg:`tag` aborts; use
5113 of the working directory is not a branch head, :hg:`tag` aborts; use
5113 -f/--force to force the tag commit to be based on a non-head
5114 -f/--force to force the tag commit to be based on a non-head
5114 changeset.
5115 changeset.
5115
5116
5116 See :hg:`help dates` for a list of formats valid for -d/--date.
5117 See :hg:`help dates` for a list of formats valid for -d/--date.
5117
5118
5118 Since tag names have priority over branch names during revision
5119 Since tag names have priority over branch names during revision
5119 lookup, using an existing branch name as a tag name is discouraged.
5120 lookup, using an existing branch name as a tag name is discouraged.
5120
5121
5121 Returns 0 on success.
5122 Returns 0 on success.
5122 """
5123 """
5123 wlock = lock = None
5124 wlock = lock = None
5124 try:
5125 try:
5125 wlock = repo.wlock()
5126 wlock = repo.wlock()
5126 lock = repo.lock()
5127 lock = repo.lock()
5127 rev_ = "."
5128 rev_ = "."
5128 names = [t.strip() for t in (name1,) + names]
5129 names = [t.strip() for t in (name1,) + names]
5129 if len(names) != len(set(names)):
5130 if len(names) != len(set(names)):
5130 raise error.Abort(_('tag names must be unique'))
5131 raise error.Abort(_('tag names must be unique'))
5131 for n in names:
5132 for n in names:
5132 scmutil.checknewlabel(repo, n, 'tag')
5133 scmutil.checknewlabel(repo, n, 'tag')
5133 if not n:
5134 if not n:
5134 raise error.Abort(_('tag names cannot consist entirely of '
5135 raise error.Abort(_('tag names cannot consist entirely of '
5135 'whitespace'))
5136 'whitespace'))
5136 if opts.get('rev') and opts.get('remove'):
5137 if opts.get('rev') and opts.get('remove'):
5137 raise error.Abort(_("--rev and --remove are incompatible"))
5138 raise error.Abort(_("--rev and --remove are incompatible"))
5138 if opts.get('rev'):
5139 if opts.get('rev'):
5139 rev_ = opts['rev']
5140 rev_ = opts['rev']
5140 message = opts.get('message')
5141 message = opts.get('message')
5141 if opts.get('remove'):
5142 if opts.get('remove'):
5142 if opts.get('local'):
5143 if opts.get('local'):
5143 expectedtype = 'local'
5144 expectedtype = 'local'
5144 else:
5145 else:
5145 expectedtype = 'global'
5146 expectedtype = 'global'
5146
5147
5147 for n in names:
5148 for n in names:
5148 if not repo.tagtype(n):
5149 if not repo.tagtype(n):
5149 raise error.Abort(_("tag '%s' does not exist") % n)
5150 raise error.Abort(_("tag '%s' does not exist") % n)
5150 if repo.tagtype(n) != expectedtype:
5151 if repo.tagtype(n) != expectedtype:
5151 if expectedtype == 'global':
5152 if expectedtype == 'global':
5152 raise error.Abort(_("tag '%s' is not a global tag") % n)
5153 raise error.Abort(_("tag '%s' is not a global tag") % n)
5153 else:
5154 else:
5154 raise error.Abort(_("tag '%s' is not a local tag") % n)
5155 raise error.Abort(_("tag '%s' is not a local tag") % n)
5155 rev_ = 'null'
5156 rev_ = 'null'
5156 if not message:
5157 if not message:
5157 # we don't translate commit messages
5158 # we don't translate commit messages
5158 message = 'Removed tag %s' % ', '.join(names)
5159 message = 'Removed tag %s' % ', '.join(names)
5159 elif not opts.get('force'):
5160 elif not opts.get('force'):
5160 for n in names:
5161 for n in names:
5161 if n in repo.tags():
5162 if n in repo.tags():
5162 raise error.Abort(_("tag '%s' already exists "
5163 raise error.Abort(_("tag '%s' already exists "
5163 "(use -f to force)") % n)
5164 "(use -f to force)") % n)
5164 if not opts.get('local'):
5165 if not opts.get('local'):
5165 p1, p2 = repo.dirstate.parents()
5166 p1, p2 = repo.dirstate.parents()
5166 if p2 != nullid:
5167 if p2 != nullid:
5167 raise error.Abort(_('uncommitted merge'))
5168 raise error.Abort(_('uncommitted merge'))
5168 bheads = repo.branchheads()
5169 bheads = repo.branchheads()
5169 if not opts.get('force') and bheads and p1 not in bheads:
5170 if not opts.get('force') and bheads and p1 not in bheads:
5170 raise error.Abort(_('working directory is not at a branch head '
5171 raise error.Abort(_('working directory is not at a branch head '
5171 '(use -f to force)'))
5172 '(use -f to force)'))
5172 r = scmutil.revsingle(repo, rev_).node()
5173 r = scmutil.revsingle(repo, rev_).node()
5173
5174
5174 if not message:
5175 if not message:
5175 # we don't translate commit messages
5176 # we don't translate commit messages
5176 message = ('Added tag %s for changeset %s' %
5177 message = ('Added tag %s for changeset %s' %
5177 (', '.join(names), short(r)))
5178 (', '.join(names), short(r)))
5178
5179
5179 date = opts.get('date')
5180 date = opts.get('date')
5180 if date:
5181 if date:
5181 date = util.parsedate(date)
5182 date = util.parsedate(date)
5182
5183
5183 if opts.get('remove'):
5184 if opts.get('remove'):
5184 editform = 'tag.remove'
5185 editform = 'tag.remove'
5185 else:
5186 else:
5186 editform = 'tag.add'
5187 editform = 'tag.add'
5187 editor = cmdutil.getcommiteditor(editform=editform, **opts)
5188 editor = cmdutil.getcommiteditor(editform=editform, **opts)
5188
5189
5189 # don't allow tagging the null rev
5190 # don't allow tagging the null rev
5190 if (not opts.get('remove') and
5191 if (not opts.get('remove') and
5191 scmutil.revsingle(repo, rev_).rev() == nullrev):
5192 scmutil.revsingle(repo, rev_).rev() == nullrev):
5192 raise error.Abort(_("cannot tag null revision"))
5193 raise error.Abort(_("cannot tag null revision"))
5193
5194
5194 tagsmod.tag(repo, names, r, message, opts.get('local'),
5195 tagsmod.tag(repo, names, r, message, opts.get('local'),
5195 opts.get('user'), date, editor=editor)
5196 opts.get('user'), date, editor=editor)
5196 finally:
5197 finally:
5197 release(lock, wlock)
5198 release(lock, wlock)
5198
5199
5199 @command('tags', formatteropts, '')
5200 @command('tags', formatteropts, '')
5200 def tags(ui, repo, **opts):
5201 def tags(ui, repo, **opts):
5201 """list repository tags
5202 """list repository tags
5202
5203
5203 This lists both regular and local tags. When the -v/--verbose
5204 This lists both regular and local tags. When the -v/--verbose
5204 switch is used, a third column "local" is printed for local tags.
5205 switch is used, a third column "local" is printed for local tags.
5205 When the -q/--quiet switch is used, only the tag name is printed.
5206 When the -q/--quiet switch is used, only the tag name is printed.
5206
5207
5207 Returns 0 on success.
5208 Returns 0 on success.
5208 """
5209 """
5209
5210
5210 ui.pager('tags')
5211 ui.pager('tags')
5211 fm = ui.formatter('tags', opts)
5212 fm = ui.formatter('tags', opts)
5212 hexfunc = fm.hexfunc
5213 hexfunc = fm.hexfunc
5213 tagtype = ""
5214 tagtype = ""
5214
5215
5215 for t, n in reversed(repo.tagslist()):
5216 for t, n in reversed(repo.tagslist()):
5216 hn = hexfunc(n)
5217 hn = hexfunc(n)
5217 label = 'tags.normal'
5218 label = 'tags.normal'
5218 tagtype = ''
5219 tagtype = ''
5219 if repo.tagtype(t) == 'local':
5220 if repo.tagtype(t) == 'local':
5220 label = 'tags.local'
5221 label = 'tags.local'
5221 tagtype = 'local'
5222 tagtype = 'local'
5222
5223
5223 fm.startitem()
5224 fm.startitem()
5224 fm.write('tag', '%s', t, label=label)
5225 fm.write('tag', '%s', t, label=label)
5225 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5226 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5226 fm.condwrite(not ui.quiet, 'rev node', fmt,
5227 fm.condwrite(not ui.quiet, 'rev node', fmt,
5227 repo.changelog.rev(n), hn, label=label)
5228 repo.changelog.rev(n), hn, label=label)
5228 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5229 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5229 tagtype, label=label)
5230 tagtype, label=label)
5230 fm.plain('\n')
5231 fm.plain('\n')
5231 fm.end()
5232 fm.end()
5232
5233
5233 @command('tip',
5234 @command('tip',
5234 [('p', 'patch', None, _('show patch')),
5235 [('p', 'patch', None, _('show patch')),
5235 ('g', 'git', None, _('use git extended diff format')),
5236 ('g', 'git', None, _('use git extended diff format')),
5236 ] + templateopts,
5237 ] + templateopts,
5237 _('[-p] [-g]'))
5238 _('[-p] [-g]'))
5238 def tip(ui, repo, **opts):
5239 def tip(ui, repo, **opts):
5239 """show the tip revision (DEPRECATED)
5240 """show the tip revision (DEPRECATED)
5240
5241
5241 The tip revision (usually just called the tip) is the changeset
5242 The tip revision (usually just called the tip) is the changeset
5242 most recently added to the repository (and therefore the most
5243 most recently added to the repository (and therefore the most
5243 recently changed head).
5244 recently changed head).
5244
5245
5245 If you have just made a commit, that commit will be the tip. If
5246 If you have just made a commit, that commit will be the tip. If
5246 you have just pulled changes from another repository, the tip of
5247 you have just pulled changes from another repository, the tip of
5247 that repository becomes the current tip. The "tip" tag is special
5248 that repository becomes the current tip. The "tip" tag is special
5248 and cannot be renamed or assigned to a different changeset.
5249 and cannot be renamed or assigned to a different changeset.
5249
5250
5250 This command is deprecated, please use :hg:`heads` instead.
5251 This command is deprecated, please use :hg:`heads` instead.
5251
5252
5252 Returns 0 on success.
5253 Returns 0 on success.
5253 """
5254 """
5254 displayer = cmdutil.show_changeset(ui, repo, opts)
5255 displayer = cmdutil.show_changeset(ui, repo, opts)
5255 displayer.show(repo['tip'])
5256 displayer.show(repo['tip'])
5256 displayer.close()
5257 displayer.close()
5257
5258
5258 @command('unbundle',
5259 @command('unbundle',
5259 [('u', 'update', None,
5260 [('u', 'update', None,
5260 _('update to new branch head if changesets were unbundled'))],
5261 _('update to new branch head if changesets were unbundled'))],
5261 _('[-u] FILE...'))
5262 _('[-u] FILE...'))
5262 def unbundle(ui, repo, fname1, *fnames, **opts):
5263 def unbundle(ui, repo, fname1, *fnames, **opts):
5263 """apply one or more bundle files
5264 """apply one or more bundle files
5264
5265
5265 Apply one or more bundle files generated by :hg:`bundle`.
5266 Apply one or more bundle files generated by :hg:`bundle`.
5266
5267
5267 Returns 0 on success, 1 if an update has unresolved files.
5268 Returns 0 on success, 1 if an update has unresolved files.
5268 """
5269 """
5269 fnames = (fname1,) + fnames
5270 fnames = (fname1,) + fnames
5270
5271
5271 with repo.lock():
5272 with repo.lock():
5272 for fname in fnames:
5273 for fname in fnames:
5273 f = hg.openpath(ui, fname)
5274 f = hg.openpath(ui, fname)
5274 gen = exchange.readbundle(ui, f, fname)
5275 gen = exchange.readbundle(ui, f, fname)
5275 if isinstance(gen, bundle2.unbundle20):
5276 if isinstance(gen, bundle2.unbundle20):
5276 tr = repo.transaction('unbundle')
5277 tr = repo.transaction('unbundle')
5277 try:
5278 try:
5278 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5279 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5279 url='bundle:' + fname)
5280 url='bundle:' + fname)
5280 tr.close()
5281 tr.close()
5281 except error.BundleUnknownFeatureError as exc:
5282 except error.BundleUnknownFeatureError as exc:
5282 raise error.Abort(_('%s: unknown bundle feature, %s')
5283 raise error.Abort(_('%s: unknown bundle feature, %s')
5283 % (fname, exc),
5284 % (fname, exc),
5284 hint=_("see https://mercurial-scm.org/"
5285 hint=_("see https://mercurial-scm.org/"
5285 "wiki/BundleFeature for more "
5286 "wiki/BundleFeature for more "
5286 "information"))
5287 "information"))
5287 finally:
5288 finally:
5288 if tr:
5289 if tr:
5289 tr.release()
5290 tr.release()
5290 changes = [r.get('return', 0)
5291 changes = [r.get('return', 0)
5291 for r in op.records['changegroup']]
5292 for r in op.records['changegroup']]
5292 modheads = changegroup.combineresults(changes)
5293 modheads = changegroup.combineresults(changes)
5293 elif isinstance(gen, streamclone.streamcloneapplier):
5294 elif isinstance(gen, streamclone.streamcloneapplier):
5294 raise error.Abort(
5295 raise error.Abort(
5295 _('packed bundles cannot be applied with '
5296 _('packed bundles cannot be applied with '
5296 '"hg unbundle"'),
5297 '"hg unbundle"'),
5297 hint=_('use "hg debugapplystreamclonebundle"'))
5298 hint=_('use "hg debugapplystreamclonebundle"'))
5298 else:
5299 else:
5299 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
5300 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
5300
5301
5301 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
5302 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
5302
5303
5303 @command('^update|up|checkout|co',
5304 @command('^update|up|checkout|co',
5304 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5305 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5305 ('c', 'check', None, _('require clean working directory')),
5306 ('c', 'check', None, _('require clean working directory')),
5306 ('m', 'merge', None, _('merge uncommitted changes')),
5307 ('m', 'merge', None, _('merge uncommitted changes')),
5307 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5308 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5308 ('r', 'rev', '', _('revision'), _('REV'))
5309 ('r', 'rev', '', _('revision'), _('REV'))
5309 ] + mergetoolopts,
5310 ] + mergetoolopts,
5310 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5311 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5311 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5312 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5312 merge=None, tool=None):
5313 merge=None, tool=None):
5313 """update working directory (or switch revisions)
5314 """update working directory (or switch revisions)
5314
5315
5315 Update the repository's working directory to the specified
5316 Update the repository's working directory to the specified
5316 changeset. If no changeset is specified, update to the tip of the
5317 changeset. If no changeset is specified, update to the tip of the
5317 current named branch and move the active bookmark (see :hg:`help
5318 current named branch and move the active bookmark (see :hg:`help
5318 bookmarks`).
5319 bookmarks`).
5319
5320
5320 Update sets the working directory's parent revision to the specified
5321 Update sets the working directory's parent revision to the specified
5321 changeset (see :hg:`help parents`).
5322 changeset (see :hg:`help parents`).
5322
5323
5323 If the changeset is not a descendant or ancestor of the working
5324 If the changeset is not a descendant or ancestor of the working
5324 directory's parent and there are uncommitted changes, the update is
5325 directory's parent and there are uncommitted changes, the update is
5325 aborted. With the -c/--check option, the working directory is checked
5326 aborted. With the -c/--check option, the working directory is checked
5326 for uncommitted changes; if none are found, the working directory is
5327 for uncommitted changes; if none are found, the working directory is
5327 updated to the specified changeset.
5328 updated to the specified changeset.
5328
5329
5329 .. container:: verbose
5330 .. container:: verbose
5330
5331
5331 The -C/--clean, -c/--check, and -m/--merge options control what
5332 The -C/--clean, -c/--check, and -m/--merge options control what
5332 happens if the working directory contains uncommitted changes.
5333 happens if the working directory contains uncommitted changes.
5333 At most of one of them can be specified.
5334 At most of one of them can be specified.
5334
5335
5335 1. If no option is specified, and if
5336 1. If no option is specified, and if
5336 the requested changeset is an ancestor or descendant of
5337 the requested changeset is an ancestor or descendant of
5337 the working directory's parent, the uncommitted changes
5338 the working directory's parent, the uncommitted changes
5338 are merged into the requested changeset and the merged
5339 are merged into the requested changeset and the merged
5339 result is left uncommitted. If the requested changeset is
5340 result is left uncommitted. If the requested changeset is
5340 not an ancestor or descendant (that is, it is on another
5341 not an ancestor or descendant (that is, it is on another
5341 branch), the update is aborted and the uncommitted changes
5342 branch), the update is aborted and the uncommitted changes
5342 are preserved.
5343 are preserved.
5343
5344
5344 2. With the -m/--merge option, the update is allowed even if the
5345 2. With the -m/--merge option, the update is allowed even if the
5345 requested changeset is not an ancestor or descendant of
5346 requested changeset is not an ancestor or descendant of
5346 the working directory's parent.
5347 the working directory's parent.
5347
5348
5348 3. With the -c/--check option, the update is aborted and the
5349 3. With the -c/--check option, the update is aborted and the
5349 uncommitted changes are preserved.
5350 uncommitted changes are preserved.
5350
5351
5351 4. With the -C/--clean option, uncommitted changes are discarded and
5352 4. With the -C/--clean option, uncommitted changes are discarded and
5352 the working directory is updated to the requested changeset.
5353 the working directory is updated to the requested changeset.
5353
5354
5354 To cancel an uncommitted merge (and lose your changes), use
5355 To cancel an uncommitted merge (and lose your changes), use
5355 :hg:`update --clean .`.
5356 :hg:`update --clean .`.
5356
5357
5357 Use null as the changeset to remove the working directory (like
5358 Use null as the changeset to remove the working directory (like
5358 :hg:`clone -U`).
5359 :hg:`clone -U`).
5359
5360
5360 If you want to revert just one file to an older revision, use
5361 If you want to revert just one file to an older revision, use
5361 :hg:`revert [-r REV] NAME`.
5362 :hg:`revert [-r REV] NAME`.
5362
5363
5363 See :hg:`help dates` for a list of formats valid for -d/--date.
5364 See :hg:`help dates` for a list of formats valid for -d/--date.
5364
5365
5365 Returns 0 on success, 1 if there are unresolved files.
5366 Returns 0 on success, 1 if there are unresolved files.
5366 """
5367 """
5367 if rev and node:
5368 if rev and node:
5368 raise error.Abort(_("please specify just one revision"))
5369 raise error.Abort(_("please specify just one revision"))
5369
5370
5370 if ui.configbool('commands', 'update.requiredest'):
5371 if ui.configbool('commands', 'update.requiredest'):
5371 if not node and not rev and not date:
5372 if not node and not rev and not date:
5372 raise error.Abort(_('you must specify a destination'),
5373 raise error.Abort(_('you must specify a destination'),
5373 hint=_('for example: hg update ".::"'))
5374 hint=_('for example: hg update ".::"'))
5374
5375
5375 if rev is None or rev == '':
5376 if rev is None or rev == '':
5376 rev = node
5377 rev = node
5377
5378
5378 if date and rev is not None:
5379 if date and rev is not None:
5379 raise error.Abort(_("you can't specify a revision and a date"))
5380 raise error.Abort(_("you can't specify a revision and a date"))
5380
5381
5381 if len([x for x in (clean, check, merge) if x]) > 1:
5382 if len([x for x in (clean, check, merge) if x]) > 1:
5382 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5383 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5383 "or -m/merge"))
5384 "or -m/merge"))
5384
5385
5385 updatecheck = None
5386 updatecheck = None
5386 if check:
5387 if check:
5387 updatecheck = 'abort'
5388 updatecheck = 'abort'
5388 elif merge:
5389 elif merge:
5389 updatecheck = 'none'
5390 updatecheck = 'none'
5390
5391
5391 with repo.wlock():
5392 with repo.wlock():
5392 cmdutil.clearunfinished(repo)
5393 cmdutil.clearunfinished(repo)
5393
5394
5394 if date:
5395 if date:
5395 rev = cmdutil.finddate(ui, repo, date)
5396 rev = cmdutil.finddate(ui, repo, date)
5396
5397
5397 # if we defined a bookmark, we have to remember the original name
5398 # if we defined a bookmark, we have to remember the original name
5398 brev = rev
5399 brev = rev
5399 rev = scmutil.revsingle(repo, rev, rev).rev()
5400 rev = scmutil.revsingle(repo, rev, rev).rev()
5400
5401
5401 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
5402 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
5402
5403
5403 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5404 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5404 updatecheck=updatecheck)
5405 updatecheck=updatecheck)
5405
5406
5406 @command('verify', [])
5407 @command('verify', [])
5407 def verify(ui, repo):
5408 def verify(ui, repo):
5408 """verify the integrity of the repository
5409 """verify the integrity of the repository
5409
5410
5410 Verify the integrity of the current repository.
5411 Verify the integrity of the current repository.
5411
5412
5412 This will perform an extensive check of the repository's
5413 This will perform an extensive check of the repository's
5413 integrity, validating the hashes and checksums of each entry in
5414 integrity, validating the hashes and checksums of each entry in
5414 the changelog, manifest, and tracked files, as well as the
5415 the changelog, manifest, and tracked files, as well as the
5415 integrity of their crosslinks and indices.
5416 integrity of their crosslinks and indices.
5416
5417
5417 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5418 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5418 for more information about recovery from corruption of the
5419 for more information about recovery from corruption of the
5419 repository.
5420 repository.
5420
5421
5421 Returns 0 on success, 1 if errors are encountered.
5422 Returns 0 on success, 1 if errors are encountered.
5422 """
5423 """
5423 return hg.verify(repo)
5424 return hg.verify(repo)
5424
5425
5425 @command('version', [] + formatteropts, norepo=True)
5426 @command('version', [] + formatteropts, norepo=True)
5426 def version_(ui, **opts):
5427 def version_(ui, **opts):
5427 """output version and copyright information"""
5428 """output version and copyright information"""
5428 if ui.verbose:
5429 if ui.verbose:
5429 ui.pager('version')
5430 ui.pager('version')
5430 fm = ui.formatter("version", opts)
5431 fm = ui.formatter("version", opts)
5431 fm.startitem()
5432 fm.startitem()
5432 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5433 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5433 util.version())
5434 util.version())
5434 license = _(
5435 license = _(
5435 "(see https://mercurial-scm.org for more information)\n"
5436 "(see https://mercurial-scm.org for more information)\n"
5436 "\nCopyright (C) 2005-2017 Matt Mackall and others\n"
5437 "\nCopyright (C) 2005-2017 Matt Mackall and others\n"
5437 "This is free software; see the source for copying conditions. "
5438 "This is free software; see the source for copying conditions. "
5438 "There is NO\nwarranty; "
5439 "There is NO\nwarranty; "
5439 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5440 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5440 )
5441 )
5441 if not ui.quiet:
5442 if not ui.quiet:
5442 fm.plain(license)
5443 fm.plain(license)
5443
5444
5444 if ui.verbose:
5445 if ui.verbose:
5445 fm.plain(_("\nEnabled extensions:\n\n"))
5446 fm.plain(_("\nEnabled extensions:\n\n"))
5446 # format names and versions into columns
5447 # format names and versions into columns
5447 names = []
5448 names = []
5448 vers = []
5449 vers = []
5449 isinternals = []
5450 isinternals = []
5450 for name, module in extensions.extensions():
5451 for name, module in extensions.extensions():
5451 names.append(name)
5452 names.append(name)
5452 vers.append(extensions.moduleversion(module) or None)
5453 vers.append(extensions.moduleversion(module) or None)
5453 isinternals.append(extensions.ismoduleinternal(module))
5454 isinternals.append(extensions.ismoduleinternal(module))
5454 fn = fm.nested("extensions")
5455 fn = fm.nested("extensions")
5455 if names:
5456 if names:
5456 namefmt = " %%-%ds " % max(len(n) for n in names)
5457 namefmt = " %%-%ds " % max(len(n) for n in names)
5457 places = [_("external"), _("internal")]
5458 places = [_("external"), _("internal")]
5458 for n, v, p in zip(names, vers, isinternals):
5459 for n, v, p in zip(names, vers, isinternals):
5459 fn.startitem()
5460 fn.startitem()
5460 fn.condwrite(ui.verbose, "name", namefmt, n)
5461 fn.condwrite(ui.verbose, "name", namefmt, n)
5461 if ui.verbose:
5462 if ui.verbose:
5462 fn.plain("%s " % places[p])
5463 fn.plain("%s " % places[p])
5463 fn.data(bundled=p)
5464 fn.data(bundled=p)
5464 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5465 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5465 if ui.verbose:
5466 if ui.verbose:
5466 fn.plain("\n")
5467 fn.plain("\n")
5467 fn.end()
5468 fn.end()
5468 fm.end()
5469 fm.end()
5469
5470
5470 def loadcmdtable(ui, name, cmdtable):
5471 def loadcmdtable(ui, name, cmdtable):
5471 """Load command functions from specified cmdtable
5472 """Load command functions from specified cmdtable
5472 """
5473 """
5473 overrides = [cmd for cmd in cmdtable if cmd in table]
5474 overrides = [cmd for cmd in cmdtable if cmd in table]
5474 if overrides:
5475 if overrides:
5475 ui.warn(_("extension '%s' overrides commands: %s\n")
5476 ui.warn(_("extension '%s' overrides commands: %s\n")
5476 % (name, " ".join(overrides)))
5477 % (name, " ".join(overrides)))
5477 table.update(cmdtable)
5478 table.update(cmdtable)
@@ -1,165 +1,169 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. Subversion subrepositories are currently silently ignored.
109 elements. Subversion subrepositories are currently silently ignored.
110
110
111 :files: files does not recurse into subrepos unless -S/--subrepos is
111 :files: files does not recurse into subrepos unless -S/--subrepos is
112 specified. However, if you specify the full path of a file or
112 specified. However, if you specify the full path of a file or
113 directory in a subrepo, it will be displayed even without
113 directory in a subrepo, it will be displayed even without
114 -S/--subrepos being specified. Git and Subversion subrepositories
114 -S/--subrepos being specified. Git and Subversion subrepositories
115 are currently silently ignored.
115 are currently silently ignored.
116
116
117 :forget: forget currently only handles exact file matches in subrepos.
117 :forget: forget currently only handles exact file matches in subrepos.
118 Git and Subversion subrepositories are currently silently ignored.
118 Git and Subversion subrepositories are currently silently ignored.
119
119
120 :incoming: incoming does not recurse in subrepos unless -S/--subrepos
120 :incoming: incoming does not recurse in subrepos unless -S/--subrepos
121 is specified. Git and Subversion subrepositories are currently
121 is specified. Git and Subversion subrepositories are currently
122 silently ignored.
122 silently ignored.
123
123
124 :outgoing: outgoing does not recurse in subrepos unless -S/--subrepos
124 :outgoing: outgoing does not recurse in subrepos unless -S/--subrepos
125 is specified. Git and Subversion subrepositories are currently
125 is specified. Git and Subversion subrepositories are currently
126 silently ignored.
126 silently ignored.
127
127
128 :pull: pull is not recursive since it is not clear what to pull prior
128 :pull: pull is not recursive since it is not clear what to pull prior
129 to running :hg:`update`. Listing and retrieving all
129 to running :hg:`update`. Listing and retrieving all
130 subrepositories changes referenced by the parent repository pulled
130 subrepositories changes referenced by the parent repository pulled
131 changesets is expensive at best, impossible in the Subversion
131 changesets is expensive at best, impossible in the Subversion
132 case.
132 case.
133
133
134 :push: Mercurial will automatically push all subrepositories first
134 :push: Mercurial will automatically push all subrepositories first
135 when the parent repository is being pushed. This ensures new
135 when the parent repository is being pushed. This ensures new
136 subrepository changes are available when referenced by top-level
136 subrepository changes are available when referenced by top-level
137 repositories. Push is a no-op for Subversion subrepositories.
137 repositories. Push is a no-op for Subversion subrepositories.
138
138
139 :serve: serve does not recurse into subrepositories unless
140 -S/--subrepos is specified. Git and Subversion subrepositories
141 are currently silently ignored.
142
139 :status: status does not recurse into subrepositories unless
143 :status: status does not recurse into subrepositories unless
140 -S/--subrepos is specified. Subrepository changes are displayed as
144 -S/--subrepos is specified. Subrepository changes are displayed as
141 regular Mercurial changes on the subrepository
145 regular Mercurial changes on the subrepository
142 elements. Subversion subrepositories are currently silently
146 elements. Subversion subrepositories are currently silently
143 ignored.
147 ignored.
144
148
145 :remove: remove does not recurse into subrepositories unless
149 :remove: remove does not recurse into subrepositories unless
146 -S/--subrepos is specified. However, if you specify a file or
150 -S/--subrepos is specified. However, if you specify a file or
147 directory path in a subrepo, it will be removed even without
151 directory path in a subrepo, it will be removed even without
148 -S/--subrepos. Git and Subversion subrepositories are currently
152 -S/--subrepos. Git and Subversion subrepositories are currently
149 silently ignored.
153 silently ignored.
150
154
151 :update: update restores the subrepos in the state they were
155 :update: update restores the subrepos in the state they were
152 originally committed in target changeset. If the recorded
156 originally committed in target changeset. If the recorded
153 changeset is not available in the current subrepository, Mercurial
157 changeset is not available in the current subrepository, Mercurial
154 will pull it in first before updating. This means that updating
158 will pull it in first before updating. This means that updating
155 can require network access when using subrepositories.
159 can require network access when using subrepositories.
156
160
157 Remapping Subrepositories Sources
161 Remapping Subrepositories Sources
158 =================================
162 =================================
159
163
160 A subrepository source location may change during a project life,
164 A subrepository source location may change during a project life,
161 invalidating references stored in the parent repository history. To
165 invalidating references stored in the parent repository history. To
162 fix this, rewriting rules can be defined in parent repository ``hgrc``
166 fix this, rewriting rules can be defined in parent repository ``hgrc``
163 file or in Mercurial configuration. See the ``[subpaths]`` section in
167 file or in Mercurial configuration. See the ``[subpaths]`` section in
164 hgrc(5) for more details.
168 hgrc(5) for more details.
165
169
@@ -1,157 +1,169 b''
1 # server.py - utility and factory of server
1 # server.py - utility and factory of server
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import os
10 import os
11 import sys
11 import sys
12 import tempfile
12 import tempfile
13
13
14 from .i18n import _
14 from .i18n import _
15
15
16 from . import (
16 from . import (
17 chgserver,
17 chgserver,
18 cmdutil,
18 commandserver,
19 commandserver,
19 error,
20 error,
20 hgweb,
21 hgweb,
21 util,
22 util,
22 )
23 )
23
24
24 def runservice(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
25 def runservice(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
25 runargs=None, appendpid=False):
26 runargs=None, appendpid=False):
26 '''Run a command as a service.'''
27 '''Run a command as a service.'''
27
28
28 def writepid(pid):
29 def writepid(pid):
29 if opts['pid_file']:
30 if opts['pid_file']:
30 if appendpid:
31 if appendpid:
31 mode = 'a'
32 mode = 'a'
32 else:
33 else:
33 mode = 'w'
34 mode = 'w'
34 fp = open(opts['pid_file'], mode)
35 fp = open(opts['pid_file'], mode)
35 fp.write(str(pid) + '\n')
36 fp.write(str(pid) + '\n')
36 fp.close()
37 fp.close()
37
38
38 if opts['daemon'] and not opts['daemon_postexec']:
39 if opts['daemon'] and not opts['daemon_postexec']:
39 # Signal child process startup with file removal
40 # Signal child process startup with file removal
40 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
41 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
41 os.close(lockfd)
42 os.close(lockfd)
42 try:
43 try:
43 if not runargs:
44 if not runargs:
44 runargs = util.hgcmd() + sys.argv[1:]
45 runargs = util.hgcmd() + sys.argv[1:]
45 runargs.append('--daemon-postexec=unlink:%s' % lockpath)
46 runargs.append('--daemon-postexec=unlink:%s' % lockpath)
46 # Don't pass --cwd to the child process, because we've already
47 # Don't pass --cwd to the child process, because we've already
47 # changed directory.
48 # changed directory.
48 for i in xrange(1, len(runargs)):
49 for i in xrange(1, len(runargs)):
49 if runargs[i].startswith('--cwd='):
50 if runargs[i].startswith('--cwd='):
50 del runargs[i]
51 del runargs[i]
51 break
52 break
52 elif runargs[i].startswith('--cwd'):
53 elif runargs[i].startswith('--cwd'):
53 del runargs[i:i + 2]
54 del runargs[i:i + 2]
54 break
55 break
55 def condfn():
56 def condfn():
56 return not os.path.exists(lockpath)
57 return not os.path.exists(lockpath)
57 pid = util.rundetached(runargs, condfn)
58 pid = util.rundetached(runargs, condfn)
58 if pid < 0:
59 if pid < 0:
59 raise error.Abort(_('child process failed to start'))
60 raise error.Abort(_('child process failed to start'))
60 writepid(pid)
61 writepid(pid)
61 finally:
62 finally:
62 util.tryunlink(lockpath)
63 util.tryunlink(lockpath)
63 if parentfn:
64 if parentfn:
64 return parentfn(pid)
65 return parentfn(pid)
65 else:
66 else:
66 return
67 return
67
68
68 if initfn:
69 if initfn:
69 initfn()
70 initfn()
70
71
71 if not opts['daemon']:
72 if not opts['daemon']:
72 writepid(util.getpid())
73 writepid(util.getpid())
73
74
74 if opts['daemon_postexec']:
75 if opts['daemon_postexec']:
75 try:
76 try:
76 os.setsid()
77 os.setsid()
77 except AttributeError:
78 except AttributeError:
78 pass
79 pass
79 for inst in opts['daemon_postexec']:
80 for inst in opts['daemon_postexec']:
80 if inst.startswith('unlink:'):
81 if inst.startswith('unlink:'):
81 lockpath = inst[7:]
82 lockpath = inst[7:]
82 os.unlink(lockpath)
83 os.unlink(lockpath)
83 elif inst.startswith('chdir:'):
84 elif inst.startswith('chdir:'):
84 os.chdir(inst[6:])
85 os.chdir(inst[6:])
85 elif inst != 'none':
86 elif inst != 'none':
86 raise error.Abort(_('invalid value for --daemon-postexec: %s')
87 raise error.Abort(_('invalid value for --daemon-postexec: %s')
87 % inst)
88 % inst)
88 util.hidewindow()
89 util.hidewindow()
89 util.stdout.flush()
90 util.stdout.flush()
90 util.stderr.flush()
91 util.stderr.flush()
91
92
92 nullfd = os.open(os.devnull, os.O_RDWR)
93 nullfd = os.open(os.devnull, os.O_RDWR)
93 logfilefd = nullfd
94 logfilefd = nullfd
94 if logfile:
95 if logfile:
95 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
96 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
96 os.dup2(nullfd, 0)
97 os.dup2(nullfd, 0)
97 os.dup2(logfilefd, 1)
98 os.dup2(logfilefd, 1)
98 os.dup2(logfilefd, 2)
99 os.dup2(logfilefd, 2)
99 if nullfd not in (0, 1, 2):
100 if nullfd not in (0, 1, 2):
100 os.close(nullfd)
101 os.close(nullfd)
101 if logfile and logfilefd not in (0, 1, 2):
102 if logfile and logfilefd not in (0, 1, 2):
102 os.close(logfilefd)
103 os.close(logfilefd)
103
104
104 if runfn:
105 if runfn:
105 return runfn()
106 return runfn()
106
107
107 _cmdservicemap = {
108 _cmdservicemap = {
108 'chgunix': chgserver.chgunixservice,
109 'chgunix': chgserver.chgunixservice,
109 'pipe': commandserver.pipeservice,
110 'pipe': commandserver.pipeservice,
110 'unix': commandserver.unixforkingservice,
111 'unix': commandserver.unixforkingservice,
111 }
112 }
112
113
113 def _createcmdservice(ui, repo, opts):
114 def _createcmdservice(ui, repo, opts):
114 mode = opts['cmdserver']
115 mode = opts['cmdserver']
115 try:
116 try:
116 return _cmdservicemap[mode](ui, repo, opts)
117 return _cmdservicemap[mode](ui, repo, opts)
117 except KeyError:
118 except KeyError:
118 raise error.Abort(_('unknown mode %s') % mode)
119 raise error.Abort(_('unknown mode %s') % mode)
119
120
120 def _createhgwebservice(ui, repo, opts):
121 def _createhgwebservice(ui, repo, opts):
121 # this way we can check if something was given in the command-line
122 # this way we can check if something was given in the command-line
122 if opts.get('port'):
123 if opts.get('port'):
123 opts['port'] = util.getport(opts.get('port'))
124 opts['port'] = util.getport(opts.get('port'))
124
125
125 alluis = set([ui])
126 alluis = set([ui])
126 if repo:
127 if repo:
127 baseui = repo.baseui
128 baseui = repo.baseui
128 alluis.update([repo.baseui, repo.ui])
129 alluis.update([repo.baseui, repo.ui])
129 else:
130 else:
130 baseui = ui
131 baseui = ui
131 webconf = opts.get('web_conf') or opts.get('webdir_conf')
132 webconf = opts.get('web_conf') or opts.get('webdir_conf')
132 if webconf:
133 if webconf:
134 if opts.get('subrepos'):
135 raise error.Abort(_('--web-conf cannot be used with --subrepos'))
136
133 # load server settings (e.g. web.port) to "copied" ui, which allows
137 # load server settings (e.g. web.port) to "copied" ui, which allows
134 # hgwebdir to reload webconf cleanly
138 # hgwebdir to reload webconf cleanly
135 servui = ui.copy()
139 servui = ui.copy()
136 servui.readconfig(webconf, sections=['web'])
140 servui.readconfig(webconf, sections=['web'])
137 alluis.add(servui)
141 alluis.add(servui)
142 elif opts.get('subrepos'):
143 servui = ui
144
145 # If repo is None, hgweb.createapp() already raises a proper abort
146 # message as long as webconf is None.
147 if repo:
148 webconf = dict()
149 cmdutil.addwebdirpath(repo, "", webconf)
138 else:
150 else:
139 servui = ui
151 servui = ui
140
152
141 optlist = ("name templates style address port prefix ipv6"
153 optlist = ("name templates style address port prefix ipv6"
142 " accesslog errorlog certificate encoding")
154 " accesslog errorlog certificate encoding")
143 for o in optlist.split():
155 for o in optlist.split():
144 val = opts.get(o, '')
156 val = opts.get(o, '')
145 if val in (None, ''): # should check against default options instead
157 if val in (None, ''): # should check against default options instead
146 continue
158 continue
147 for u in alluis:
159 for u in alluis:
148 u.setconfig("web", o, val, 'serve')
160 u.setconfig("web", o, val, 'serve')
149
161
150 app = hgweb.createapp(baseui, repo, webconf)
162 app = hgweb.createapp(baseui, repo, webconf)
151 return hgweb.httpservice(servui, app, opts)
163 return hgweb.httpservice(servui, app, opts)
152
164
153 def createservice(ui, repo, opts):
165 def createservice(ui, repo, opts):
154 if opts["cmdserver"]:
166 if opts["cmdserver"]:
155 return _createcmdservice(ui, repo, opts)
167 return _createcmdservice(ui, repo, opts)
156 else:
168 else:
157 return _createhgwebservice(ui, repo, opts)
169 return _createhgwebservice(ui, repo, opts)
@@ -1,1974 +1,1987 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 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import copy
10 import copy
11 import errno
11 import errno
12 import hashlib
12 import hashlib
13 import os
13 import os
14 import posixpath
14 import posixpath
15 import re
15 import re
16 import stat
16 import stat
17 import subprocess
17 import subprocess
18 import sys
18 import sys
19 import tarfile
19 import tarfile
20 import xml.dom.minidom
20 import xml.dom.minidom
21
21
22
22
23 from .i18n import _
23 from .i18n import _
24 from . import (
24 from . import (
25 cmdutil,
25 cmdutil,
26 config,
26 config,
27 encoding,
27 encoding,
28 error,
28 error,
29 exchange,
29 exchange,
30 filemerge,
30 filemerge,
31 match as matchmod,
31 match as matchmod,
32 node,
32 node,
33 pathutil,
33 pathutil,
34 phases,
34 phases,
35 pycompat,
35 pycompat,
36 scmutil,
36 scmutil,
37 util,
37 util,
38 vfs as vfsmod,
38 vfs as vfsmod,
39 )
39 )
40
40
41 hg = None
41 hg = None
42 propertycache = util.propertycache
42 propertycache = util.propertycache
43
43
44 nullstate = ('', '', 'empty')
44 nullstate = ('', '', 'empty')
45
45
46 def _expandedabspath(path):
46 def _expandedabspath(path):
47 '''
47 '''
48 get a path or url and if it is a path expand it and return an absolute path
48 get a path or url and if it is a path expand it and return an absolute path
49 '''
49 '''
50 expandedpath = util.urllocalpath(util.expandpath(path))
50 expandedpath = util.urllocalpath(util.expandpath(path))
51 u = util.url(expandedpath)
51 u = util.url(expandedpath)
52 if not u.scheme:
52 if not u.scheme:
53 path = util.normpath(os.path.abspath(u.path))
53 path = util.normpath(os.path.abspath(u.path))
54 return path
54 return path
55
55
56 def _getstorehashcachename(remotepath):
56 def _getstorehashcachename(remotepath):
57 '''get a unique filename for the store hash cache of a remote repository'''
57 '''get a unique filename for the store hash cache of a remote repository'''
58 return hashlib.sha1(_expandedabspath(remotepath)).hexdigest()[0:12]
58 return hashlib.sha1(_expandedabspath(remotepath)).hexdigest()[0:12]
59
59
60 class SubrepoAbort(error.Abort):
60 class SubrepoAbort(error.Abort):
61 """Exception class used to avoid handling a subrepo error more than once"""
61 """Exception class used to avoid handling a subrepo error more than once"""
62 def __init__(self, *args, **kw):
62 def __init__(self, *args, **kw):
63 self.subrepo = kw.pop('subrepo', None)
63 self.subrepo = kw.pop('subrepo', None)
64 self.cause = kw.pop('cause', None)
64 self.cause = kw.pop('cause', None)
65 error.Abort.__init__(self, *args, **kw)
65 error.Abort.__init__(self, *args, **kw)
66
66
67 def annotatesubrepoerror(func):
67 def annotatesubrepoerror(func):
68 def decoratedmethod(self, *args, **kargs):
68 def decoratedmethod(self, *args, **kargs):
69 try:
69 try:
70 res = func(self, *args, **kargs)
70 res = func(self, *args, **kargs)
71 except SubrepoAbort as ex:
71 except SubrepoAbort as ex:
72 # This exception has already been handled
72 # This exception has already been handled
73 raise ex
73 raise ex
74 except error.Abort as ex:
74 except error.Abort as ex:
75 subrepo = subrelpath(self)
75 subrepo = subrelpath(self)
76 errormsg = str(ex) + ' ' + _('(in subrepo %s)') % subrepo
76 errormsg = str(ex) + ' ' + _('(in subrepo %s)') % subrepo
77 # avoid handling this exception by raising a SubrepoAbort exception
77 # avoid handling this exception by raising a SubrepoAbort exception
78 raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo,
78 raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo,
79 cause=sys.exc_info())
79 cause=sys.exc_info())
80 return res
80 return res
81 return decoratedmethod
81 return decoratedmethod
82
82
83 def state(ctx, ui):
83 def state(ctx, ui):
84 """return a state dict, mapping subrepo paths configured in .hgsub
84 """return a state dict, mapping subrepo paths configured in .hgsub
85 to tuple: (source from .hgsub, revision from .hgsubstate, kind
85 to tuple: (source from .hgsub, revision from .hgsubstate, kind
86 (key in types dict))
86 (key in types dict))
87 """
87 """
88 p = config.config()
88 p = config.config()
89 repo = ctx.repo()
89 repo = ctx.repo()
90 def read(f, sections=None, remap=None):
90 def read(f, sections=None, remap=None):
91 if f in ctx:
91 if f in ctx:
92 try:
92 try:
93 data = ctx[f].data()
93 data = ctx[f].data()
94 except IOError as err:
94 except IOError as err:
95 if err.errno != errno.ENOENT:
95 if err.errno != errno.ENOENT:
96 raise
96 raise
97 # handle missing subrepo spec files as removed
97 # handle missing subrepo spec files as removed
98 ui.warn(_("warning: subrepo spec file \'%s\' not found\n") %
98 ui.warn(_("warning: subrepo spec file \'%s\' not found\n") %
99 repo.pathto(f))
99 repo.pathto(f))
100 return
100 return
101 p.parse(f, data, sections, remap, read)
101 p.parse(f, data, sections, remap, read)
102 else:
102 else:
103 raise error.Abort(_("subrepo spec file \'%s\' not found") %
103 raise error.Abort(_("subrepo spec file \'%s\' not found") %
104 repo.pathto(f))
104 repo.pathto(f))
105 if '.hgsub' in ctx:
105 if '.hgsub' in ctx:
106 read('.hgsub')
106 read('.hgsub')
107
107
108 for path, src in ui.configitems('subpaths'):
108 for path, src in ui.configitems('subpaths'):
109 p.set('subpaths', path, src, ui.configsource('subpaths', path))
109 p.set('subpaths', path, src, ui.configsource('subpaths', path))
110
110
111 rev = {}
111 rev = {}
112 if '.hgsubstate' in ctx:
112 if '.hgsubstate' in ctx:
113 try:
113 try:
114 for i, l in enumerate(ctx['.hgsubstate'].data().splitlines()):
114 for i, l in enumerate(ctx['.hgsubstate'].data().splitlines()):
115 l = l.lstrip()
115 l = l.lstrip()
116 if not l:
116 if not l:
117 continue
117 continue
118 try:
118 try:
119 revision, path = l.split(" ", 1)
119 revision, path = l.split(" ", 1)
120 except ValueError:
120 except ValueError:
121 raise error.Abort(_("invalid subrepository revision "
121 raise error.Abort(_("invalid subrepository revision "
122 "specifier in \'%s\' line %d")
122 "specifier in \'%s\' line %d")
123 % (repo.pathto('.hgsubstate'), (i + 1)))
123 % (repo.pathto('.hgsubstate'), (i + 1)))
124 rev[path] = revision
124 rev[path] = revision
125 except IOError as err:
125 except IOError as err:
126 if err.errno != errno.ENOENT:
126 if err.errno != errno.ENOENT:
127 raise
127 raise
128
128
129 def remap(src):
129 def remap(src):
130 for pattern, repl in p.items('subpaths'):
130 for pattern, repl in p.items('subpaths'):
131 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
131 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
132 # does a string decode.
132 # does a string decode.
133 repl = util.escapestr(repl)
133 repl = util.escapestr(repl)
134 # However, we still want to allow back references to go
134 # However, we still want to allow back references to go
135 # through unharmed, so we turn r'\\1' into r'\1'. Again,
135 # through unharmed, so we turn r'\\1' into r'\1'. Again,
136 # extra escapes are needed because re.sub string decodes.
136 # extra escapes are needed because re.sub string decodes.
137 repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
137 repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
138 try:
138 try:
139 src = re.sub(pattern, repl, src, 1)
139 src = re.sub(pattern, repl, src, 1)
140 except re.error as e:
140 except re.error as e:
141 raise error.Abort(_("bad subrepository pattern in %s: %s")
141 raise error.Abort(_("bad subrepository pattern in %s: %s")
142 % (p.source('subpaths', pattern), e))
142 % (p.source('subpaths', pattern), e))
143 return src
143 return src
144
144
145 state = {}
145 state = {}
146 for path, src in p[''].items():
146 for path, src in p[''].items():
147 kind = 'hg'
147 kind = 'hg'
148 if src.startswith('['):
148 if src.startswith('['):
149 if ']' not in src:
149 if ']' not in src:
150 raise error.Abort(_('missing ] in subrepo source'))
150 raise error.Abort(_('missing ] in subrepo source'))
151 kind, src = src.split(']', 1)
151 kind, src = src.split(']', 1)
152 kind = kind[1:]
152 kind = kind[1:]
153 src = src.lstrip() # strip any extra whitespace after ']'
153 src = src.lstrip() # strip any extra whitespace after ']'
154
154
155 if not util.url(src).isabs():
155 if not util.url(src).isabs():
156 parent = _abssource(repo, abort=False)
156 parent = _abssource(repo, abort=False)
157 if parent:
157 if parent:
158 parent = util.url(parent)
158 parent = util.url(parent)
159 parent.path = posixpath.join(parent.path or '', src)
159 parent.path = posixpath.join(parent.path or '', src)
160 parent.path = posixpath.normpath(parent.path)
160 parent.path = posixpath.normpath(parent.path)
161 joined = str(parent)
161 joined = str(parent)
162 # Remap the full joined path and use it if it changes,
162 # Remap the full joined path and use it if it changes,
163 # else remap the original source.
163 # else remap the original source.
164 remapped = remap(joined)
164 remapped = remap(joined)
165 if remapped == joined:
165 if remapped == joined:
166 src = remap(src)
166 src = remap(src)
167 else:
167 else:
168 src = remapped
168 src = remapped
169
169
170 src = remap(src)
170 src = remap(src)
171 state[util.pconvert(path)] = (src.strip(), rev.get(path, ''), kind)
171 state[util.pconvert(path)] = (src.strip(), rev.get(path, ''), kind)
172
172
173 return state
173 return state
174
174
175 def writestate(repo, state):
175 def writestate(repo, state):
176 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
176 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
177 lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)
177 lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)
178 if state[s][1] != nullstate[1]]
178 if state[s][1] != nullstate[1]]
179 repo.wwrite('.hgsubstate', ''.join(lines), '')
179 repo.wwrite('.hgsubstate', ''.join(lines), '')
180
180
181 def submerge(repo, wctx, mctx, actx, overwrite, labels=None):
181 def submerge(repo, wctx, mctx, actx, overwrite, labels=None):
182 """delegated from merge.applyupdates: merging of .hgsubstate file
182 """delegated from merge.applyupdates: merging of .hgsubstate file
183 in working context, merging context and ancestor context"""
183 in working context, merging context and ancestor context"""
184 if mctx == actx: # backwards?
184 if mctx == actx: # backwards?
185 actx = wctx.p1()
185 actx = wctx.p1()
186 s1 = wctx.substate
186 s1 = wctx.substate
187 s2 = mctx.substate
187 s2 = mctx.substate
188 sa = actx.substate
188 sa = actx.substate
189 sm = {}
189 sm = {}
190
190
191 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
191 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
192
192
193 def debug(s, msg, r=""):
193 def debug(s, msg, r=""):
194 if r:
194 if r:
195 r = "%s:%s:%s" % r
195 r = "%s:%s:%s" % r
196 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
196 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
197
197
198 promptssrc = filemerge.partextras(labels)
198 promptssrc = filemerge.partextras(labels)
199 for s, l in sorted(s1.iteritems()):
199 for s, l in sorted(s1.iteritems()):
200 prompts = None
200 prompts = None
201 a = sa.get(s, nullstate)
201 a = sa.get(s, nullstate)
202 ld = l # local state with possible dirty flag for compares
202 ld = l # local state with possible dirty flag for compares
203 if wctx.sub(s).dirty():
203 if wctx.sub(s).dirty():
204 ld = (l[0], l[1] + "+")
204 ld = (l[0], l[1] + "+")
205 if wctx == actx: # overwrite
205 if wctx == actx: # overwrite
206 a = ld
206 a = ld
207
207
208 prompts = promptssrc.copy()
208 prompts = promptssrc.copy()
209 prompts['s'] = s
209 prompts['s'] = s
210 if s in s2:
210 if s in s2:
211 r = s2[s]
211 r = s2[s]
212 if ld == r or r == a: # no change or local is newer
212 if ld == r or r == a: # no change or local is newer
213 sm[s] = l
213 sm[s] = l
214 continue
214 continue
215 elif ld == a: # other side changed
215 elif ld == a: # other side changed
216 debug(s, "other changed, get", r)
216 debug(s, "other changed, get", r)
217 wctx.sub(s).get(r, overwrite)
217 wctx.sub(s).get(r, overwrite)
218 sm[s] = r
218 sm[s] = r
219 elif ld[0] != r[0]: # sources differ
219 elif ld[0] != r[0]: # sources differ
220 prompts['lo'] = l[0]
220 prompts['lo'] = l[0]
221 prompts['ro'] = r[0]
221 prompts['ro'] = r[0]
222 if repo.ui.promptchoice(
222 if repo.ui.promptchoice(
223 _(' subrepository sources for %(s)s differ\n'
223 _(' subrepository sources for %(s)s differ\n'
224 'use (l)ocal%(l)s source (%(lo)s)'
224 'use (l)ocal%(l)s source (%(lo)s)'
225 ' or (r)emote%(o)s source (%(ro)s)?'
225 ' or (r)emote%(o)s source (%(ro)s)?'
226 '$$ &Local $$ &Remote') % prompts, 0):
226 '$$ &Local $$ &Remote') % prompts, 0):
227 debug(s, "prompt changed, get", r)
227 debug(s, "prompt changed, get", r)
228 wctx.sub(s).get(r, overwrite)
228 wctx.sub(s).get(r, overwrite)
229 sm[s] = r
229 sm[s] = r
230 elif ld[1] == a[1]: # local side is unchanged
230 elif ld[1] == a[1]: # local side is unchanged
231 debug(s, "other side changed, get", r)
231 debug(s, "other side changed, get", r)
232 wctx.sub(s).get(r, overwrite)
232 wctx.sub(s).get(r, overwrite)
233 sm[s] = r
233 sm[s] = r
234 else:
234 else:
235 debug(s, "both sides changed")
235 debug(s, "both sides changed")
236 srepo = wctx.sub(s)
236 srepo = wctx.sub(s)
237 prompts['sl'] = srepo.shortid(l[1])
237 prompts['sl'] = srepo.shortid(l[1])
238 prompts['sr'] = srepo.shortid(r[1])
238 prompts['sr'] = srepo.shortid(r[1])
239 option = repo.ui.promptchoice(
239 option = repo.ui.promptchoice(
240 _(' subrepository %(s)s diverged (local revision: %(sl)s, '
240 _(' subrepository %(s)s diverged (local revision: %(sl)s, '
241 'remote revision: %(sr)s)\n'
241 'remote revision: %(sr)s)\n'
242 '(M)erge, keep (l)ocal%(l)s or keep (r)emote%(o)s?'
242 '(M)erge, keep (l)ocal%(l)s or keep (r)emote%(o)s?'
243 '$$ &Merge $$ &Local $$ &Remote')
243 '$$ &Merge $$ &Local $$ &Remote')
244 % prompts, 0)
244 % prompts, 0)
245 if option == 0:
245 if option == 0:
246 wctx.sub(s).merge(r)
246 wctx.sub(s).merge(r)
247 sm[s] = l
247 sm[s] = l
248 debug(s, "merge with", r)
248 debug(s, "merge with", r)
249 elif option == 1:
249 elif option == 1:
250 sm[s] = l
250 sm[s] = l
251 debug(s, "keep local subrepo revision", l)
251 debug(s, "keep local subrepo revision", l)
252 else:
252 else:
253 wctx.sub(s).get(r, overwrite)
253 wctx.sub(s).get(r, overwrite)
254 sm[s] = r
254 sm[s] = r
255 debug(s, "get remote subrepo revision", r)
255 debug(s, "get remote subrepo revision", r)
256 elif ld == a: # remote removed, local unchanged
256 elif ld == a: # remote removed, local unchanged
257 debug(s, "remote removed, remove")
257 debug(s, "remote removed, remove")
258 wctx.sub(s).remove()
258 wctx.sub(s).remove()
259 elif a == nullstate: # not present in remote or ancestor
259 elif a == nullstate: # not present in remote or ancestor
260 debug(s, "local added, keep")
260 debug(s, "local added, keep")
261 sm[s] = l
261 sm[s] = l
262 continue
262 continue
263 else:
263 else:
264 if repo.ui.promptchoice(
264 if repo.ui.promptchoice(
265 _(' local%(l)s changed subrepository %(s)s'
265 _(' local%(l)s changed subrepository %(s)s'
266 ' which remote%(o)s removed\n'
266 ' which remote%(o)s removed\n'
267 'use (c)hanged version or (d)elete?'
267 'use (c)hanged version or (d)elete?'
268 '$$ &Changed $$ &Delete') % prompts, 0):
268 '$$ &Changed $$ &Delete') % prompts, 0):
269 debug(s, "prompt remove")
269 debug(s, "prompt remove")
270 wctx.sub(s).remove()
270 wctx.sub(s).remove()
271
271
272 for s, r in sorted(s2.items()):
272 for s, r in sorted(s2.items()):
273 prompts = None
273 prompts = None
274 if s in s1:
274 if s in s1:
275 continue
275 continue
276 elif s not in sa:
276 elif s not in sa:
277 debug(s, "remote added, get", r)
277 debug(s, "remote added, get", r)
278 mctx.sub(s).get(r)
278 mctx.sub(s).get(r)
279 sm[s] = r
279 sm[s] = r
280 elif r != sa[s]:
280 elif r != sa[s]:
281 prompts = promptssrc.copy()
281 prompts = promptssrc.copy()
282 prompts['s'] = s
282 prompts['s'] = s
283 if repo.ui.promptchoice(
283 if repo.ui.promptchoice(
284 _(' remote%(o)s changed subrepository %(s)s'
284 _(' remote%(o)s changed subrepository %(s)s'
285 ' which local%(l)s removed\n'
285 ' which local%(l)s removed\n'
286 'use (c)hanged version or (d)elete?'
286 'use (c)hanged version or (d)elete?'
287 '$$ &Changed $$ &Delete') % prompts, 0) == 0:
287 '$$ &Changed $$ &Delete') % prompts, 0) == 0:
288 debug(s, "prompt recreate", r)
288 debug(s, "prompt recreate", r)
289 mctx.sub(s).get(r)
289 mctx.sub(s).get(r)
290 sm[s] = r
290 sm[s] = r
291
291
292 # record merged .hgsubstate
292 # record merged .hgsubstate
293 writestate(repo, sm)
293 writestate(repo, sm)
294 return sm
294 return sm
295
295
296 def _updateprompt(ui, sub, dirty, local, remote):
296 def _updateprompt(ui, sub, dirty, local, remote):
297 if dirty:
297 if dirty:
298 msg = (_(' subrepository sources for %s differ\n'
298 msg = (_(' subrepository sources for %s differ\n'
299 'use (l)ocal source (%s) or (r)emote source (%s)?'
299 'use (l)ocal source (%s) or (r)emote source (%s)?'
300 '$$ &Local $$ &Remote')
300 '$$ &Local $$ &Remote')
301 % (subrelpath(sub), local, remote))
301 % (subrelpath(sub), local, remote))
302 else:
302 else:
303 msg = (_(' subrepository sources for %s differ (in checked out '
303 msg = (_(' subrepository sources for %s differ (in checked out '
304 'version)\n'
304 'version)\n'
305 'use (l)ocal source (%s) or (r)emote source (%s)?'
305 'use (l)ocal source (%s) or (r)emote source (%s)?'
306 '$$ &Local $$ &Remote')
306 '$$ &Local $$ &Remote')
307 % (subrelpath(sub), local, remote))
307 % (subrelpath(sub), local, remote))
308 return ui.promptchoice(msg, 0)
308 return ui.promptchoice(msg, 0)
309
309
310 def reporelpath(repo):
310 def reporelpath(repo):
311 """return path to this (sub)repo as seen from outermost repo"""
311 """return path to this (sub)repo as seen from outermost repo"""
312 parent = repo
312 parent = repo
313 while util.safehasattr(parent, '_subparent'):
313 while util.safehasattr(parent, '_subparent'):
314 parent = parent._subparent
314 parent = parent._subparent
315 return repo.root[len(pathutil.normasprefix(parent.root)):]
315 return repo.root[len(pathutil.normasprefix(parent.root)):]
316
316
317 def subrelpath(sub):
317 def subrelpath(sub):
318 """return path to this subrepo as seen from outermost repo"""
318 """return path to this subrepo as seen from outermost repo"""
319 return sub._relpath
319 return sub._relpath
320
320
321 def _abssource(repo, push=False, abort=True):
321 def _abssource(repo, push=False, abort=True):
322 """return pull/push path of repo - either based on parent repo .hgsub info
322 """return pull/push path of repo - either based on parent repo .hgsub info
323 or on the top repo config. Abort or return None if no source found."""
323 or on the top repo config. Abort or return None if no source found."""
324 if util.safehasattr(repo, '_subparent'):
324 if util.safehasattr(repo, '_subparent'):
325 source = util.url(repo._subsource)
325 source = util.url(repo._subsource)
326 if source.isabs():
326 if source.isabs():
327 return str(source)
327 return str(source)
328 source.path = posixpath.normpath(source.path)
328 source.path = posixpath.normpath(source.path)
329 parent = _abssource(repo._subparent, push, abort=False)
329 parent = _abssource(repo._subparent, push, abort=False)
330 if parent:
330 if parent:
331 parent = util.url(util.pconvert(parent))
331 parent = util.url(util.pconvert(parent))
332 parent.path = posixpath.join(parent.path or '', source.path)
332 parent.path = posixpath.join(parent.path or '', source.path)
333 parent.path = posixpath.normpath(parent.path)
333 parent.path = posixpath.normpath(parent.path)
334 return str(parent)
334 return str(parent)
335 else: # recursion reached top repo
335 else: # recursion reached top repo
336 if util.safehasattr(repo, '_subtoppath'):
336 if util.safehasattr(repo, '_subtoppath'):
337 return repo._subtoppath
337 return repo._subtoppath
338 if push and repo.ui.config('paths', 'default-push'):
338 if push and repo.ui.config('paths', 'default-push'):
339 return repo.ui.config('paths', 'default-push')
339 return repo.ui.config('paths', 'default-push')
340 if repo.ui.config('paths', 'default'):
340 if repo.ui.config('paths', 'default'):
341 return repo.ui.config('paths', 'default')
341 return repo.ui.config('paths', 'default')
342 if repo.shared():
342 if repo.shared():
343 # chop off the .hg component to get the default path form
343 # chop off the .hg component to get the default path form
344 return os.path.dirname(repo.sharedpath)
344 return os.path.dirname(repo.sharedpath)
345 if abort:
345 if abort:
346 raise error.Abort(_("default path for subrepository not found"))
346 raise error.Abort(_("default path for subrepository not found"))
347
347
348 def _sanitize(ui, vfs, ignore):
348 def _sanitize(ui, vfs, ignore):
349 for dirname, dirs, names in vfs.walk():
349 for dirname, dirs, names in vfs.walk():
350 for i, d in enumerate(dirs):
350 for i, d in enumerate(dirs):
351 if d.lower() == ignore:
351 if d.lower() == ignore:
352 del dirs[i]
352 del dirs[i]
353 break
353 break
354 if vfs.basename(dirname).lower() != '.hg':
354 if vfs.basename(dirname).lower() != '.hg':
355 continue
355 continue
356 for f in names:
356 for f in names:
357 if f.lower() == 'hgrc':
357 if f.lower() == 'hgrc':
358 ui.warn(_("warning: removing potentially hostile 'hgrc' "
358 ui.warn(_("warning: removing potentially hostile 'hgrc' "
359 "in '%s'\n") % vfs.join(dirname))
359 "in '%s'\n") % vfs.join(dirname))
360 vfs.unlink(vfs.reljoin(dirname, f))
360 vfs.unlink(vfs.reljoin(dirname, f))
361
361
362 def subrepo(ctx, path, allowwdir=False, allowcreate=True):
362 def subrepo(ctx, path, allowwdir=False, allowcreate=True):
363 """return instance of the right subrepo class for subrepo in path"""
363 """return instance of the right subrepo class for subrepo in path"""
364 # subrepo inherently violates our import layering rules
364 # subrepo inherently violates our import layering rules
365 # because it wants to make repo objects from deep inside the stack
365 # because it wants to make repo objects from deep inside the stack
366 # so we manually delay the circular imports to not break
366 # so we manually delay the circular imports to not break
367 # scripts that don't use our demand-loading
367 # scripts that don't use our demand-loading
368 global hg
368 global hg
369 from . import hg as h
369 from . import hg as h
370 hg = h
370 hg = h
371
371
372 pathutil.pathauditor(ctx.repo().root)(path)
372 pathutil.pathauditor(ctx.repo().root)(path)
373 state = ctx.substate[path]
373 state = ctx.substate[path]
374 if state[2] not in types:
374 if state[2] not in types:
375 raise error.Abort(_('unknown subrepo type %s') % state[2])
375 raise error.Abort(_('unknown subrepo type %s') % state[2])
376 if allowwdir:
376 if allowwdir:
377 state = (state[0], ctx.subrev(path), state[2])
377 state = (state[0], ctx.subrev(path), state[2])
378 return types[state[2]](ctx, path, state[:2], allowcreate)
378 return types[state[2]](ctx, path, state[:2], allowcreate)
379
379
380 def nullsubrepo(ctx, path, pctx):
380 def nullsubrepo(ctx, path, pctx):
381 """return an empty subrepo in pctx for the extant subrepo in ctx"""
381 """return an empty subrepo in pctx for the extant subrepo in ctx"""
382 # subrepo inherently violates our import layering rules
382 # subrepo inherently violates our import layering rules
383 # because it wants to make repo objects from deep inside the stack
383 # because it wants to make repo objects from deep inside the stack
384 # so we manually delay the circular imports to not break
384 # so we manually delay the circular imports to not break
385 # scripts that don't use our demand-loading
385 # scripts that don't use our demand-loading
386 global hg
386 global hg
387 from . import hg as h
387 from . import hg as h
388 hg = h
388 hg = h
389
389
390 pathutil.pathauditor(ctx.repo().root)(path)
390 pathutil.pathauditor(ctx.repo().root)(path)
391 state = ctx.substate[path]
391 state = ctx.substate[path]
392 if state[2] not in types:
392 if state[2] not in types:
393 raise error.Abort(_('unknown subrepo type %s') % state[2])
393 raise error.Abort(_('unknown subrepo type %s') % state[2])
394 subrev = ''
394 subrev = ''
395 if state[2] == 'hg':
395 if state[2] == 'hg':
396 subrev = "0" * 40
396 subrev = "0" * 40
397 return types[state[2]](pctx, path, (state[0], subrev), True)
397 return types[state[2]](pctx, path, (state[0], subrev), True)
398
398
399 def newcommitphase(ui, ctx):
399 def newcommitphase(ui, ctx):
400 commitphase = phases.newcommitphase(ui)
400 commitphase = phases.newcommitphase(ui)
401 substate = getattr(ctx, "substate", None)
401 substate = getattr(ctx, "substate", None)
402 if not substate:
402 if not substate:
403 return commitphase
403 return commitphase
404 check = ui.config('phases', 'checksubrepos', 'follow')
404 check = ui.config('phases', 'checksubrepos', 'follow')
405 if check not in ('ignore', 'follow', 'abort'):
405 if check not in ('ignore', 'follow', 'abort'):
406 raise error.Abort(_('invalid phases.checksubrepos configuration: %s')
406 raise error.Abort(_('invalid phases.checksubrepos configuration: %s')
407 % (check))
407 % (check))
408 if check == 'ignore':
408 if check == 'ignore':
409 return commitphase
409 return commitphase
410 maxphase = phases.public
410 maxphase = phases.public
411 maxsub = None
411 maxsub = None
412 for s in sorted(substate):
412 for s in sorted(substate):
413 sub = ctx.sub(s)
413 sub = ctx.sub(s)
414 subphase = sub.phase(substate[s][1])
414 subphase = sub.phase(substate[s][1])
415 if maxphase < subphase:
415 if maxphase < subphase:
416 maxphase = subphase
416 maxphase = subphase
417 maxsub = s
417 maxsub = s
418 if commitphase < maxphase:
418 if commitphase < maxphase:
419 if check == 'abort':
419 if check == 'abort':
420 raise error.Abort(_("can't commit in %s phase"
420 raise error.Abort(_("can't commit in %s phase"
421 " conflicting %s from subrepository %s") %
421 " conflicting %s from subrepository %s") %
422 (phases.phasenames[commitphase],
422 (phases.phasenames[commitphase],
423 phases.phasenames[maxphase], maxsub))
423 phases.phasenames[maxphase], maxsub))
424 ui.warn(_("warning: changes are committed in"
424 ui.warn(_("warning: changes are committed in"
425 " %s phase from subrepository %s\n") %
425 " %s phase from subrepository %s\n") %
426 (phases.phasenames[maxphase], maxsub))
426 (phases.phasenames[maxphase], maxsub))
427 return maxphase
427 return maxphase
428 return commitphase
428 return commitphase
429
429
430 # subrepo classes need to implement the following abstract class:
430 # subrepo classes need to implement the following abstract class:
431
431
432 class abstractsubrepo(object):
432 class abstractsubrepo(object):
433
433
434 def __init__(self, ctx, path):
434 def __init__(self, ctx, path):
435 """Initialize abstractsubrepo part
435 """Initialize abstractsubrepo part
436
436
437 ``ctx`` is the context referring this subrepository in the
437 ``ctx`` is the context referring this subrepository in the
438 parent repository.
438 parent repository.
439
439
440 ``path`` is the path to this subrepository as seen from
440 ``path`` is the path to this subrepository as seen from
441 innermost repository.
441 innermost repository.
442 """
442 """
443 self.ui = ctx.repo().ui
443 self.ui = ctx.repo().ui
444 self._ctx = ctx
444 self._ctx = ctx
445 self._path = path
445 self._path = path
446
446
447 def addwebdirpath(self, serverpath, webconf):
448 """Add the hgwebdir entries for this subrepo, and any of its subrepos.
449
450 ``serverpath`` is the path component of the URL for this repo.
451
452 ``webconf`` is the dictionary of hgwebdir entries.
453 """
454 pass
455
447 def storeclean(self, path):
456 def storeclean(self, path):
448 """
457 """
449 returns true if the repository has not changed since it was last
458 returns true if the repository has not changed since it was last
450 cloned from or pushed to a given repository.
459 cloned from or pushed to a given repository.
451 """
460 """
452 return False
461 return False
453
462
454 def dirty(self, ignoreupdate=False):
463 def dirty(self, ignoreupdate=False):
455 """returns true if the dirstate of the subrepo is dirty or does not
464 """returns true if the dirstate of the subrepo is dirty or does not
456 match current stored state. If ignoreupdate is true, only check
465 match current stored state. If ignoreupdate is true, only check
457 whether the subrepo has uncommitted changes in its dirstate.
466 whether the subrepo has uncommitted changes in its dirstate.
458 """
467 """
459 raise NotImplementedError
468 raise NotImplementedError
460
469
461 def dirtyreason(self, ignoreupdate=False):
470 def dirtyreason(self, ignoreupdate=False):
462 """return reason string if it is ``dirty()``
471 """return reason string if it is ``dirty()``
463
472
464 Returned string should have enough information for the message
473 Returned string should have enough information for the message
465 of exception.
474 of exception.
466
475
467 This returns None, otherwise.
476 This returns None, otherwise.
468 """
477 """
469 if self.dirty(ignoreupdate=ignoreupdate):
478 if self.dirty(ignoreupdate=ignoreupdate):
470 return _("uncommitted changes in subrepository '%s'"
479 return _("uncommitted changes in subrepository '%s'"
471 ) % subrelpath(self)
480 ) % subrelpath(self)
472
481
473 def bailifchanged(self, ignoreupdate=False, hint=None):
482 def bailifchanged(self, ignoreupdate=False, hint=None):
474 """raise Abort if subrepository is ``dirty()``
483 """raise Abort if subrepository is ``dirty()``
475 """
484 """
476 dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate)
485 dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate)
477 if dirtyreason:
486 if dirtyreason:
478 raise error.Abort(dirtyreason, hint=hint)
487 raise error.Abort(dirtyreason, hint=hint)
479
488
480 def basestate(self):
489 def basestate(self):
481 """current working directory base state, disregarding .hgsubstate
490 """current working directory base state, disregarding .hgsubstate
482 state and working directory modifications"""
491 state and working directory modifications"""
483 raise NotImplementedError
492 raise NotImplementedError
484
493
485 def checknested(self, path):
494 def checknested(self, path):
486 """check if path is a subrepository within this repository"""
495 """check if path is a subrepository within this repository"""
487 return False
496 return False
488
497
489 def commit(self, text, user, date):
498 def commit(self, text, user, date):
490 """commit the current changes to the subrepo with the given
499 """commit the current changes to the subrepo with the given
491 log message. Use given user and date if possible. Return the
500 log message. Use given user and date if possible. Return the
492 new state of the subrepo.
501 new state of the subrepo.
493 """
502 """
494 raise NotImplementedError
503 raise NotImplementedError
495
504
496 def phase(self, state):
505 def phase(self, state):
497 """returns phase of specified state in the subrepository.
506 """returns phase of specified state in the subrepository.
498 """
507 """
499 return phases.public
508 return phases.public
500
509
501 def remove(self):
510 def remove(self):
502 """remove the subrepo
511 """remove the subrepo
503
512
504 (should verify the dirstate is not dirty first)
513 (should verify the dirstate is not dirty first)
505 """
514 """
506 raise NotImplementedError
515 raise NotImplementedError
507
516
508 def get(self, state, overwrite=False):
517 def get(self, state, overwrite=False):
509 """run whatever commands are needed to put the subrepo into
518 """run whatever commands are needed to put the subrepo into
510 this state
519 this state
511 """
520 """
512 raise NotImplementedError
521 raise NotImplementedError
513
522
514 def merge(self, state):
523 def merge(self, state):
515 """merge currently-saved state with the new state."""
524 """merge currently-saved state with the new state."""
516 raise NotImplementedError
525 raise NotImplementedError
517
526
518 def push(self, opts):
527 def push(self, opts):
519 """perform whatever action is analogous to 'hg push'
528 """perform whatever action is analogous to 'hg push'
520
529
521 This may be a no-op on some systems.
530 This may be a no-op on some systems.
522 """
531 """
523 raise NotImplementedError
532 raise NotImplementedError
524
533
525 def add(self, ui, match, prefix, explicitonly, **opts):
534 def add(self, ui, match, prefix, explicitonly, **opts):
526 return []
535 return []
527
536
528 def addremove(self, matcher, prefix, opts, dry_run, similarity):
537 def addremove(self, matcher, prefix, opts, dry_run, similarity):
529 self.ui.warn("%s: %s" % (prefix, _("addremove is not supported")))
538 self.ui.warn("%s: %s" % (prefix, _("addremove is not supported")))
530 return 1
539 return 1
531
540
532 def cat(self, match, prefix, **opts):
541 def cat(self, match, prefix, **opts):
533 return 1
542 return 1
534
543
535 def status(self, rev2, **opts):
544 def status(self, rev2, **opts):
536 return scmutil.status([], [], [], [], [], [], [])
545 return scmutil.status([], [], [], [], [], [], [])
537
546
538 def diff(self, ui, diffopts, node2, match, prefix, **opts):
547 def diff(self, ui, diffopts, node2, match, prefix, **opts):
539 pass
548 pass
540
549
541 def outgoing(self, ui, dest, opts):
550 def outgoing(self, ui, dest, opts):
542 return 1
551 return 1
543
552
544 def incoming(self, ui, source, opts):
553 def incoming(self, ui, source, opts):
545 return 1
554 return 1
546
555
547 def files(self):
556 def files(self):
548 """return filename iterator"""
557 """return filename iterator"""
549 raise NotImplementedError
558 raise NotImplementedError
550
559
551 def filedata(self, name, decode):
560 def filedata(self, name, decode):
552 """return file data, optionally passed through repo decoders"""
561 """return file data, optionally passed through repo decoders"""
553 raise NotImplementedError
562 raise NotImplementedError
554
563
555 def fileflags(self, name):
564 def fileflags(self, name):
556 """return file flags"""
565 """return file flags"""
557 return ''
566 return ''
558
567
559 def getfileset(self, expr):
568 def getfileset(self, expr):
560 """Resolve the fileset expression for this repo"""
569 """Resolve the fileset expression for this repo"""
561 return set()
570 return set()
562
571
563 def printfiles(self, ui, m, fm, fmt, subrepos):
572 def printfiles(self, ui, m, fm, fmt, subrepos):
564 """handle the files command for this subrepo"""
573 """handle the files command for this subrepo"""
565 return 1
574 return 1
566
575
567 def archive(self, archiver, prefix, match=None, decode=True):
576 def archive(self, archiver, prefix, match=None, decode=True):
568 if match is not None:
577 if match is not None:
569 files = [f for f in self.files() if match(f)]
578 files = [f for f in self.files() if match(f)]
570 else:
579 else:
571 files = self.files()
580 files = self.files()
572 total = len(files)
581 total = len(files)
573 relpath = subrelpath(self)
582 relpath = subrelpath(self)
574 self.ui.progress(_('archiving (%s)') % relpath, 0,
583 self.ui.progress(_('archiving (%s)') % relpath, 0,
575 unit=_('files'), total=total)
584 unit=_('files'), total=total)
576 for i, name in enumerate(files):
585 for i, name in enumerate(files):
577 flags = self.fileflags(name)
586 flags = self.fileflags(name)
578 mode = 'x' in flags and 0o755 or 0o644
587 mode = 'x' in flags and 0o755 or 0o644
579 symlink = 'l' in flags
588 symlink = 'l' in flags
580 archiver.addfile(prefix + self._path + '/' + name,
589 archiver.addfile(prefix + self._path + '/' + name,
581 mode, symlink, self.filedata(name, decode))
590 mode, symlink, self.filedata(name, decode))
582 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
591 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
583 unit=_('files'), total=total)
592 unit=_('files'), total=total)
584 self.ui.progress(_('archiving (%s)') % relpath, None)
593 self.ui.progress(_('archiving (%s)') % relpath, None)
585 return total
594 return total
586
595
587 def walk(self, match):
596 def walk(self, match):
588 '''
597 '''
589 walk recursively through the directory tree, finding all files
598 walk recursively through the directory tree, finding all files
590 matched by the match function
599 matched by the match function
591 '''
600 '''
592 pass
601 pass
593
602
594 def forget(self, match, prefix):
603 def forget(self, match, prefix):
595 return ([], [])
604 return ([], [])
596
605
597 def removefiles(self, matcher, prefix, after, force, subrepos, warnings):
606 def removefiles(self, matcher, prefix, after, force, subrepos, warnings):
598 """remove the matched files from the subrepository and the filesystem,
607 """remove the matched files from the subrepository and the filesystem,
599 possibly by force and/or after the file has been removed from the
608 possibly by force and/or after the file has been removed from the
600 filesystem. Return 0 on success, 1 on any warning.
609 filesystem. Return 0 on success, 1 on any warning.
601 """
610 """
602 warnings.append(_("warning: removefiles not implemented (%s)")
611 warnings.append(_("warning: removefiles not implemented (%s)")
603 % self._path)
612 % self._path)
604 return 1
613 return 1
605
614
606 def revert(self, substate, *pats, **opts):
615 def revert(self, substate, *pats, **opts):
607 self.ui.warn(_('%s: reverting %s subrepos is unsupported\n') \
616 self.ui.warn(_('%s: reverting %s subrepos is unsupported\n') \
608 % (substate[0], substate[2]))
617 % (substate[0], substate[2]))
609 return []
618 return []
610
619
611 def shortid(self, revid):
620 def shortid(self, revid):
612 return revid
621 return revid
613
622
614 def verify(self):
623 def verify(self):
615 '''verify the integrity of the repository. Return 0 on success or
624 '''verify the integrity of the repository. Return 0 on success or
616 warning, 1 on any error.
625 warning, 1 on any error.
617 '''
626 '''
618 return 0
627 return 0
619
628
620 @propertycache
629 @propertycache
621 def wvfs(self):
630 def wvfs(self):
622 """return vfs to access the working directory of this subrepository
631 """return vfs to access the working directory of this subrepository
623 """
632 """
624 return vfsmod.vfs(self._ctx.repo().wvfs.join(self._path))
633 return vfsmod.vfs(self._ctx.repo().wvfs.join(self._path))
625
634
626 @propertycache
635 @propertycache
627 def _relpath(self):
636 def _relpath(self):
628 """return path to this subrepository as seen from outermost repository
637 """return path to this subrepository as seen from outermost repository
629 """
638 """
630 return self.wvfs.reljoin(reporelpath(self._ctx.repo()), self._path)
639 return self.wvfs.reljoin(reporelpath(self._ctx.repo()), self._path)
631
640
632 class hgsubrepo(abstractsubrepo):
641 class hgsubrepo(abstractsubrepo):
633 def __init__(self, ctx, path, state, allowcreate):
642 def __init__(self, ctx, path, state, allowcreate):
634 super(hgsubrepo, self).__init__(ctx, path)
643 super(hgsubrepo, self).__init__(ctx, path)
635 self._state = state
644 self._state = state
636 r = ctx.repo()
645 r = ctx.repo()
637 root = r.wjoin(path)
646 root = r.wjoin(path)
638 create = allowcreate and not r.wvfs.exists('%s/.hg' % path)
647 create = allowcreate and not r.wvfs.exists('%s/.hg' % path)
639 self._repo = hg.repository(r.baseui, root, create=create)
648 self._repo = hg.repository(r.baseui, root, create=create)
640
649
641 # Propagate the parent's --hidden option
650 # Propagate the parent's --hidden option
642 if r is r.unfiltered():
651 if r is r.unfiltered():
643 self._repo = self._repo.unfiltered()
652 self._repo = self._repo.unfiltered()
644
653
645 self.ui = self._repo.ui
654 self.ui = self._repo.ui
646 for s, k in [('ui', 'commitsubrepos')]:
655 for s, k in [('ui', 'commitsubrepos')]:
647 v = r.ui.config(s, k)
656 v = r.ui.config(s, k)
648 if v:
657 if v:
649 self.ui.setconfig(s, k, v, 'subrepo')
658 self.ui.setconfig(s, k, v, 'subrepo')
650 # internal config: ui._usedassubrepo
659 # internal config: ui._usedassubrepo
651 self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
660 self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
652 self._initrepo(r, state[0], create)
661 self._initrepo(r, state[0], create)
653
662
663 @annotatesubrepoerror
664 def addwebdirpath(self, serverpath, webconf):
665 cmdutil.addwebdirpath(self._repo, subrelpath(self), webconf)
666
654 def storeclean(self, path):
667 def storeclean(self, path):
655 with self._repo.lock():
668 with self._repo.lock():
656 return self._storeclean(path)
669 return self._storeclean(path)
657
670
658 def _storeclean(self, path):
671 def _storeclean(self, path):
659 clean = True
672 clean = True
660 itercache = self._calcstorehash(path)
673 itercache = self._calcstorehash(path)
661 for filehash in self._readstorehashcache(path):
674 for filehash in self._readstorehashcache(path):
662 if filehash != next(itercache, None):
675 if filehash != next(itercache, None):
663 clean = False
676 clean = False
664 break
677 break
665 if clean:
678 if clean:
666 # if not empty:
679 # if not empty:
667 # the cached and current pull states have a different size
680 # the cached and current pull states have a different size
668 clean = next(itercache, None) is None
681 clean = next(itercache, None) is None
669 return clean
682 return clean
670
683
671 def _calcstorehash(self, remotepath):
684 def _calcstorehash(self, remotepath):
672 '''calculate a unique "store hash"
685 '''calculate a unique "store hash"
673
686
674 This method is used to to detect when there are changes that may
687 This method is used to to detect when there are changes that may
675 require a push to a given remote path.'''
688 require a push to a given remote path.'''
676 # sort the files that will be hashed in increasing (likely) file size
689 # sort the files that will be hashed in increasing (likely) file size
677 filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i')
690 filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i')
678 yield '# %s\n' % _expandedabspath(remotepath)
691 yield '# %s\n' % _expandedabspath(remotepath)
679 vfs = self._repo.vfs
692 vfs = self._repo.vfs
680 for relname in filelist:
693 for relname in filelist:
681 filehash = hashlib.sha1(vfs.tryread(relname)).hexdigest()
694 filehash = hashlib.sha1(vfs.tryread(relname)).hexdigest()
682 yield '%s = %s\n' % (relname, filehash)
695 yield '%s = %s\n' % (relname, filehash)
683
696
684 @propertycache
697 @propertycache
685 def _cachestorehashvfs(self):
698 def _cachestorehashvfs(self):
686 return vfsmod.vfs(self._repo.vfs.join('cache/storehash'))
699 return vfsmod.vfs(self._repo.vfs.join('cache/storehash'))
687
700
688 def _readstorehashcache(self, remotepath):
701 def _readstorehashcache(self, remotepath):
689 '''read the store hash cache for a given remote repository'''
702 '''read the store hash cache for a given remote repository'''
690 cachefile = _getstorehashcachename(remotepath)
703 cachefile = _getstorehashcachename(remotepath)
691 return self._cachestorehashvfs.tryreadlines(cachefile, 'r')
704 return self._cachestorehashvfs.tryreadlines(cachefile, 'r')
692
705
693 def _cachestorehash(self, remotepath):
706 def _cachestorehash(self, remotepath):
694 '''cache the current store hash
707 '''cache the current store hash
695
708
696 Each remote repo requires its own store hash cache, because a subrepo
709 Each remote repo requires its own store hash cache, because a subrepo
697 store may be "clean" versus a given remote repo, but not versus another
710 store may be "clean" versus a given remote repo, but not versus another
698 '''
711 '''
699 cachefile = _getstorehashcachename(remotepath)
712 cachefile = _getstorehashcachename(remotepath)
700 with self._repo.lock():
713 with self._repo.lock():
701 storehash = list(self._calcstorehash(remotepath))
714 storehash = list(self._calcstorehash(remotepath))
702 vfs = self._cachestorehashvfs
715 vfs = self._cachestorehashvfs
703 vfs.writelines(cachefile, storehash, mode='w', notindexed=True)
716 vfs.writelines(cachefile, storehash, mode='w', notindexed=True)
704
717
705 def _getctx(self):
718 def _getctx(self):
706 '''fetch the context for this subrepo revision, possibly a workingctx
719 '''fetch the context for this subrepo revision, possibly a workingctx
707 '''
720 '''
708 if self._ctx.rev() is None:
721 if self._ctx.rev() is None:
709 return self._repo[None] # workingctx if parent is workingctx
722 return self._repo[None] # workingctx if parent is workingctx
710 else:
723 else:
711 rev = self._state[1]
724 rev = self._state[1]
712 return self._repo[rev]
725 return self._repo[rev]
713
726
714 @annotatesubrepoerror
727 @annotatesubrepoerror
715 def _initrepo(self, parentrepo, source, create):
728 def _initrepo(self, parentrepo, source, create):
716 self._repo._subparent = parentrepo
729 self._repo._subparent = parentrepo
717 self._repo._subsource = source
730 self._repo._subsource = source
718
731
719 if create:
732 if create:
720 lines = ['[paths]\n']
733 lines = ['[paths]\n']
721
734
722 def addpathconfig(key, value):
735 def addpathconfig(key, value):
723 if value:
736 if value:
724 lines.append('%s = %s\n' % (key, value))
737 lines.append('%s = %s\n' % (key, value))
725 self.ui.setconfig('paths', key, value, 'subrepo')
738 self.ui.setconfig('paths', key, value, 'subrepo')
726
739
727 defpath = _abssource(self._repo, abort=False)
740 defpath = _abssource(self._repo, abort=False)
728 defpushpath = _abssource(self._repo, True, abort=False)
741 defpushpath = _abssource(self._repo, True, abort=False)
729 addpathconfig('default', defpath)
742 addpathconfig('default', defpath)
730 if defpath != defpushpath:
743 if defpath != defpushpath:
731 addpathconfig('default-push', defpushpath)
744 addpathconfig('default-push', defpushpath)
732
745
733 fp = self._repo.vfs("hgrc", "w", text=True)
746 fp = self._repo.vfs("hgrc", "w", text=True)
734 try:
747 try:
735 fp.write(''.join(lines))
748 fp.write(''.join(lines))
736 finally:
749 finally:
737 fp.close()
750 fp.close()
738
751
739 @annotatesubrepoerror
752 @annotatesubrepoerror
740 def add(self, ui, match, prefix, explicitonly, **opts):
753 def add(self, ui, match, prefix, explicitonly, **opts):
741 return cmdutil.add(ui, self._repo, match,
754 return cmdutil.add(ui, self._repo, match,
742 self.wvfs.reljoin(prefix, self._path),
755 self.wvfs.reljoin(prefix, self._path),
743 explicitonly, **opts)
756 explicitonly, **opts)
744
757
745 @annotatesubrepoerror
758 @annotatesubrepoerror
746 def addremove(self, m, prefix, opts, dry_run, similarity):
759 def addremove(self, m, prefix, opts, dry_run, similarity):
747 # In the same way as sub directories are processed, once in a subrepo,
760 # In the same way as sub directories are processed, once in a subrepo,
748 # always entry any of its subrepos. Don't corrupt the options that will
761 # always entry any of its subrepos. Don't corrupt the options that will
749 # be used to process sibling subrepos however.
762 # be used to process sibling subrepos however.
750 opts = copy.copy(opts)
763 opts = copy.copy(opts)
751 opts['subrepos'] = True
764 opts['subrepos'] = True
752 return scmutil.addremove(self._repo, m,
765 return scmutil.addremove(self._repo, m,
753 self.wvfs.reljoin(prefix, self._path), opts,
766 self.wvfs.reljoin(prefix, self._path), opts,
754 dry_run, similarity)
767 dry_run, similarity)
755
768
756 @annotatesubrepoerror
769 @annotatesubrepoerror
757 def cat(self, match, prefix, **opts):
770 def cat(self, match, prefix, **opts):
758 rev = self._state[1]
771 rev = self._state[1]
759 ctx = self._repo[rev]
772 ctx = self._repo[rev]
760 return cmdutil.cat(self.ui, self._repo, ctx, match, prefix, **opts)
773 return cmdutil.cat(self.ui, self._repo, ctx, match, prefix, **opts)
761
774
762 @annotatesubrepoerror
775 @annotatesubrepoerror
763 def status(self, rev2, **opts):
776 def status(self, rev2, **opts):
764 try:
777 try:
765 rev1 = self._state[1]
778 rev1 = self._state[1]
766 ctx1 = self._repo[rev1]
779 ctx1 = self._repo[rev1]
767 ctx2 = self._repo[rev2]
780 ctx2 = self._repo[rev2]
768 return self._repo.status(ctx1, ctx2, **opts)
781 return self._repo.status(ctx1, ctx2, **opts)
769 except error.RepoLookupError as inst:
782 except error.RepoLookupError as inst:
770 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
783 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
771 % (inst, subrelpath(self)))
784 % (inst, subrelpath(self)))
772 return scmutil.status([], [], [], [], [], [], [])
785 return scmutil.status([], [], [], [], [], [], [])
773
786
774 @annotatesubrepoerror
787 @annotatesubrepoerror
775 def diff(self, ui, diffopts, node2, match, prefix, **opts):
788 def diff(self, ui, diffopts, node2, match, prefix, **opts):
776 try:
789 try:
777 node1 = node.bin(self._state[1])
790 node1 = node.bin(self._state[1])
778 # We currently expect node2 to come from substate and be
791 # We currently expect node2 to come from substate and be
779 # in hex format
792 # in hex format
780 if node2 is not None:
793 if node2 is not None:
781 node2 = node.bin(node2)
794 node2 = node.bin(node2)
782 cmdutil.diffordiffstat(ui, self._repo, diffopts,
795 cmdutil.diffordiffstat(ui, self._repo, diffopts,
783 node1, node2, match,
796 node1, node2, match,
784 prefix=posixpath.join(prefix, self._path),
797 prefix=posixpath.join(prefix, self._path),
785 listsubrepos=True, **opts)
798 listsubrepos=True, **opts)
786 except error.RepoLookupError as inst:
799 except error.RepoLookupError as inst:
787 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
800 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
788 % (inst, subrelpath(self)))
801 % (inst, subrelpath(self)))
789
802
790 @annotatesubrepoerror
803 @annotatesubrepoerror
791 def archive(self, archiver, prefix, match=None, decode=True):
804 def archive(self, archiver, prefix, match=None, decode=True):
792 self._get(self._state + ('hg',))
805 self._get(self._state + ('hg',))
793 total = abstractsubrepo.archive(self, archiver, prefix, match)
806 total = abstractsubrepo.archive(self, archiver, prefix, match)
794 rev = self._state[1]
807 rev = self._state[1]
795 ctx = self._repo[rev]
808 ctx = self._repo[rev]
796 for subpath in ctx.substate:
809 for subpath in ctx.substate:
797 s = subrepo(ctx, subpath, True)
810 s = subrepo(ctx, subpath, True)
798 submatch = matchmod.subdirmatcher(subpath, match)
811 submatch = matchmod.subdirmatcher(subpath, match)
799 total += s.archive(archiver, prefix + self._path + '/', submatch,
812 total += s.archive(archiver, prefix + self._path + '/', submatch,
800 decode)
813 decode)
801 return total
814 return total
802
815
803 @annotatesubrepoerror
816 @annotatesubrepoerror
804 def dirty(self, ignoreupdate=False):
817 def dirty(self, ignoreupdate=False):
805 r = self._state[1]
818 r = self._state[1]
806 if r == '' and not ignoreupdate: # no state recorded
819 if r == '' and not ignoreupdate: # no state recorded
807 return True
820 return True
808 w = self._repo[None]
821 w = self._repo[None]
809 if r != w.p1().hex() and not ignoreupdate:
822 if r != w.p1().hex() and not ignoreupdate:
810 # different version checked out
823 # different version checked out
811 return True
824 return True
812 return w.dirty() # working directory changed
825 return w.dirty() # working directory changed
813
826
814 def basestate(self):
827 def basestate(self):
815 return self._repo['.'].hex()
828 return self._repo['.'].hex()
816
829
817 def checknested(self, path):
830 def checknested(self, path):
818 return self._repo._checknested(self._repo.wjoin(path))
831 return self._repo._checknested(self._repo.wjoin(path))
819
832
820 @annotatesubrepoerror
833 @annotatesubrepoerror
821 def commit(self, text, user, date):
834 def commit(self, text, user, date):
822 # don't bother committing in the subrepo if it's only been
835 # don't bother committing in the subrepo if it's only been
823 # updated
836 # updated
824 if not self.dirty(True):
837 if not self.dirty(True):
825 return self._repo['.'].hex()
838 return self._repo['.'].hex()
826 self.ui.debug("committing subrepo %s\n" % subrelpath(self))
839 self.ui.debug("committing subrepo %s\n" % subrelpath(self))
827 n = self._repo.commit(text, user, date)
840 n = self._repo.commit(text, user, date)
828 if not n:
841 if not n:
829 return self._repo['.'].hex() # different version checked out
842 return self._repo['.'].hex() # different version checked out
830 return node.hex(n)
843 return node.hex(n)
831
844
832 @annotatesubrepoerror
845 @annotatesubrepoerror
833 def phase(self, state):
846 def phase(self, state):
834 return self._repo[state].phase()
847 return self._repo[state].phase()
835
848
836 @annotatesubrepoerror
849 @annotatesubrepoerror
837 def remove(self):
850 def remove(self):
838 # we can't fully delete the repository as it may contain
851 # we can't fully delete the repository as it may contain
839 # local-only history
852 # local-only history
840 self.ui.note(_('removing subrepo %s\n') % subrelpath(self))
853 self.ui.note(_('removing subrepo %s\n') % subrelpath(self))
841 hg.clean(self._repo, node.nullid, False)
854 hg.clean(self._repo, node.nullid, False)
842
855
843 def _get(self, state):
856 def _get(self, state):
844 source, revision, kind = state
857 source, revision, kind = state
845 if revision in self._repo.unfiltered():
858 if revision in self._repo.unfiltered():
846 return True
859 return True
847 self._repo._subsource = source
860 self._repo._subsource = source
848 srcurl = _abssource(self._repo)
861 srcurl = _abssource(self._repo)
849 other = hg.peer(self._repo, {}, srcurl)
862 other = hg.peer(self._repo, {}, srcurl)
850 if len(self._repo) == 0:
863 if len(self._repo) == 0:
851 self.ui.status(_('cloning subrepo %s from %s\n')
864 self.ui.status(_('cloning subrepo %s from %s\n')
852 % (subrelpath(self), srcurl))
865 % (subrelpath(self), srcurl))
853 parentrepo = self._repo._subparent
866 parentrepo = self._repo._subparent
854 # use self._repo.vfs instead of self.wvfs to remove .hg only
867 # use self._repo.vfs instead of self.wvfs to remove .hg only
855 self._repo.vfs.rmtree()
868 self._repo.vfs.rmtree()
856 other, cloned = hg.clone(self._repo._subparent.baseui, {},
869 other, cloned = hg.clone(self._repo._subparent.baseui, {},
857 other, self._repo.root,
870 other, self._repo.root,
858 update=False)
871 update=False)
859 self._repo = cloned.local()
872 self._repo = cloned.local()
860 self._initrepo(parentrepo, source, create=True)
873 self._initrepo(parentrepo, source, create=True)
861 self._cachestorehash(srcurl)
874 self._cachestorehash(srcurl)
862 else:
875 else:
863 self.ui.status(_('pulling subrepo %s from %s\n')
876 self.ui.status(_('pulling subrepo %s from %s\n')
864 % (subrelpath(self), srcurl))
877 % (subrelpath(self), srcurl))
865 cleansub = self.storeclean(srcurl)
878 cleansub = self.storeclean(srcurl)
866 exchange.pull(self._repo, other)
879 exchange.pull(self._repo, other)
867 if cleansub:
880 if cleansub:
868 # keep the repo clean after pull
881 # keep the repo clean after pull
869 self._cachestorehash(srcurl)
882 self._cachestorehash(srcurl)
870 return False
883 return False
871
884
872 @annotatesubrepoerror
885 @annotatesubrepoerror
873 def get(self, state, overwrite=False):
886 def get(self, state, overwrite=False):
874 inrepo = self._get(state)
887 inrepo = self._get(state)
875 source, revision, kind = state
888 source, revision, kind = state
876 repo = self._repo
889 repo = self._repo
877 repo.ui.debug("getting subrepo %s\n" % self._path)
890 repo.ui.debug("getting subrepo %s\n" % self._path)
878 if inrepo:
891 if inrepo:
879 urepo = repo.unfiltered()
892 urepo = repo.unfiltered()
880 ctx = urepo[revision]
893 ctx = urepo[revision]
881 if ctx.hidden():
894 if ctx.hidden():
882 urepo.ui.warn(
895 urepo.ui.warn(
883 _('revision %s in subrepo %s is hidden\n') \
896 _('revision %s in subrepo %s is hidden\n') \
884 % (revision[0:12], self._path))
897 % (revision[0:12], self._path))
885 repo = urepo
898 repo = urepo
886 hg.updaterepo(repo, revision, overwrite)
899 hg.updaterepo(repo, revision, overwrite)
887
900
888 @annotatesubrepoerror
901 @annotatesubrepoerror
889 def merge(self, state):
902 def merge(self, state):
890 self._get(state)
903 self._get(state)
891 cur = self._repo['.']
904 cur = self._repo['.']
892 dst = self._repo[state[1]]
905 dst = self._repo[state[1]]
893 anc = dst.ancestor(cur)
906 anc = dst.ancestor(cur)
894
907
895 def mergefunc():
908 def mergefunc():
896 if anc == cur and dst.branch() == cur.branch():
909 if anc == cur and dst.branch() == cur.branch():
897 self.ui.debug("updating subrepo %s\n" % subrelpath(self))
910 self.ui.debug("updating subrepo %s\n" % subrelpath(self))
898 hg.update(self._repo, state[1])
911 hg.update(self._repo, state[1])
899 elif anc == dst:
912 elif anc == dst:
900 self.ui.debug("skipping subrepo %s\n" % subrelpath(self))
913 self.ui.debug("skipping subrepo %s\n" % subrelpath(self))
901 else:
914 else:
902 self.ui.debug("merging subrepo %s\n" % subrelpath(self))
915 self.ui.debug("merging subrepo %s\n" % subrelpath(self))
903 hg.merge(self._repo, state[1], remind=False)
916 hg.merge(self._repo, state[1], remind=False)
904
917
905 wctx = self._repo[None]
918 wctx = self._repo[None]
906 if self.dirty():
919 if self.dirty():
907 if anc != dst:
920 if anc != dst:
908 if _updateprompt(self.ui, self, wctx.dirty(), cur, dst):
921 if _updateprompt(self.ui, self, wctx.dirty(), cur, dst):
909 mergefunc()
922 mergefunc()
910 else:
923 else:
911 mergefunc()
924 mergefunc()
912 else:
925 else:
913 mergefunc()
926 mergefunc()
914
927
915 @annotatesubrepoerror
928 @annotatesubrepoerror
916 def push(self, opts):
929 def push(self, opts):
917 force = opts.get('force')
930 force = opts.get('force')
918 newbranch = opts.get('new_branch')
931 newbranch = opts.get('new_branch')
919 ssh = opts.get('ssh')
932 ssh = opts.get('ssh')
920
933
921 # push subrepos depth-first for coherent ordering
934 # push subrepos depth-first for coherent ordering
922 c = self._repo['']
935 c = self._repo['']
923 subs = c.substate # only repos that are committed
936 subs = c.substate # only repos that are committed
924 for s in sorted(subs):
937 for s in sorted(subs):
925 if c.sub(s).push(opts) == 0:
938 if c.sub(s).push(opts) == 0:
926 return False
939 return False
927
940
928 dsturl = _abssource(self._repo, True)
941 dsturl = _abssource(self._repo, True)
929 if not force:
942 if not force:
930 if self.storeclean(dsturl):
943 if self.storeclean(dsturl):
931 self.ui.status(
944 self.ui.status(
932 _('no changes made to subrepo %s since last push to %s\n')
945 _('no changes made to subrepo %s since last push to %s\n')
933 % (subrelpath(self), dsturl))
946 % (subrelpath(self), dsturl))
934 return None
947 return None
935 self.ui.status(_('pushing subrepo %s to %s\n') %
948 self.ui.status(_('pushing subrepo %s to %s\n') %
936 (subrelpath(self), dsturl))
949 (subrelpath(self), dsturl))
937 other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
950 other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
938 res = exchange.push(self._repo, other, force, newbranch=newbranch)
951 res = exchange.push(self._repo, other, force, newbranch=newbranch)
939
952
940 # the repo is now clean
953 # the repo is now clean
941 self._cachestorehash(dsturl)
954 self._cachestorehash(dsturl)
942 return res.cgresult
955 return res.cgresult
943
956
944 @annotatesubrepoerror
957 @annotatesubrepoerror
945 def outgoing(self, ui, dest, opts):
958 def outgoing(self, ui, dest, opts):
946 if 'rev' in opts or 'branch' in opts:
959 if 'rev' in opts or 'branch' in opts:
947 opts = copy.copy(opts)
960 opts = copy.copy(opts)
948 opts.pop('rev', None)
961 opts.pop('rev', None)
949 opts.pop('branch', None)
962 opts.pop('branch', None)
950 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
963 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
951
964
952 @annotatesubrepoerror
965 @annotatesubrepoerror
953 def incoming(self, ui, source, opts):
966 def incoming(self, ui, source, opts):
954 if 'rev' in opts or 'branch' in opts:
967 if 'rev' in opts or 'branch' in opts:
955 opts = copy.copy(opts)
968 opts = copy.copy(opts)
956 opts.pop('rev', None)
969 opts.pop('rev', None)
957 opts.pop('branch', None)
970 opts.pop('branch', None)
958 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
971 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
959
972
960 @annotatesubrepoerror
973 @annotatesubrepoerror
961 def files(self):
974 def files(self):
962 rev = self._state[1]
975 rev = self._state[1]
963 ctx = self._repo[rev]
976 ctx = self._repo[rev]
964 return ctx.manifest().keys()
977 return ctx.manifest().keys()
965
978
966 def filedata(self, name, decode):
979 def filedata(self, name, decode):
967 rev = self._state[1]
980 rev = self._state[1]
968 data = self._repo[rev][name].data()
981 data = self._repo[rev][name].data()
969 if decode:
982 if decode:
970 data = self._repo.wwritedata(name, data)
983 data = self._repo.wwritedata(name, data)
971 return data
984 return data
972
985
973 def fileflags(self, name):
986 def fileflags(self, name):
974 rev = self._state[1]
987 rev = self._state[1]
975 ctx = self._repo[rev]
988 ctx = self._repo[rev]
976 return ctx.flags(name)
989 return ctx.flags(name)
977
990
978 @annotatesubrepoerror
991 @annotatesubrepoerror
979 def printfiles(self, ui, m, fm, fmt, subrepos):
992 def printfiles(self, ui, m, fm, fmt, subrepos):
980 # If the parent context is a workingctx, use the workingctx here for
993 # If the parent context is a workingctx, use the workingctx here for
981 # consistency.
994 # consistency.
982 if self._ctx.rev() is None:
995 if self._ctx.rev() is None:
983 ctx = self._repo[None]
996 ctx = self._repo[None]
984 else:
997 else:
985 rev = self._state[1]
998 rev = self._state[1]
986 ctx = self._repo[rev]
999 ctx = self._repo[rev]
987 return cmdutil.files(ui, ctx, m, fm, fmt, subrepos)
1000 return cmdutil.files(ui, ctx, m, fm, fmt, subrepos)
988
1001
989 @annotatesubrepoerror
1002 @annotatesubrepoerror
990 def getfileset(self, expr):
1003 def getfileset(self, expr):
991 if self._ctx.rev() is None:
1004 if self._ctx.rev() is None:
992 ctx = self._repo[None]
1005 ctx = self._repo[None]
993 else:
1006 else:
994 rev = self._state[1]
1007 rev = self._state[1]
995 ctx = self._repo[rev]
1008 ctx = self._repo[rev]
996
1009
997 files = ctx.getfileset(expr)
1010 files = ctx.getfileset(expr)
998
1011
999 for subpath in ctx.substate:
1012 for subpath in ctx.substate:
1000 sub = ctx.sub(subpath)
1013 sub = ctx.sub(subpath)
1001
1014
1002 try:
1015 try:
1003 files.extend(subpath + '/' + f for f in sub.getfileset(expr))
1016 files.extend(subpath + '/' + f for f in sub.getfileset(expr))
1004 except error.LookupError:
1017 except error.LookupError:
1005 self.ui.status(_("skipping missing subrepository: %s\n")
1018 self.ui.status(_("skipping missing subrepository: %s\n")
1006 % self.wvfs.reljoin(reporelpath(self), subpath))
1019 % self.wvfs.reljoin(reporelpath(self), subpath))
1007 return files
1020 return files
1008
1021
1009 def walk(self, match):
1022 def walk(self, match):
1010 ctx = self._repo[None]
1023 ctx = self._repo[None]
1011 return ctx.walk(match)
1024 return ctx.walk(match)
1012
1025
1013 @annotatesubrepoerror
1026 @annotatesubrepoerror
1014 def forget(self, match, prefix):
1027 def forget(self, match, prefix):
1015 return cmdutil.forget(self.ui, self._repo, match,
1028 return cmdutil.forget(self.ui, self._repo, match,
1016 self.wvfs.reljoin(prefix, self._path), True)
1029 self.wvfs.reljoin(prefix, self._path), True)
1017
1030
1018 @annotatesubrepoerror
1031 @annotatesubrepoerror
1019 def removefiles(self, matcher, prefix, after, force, subrepos, warnings):
1032 def removefiles(self, matcher, prefix, after, force, subrepos, warnings):
1020 return cmdutil.remove(self.ui, self._repo, matcher,
1033 return cmdutil.remove(self.ui, self._repo, matcher,
1021 self.wvfs.reljoin(prefix, self._path),
1034 self.wvfs.reljoin(prefix, self._path),
1022 after, force, subrepos)
1035 after, force, subrepos)
1023
1036
1024 @annotatesubrepoerror
1037 @annotatesubrepoerror
1025 def revert(self, substate, *pats, **opts):
1038 def revert(self, substate, *pats, **opts):
1026 # reverting a subrepo is a 2 step process:
1039 # reverting a subrepo is a 2 step process:
1027 # 1. if the no_backup is not set, revert all modified
1040 # 1. if the no_backup is not set, revert all modified
1028 # files inside the subrepo
1041 # files inside the subrepo
1029 # 2. update the subrepo to the revision specified in
1042 # 2. update the subrepo to the revision specified in
1030 # the corresponding substate dictionary
1043 # the corresponding substate dictionary
1031 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1044 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1032 if not opts.get('no_backup'):
1045 if not opts.get('no_backup'):
1033 # Revert all files on the subrepo, creating backups
1046 # Revert all files on the subrepo, creating backups
1034 # Note that this will not recursively revert subrepos
1047 # Note that this will not recursively revert subrepos
1035 # We could do it if there was a set:subrepos() predicate
1048 # We could do it if there was a set:subrepos() predicate
1036 opts = opts.copy()
1049 opts = opts.copy()
1037 opts['date'] = None
1050 opts['date'] = None
1038 opts['rev'] = substate[1]
1051 opts['rev'] = substate[1]
1039
1052
1040 self.filerevert(*pats, **opts)
1053 self.filerevert(*pats, **opts)
1041
1054
1042 # Update the repo to the revision specified in the given substate
1055 # Update the repo to the revision specified in the given substate
1043 if not opts.get('dry_run'):
1056 if not opts.get('dry_run'):
1044 self.get(substate, overwrite=True)
1057 self.get(substate, overwrite=True)
1045
1058
1046 def filerevert(self, *pats, **opts):
1059 def filerevert(self, *pats, **opts):
1047 ctx = self._repo[opts['rev']]
1060 ctx = self._repo[opts['rev']]
1048 parents = self._repo.dirstate.parents()
1061 parents = self._repo.dirstate.parents()
1049 if opts.get('all'):
1062 if opts.get('all'):
1050 pats = ['set:modified()']
1063 pats = ['set:modified()']
1051 else:
1064 else:
1052 pats = []
1065 pats = []
1053 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts)
1066 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts)
1054
1067
1055 def shortid(self, revid):
1068 def shortid(self, revid):
1056 return revid[:12]
1069 return revid[:12]
1057
1070
1058 def verify(self):
1071 def verify(self):
1059 try:
1072 try:
1060 rev = self._state[1]
1073 rev = self._state[1]
1061 ctx = self._repo.unfiltered()[rev]
1074 ctx = self._repo.unfiltered()[rev]
1062 if ctx.hidden():
1075 if ctx.hidden():
1063 # Since hidden revisions aren't pushed/pulled, it seems worth an
1076 # Since hidden revisions aren't pushed/pulled, it seems worth an
1064 # explicit warning.
1077 # explicit warning.
1065 ui = self._repo.ui
1078 ui = self._repo.ui
1066 ui.warn(_("subrepo '%s' is hidden in revision %s\n") %
1079 ui.warn(_("subrepo '%s' is hidden in revision %s\n") %
1067 (self._relpath, node.short(self._ctx.node())))
1080 (self._relpath, node.short(self._ctx.node())))
1068 return 0
1081 return 0
1069 except error.RepoLookupError:
1082 except error.RepoLookupError:
1070 # A missing subrepo revision may be a case of needing to pull it, so
1083 # A missing subrepo revision may be a case of needing to pull it, so
1071 # don't treat this as an error.
1084 # don't treat this as an error.
1072 self._repo.ui.warn(_("subrepo '%s' not found in revision %s\n") %
1085 self._repo.ui.warn(_("subrepo '%s' not found in revision %s\n") %
1073 (self._relpath, node.short(self._ctx.node())))
1086 (self._relpath, node.short(self._ctx.node())))
1074 return 0
1087 return 0
1075
1088
1076 @propertycache
1089 @propertycache
1077 def wvfs(self):
1090 def wvfs(self):
1078 """return own wvfs for efficiency and consistency
1091 """return own wvfs for efficiency and consistency
1079 """
1092 """
1080 return self._repo.wvfs
1093 return self._repo.wvfs
1081
1094
1082 @propertycache
1095 @propertycache
1083 def _relpath(self):
1096 def _relpath(self):
1084 """return path to this subrepository as seen from outermost repository
1097 """return path to this subrepository as seen from outermost repository
1085 """
1098 """
1086 # Keep consistent dir separators by avoiding vfs.join(self._path)
1099 # Keep consistent dir separators by avoiding vfs.join(self._path)
1087 return reporelpath(self._repo)
1100 return reporelpath(self._repo)
1088
1101
1089 class svnsubrepo(abstractsubrepo):
1102 class svnsubrepo(abstractsubrepo):
1090 def __init__(self, ctx, path, state, allowcreate):
1103 def __init__(self, ctx, path, state, allowcreate):
1091 super(svnsubrepo, self).__init__(ctx, path)
1104 super(svnsubrepo, self).__init__(ctx, path)
1092 self._state = state
1105 self._state = state
1093 self._exe = util.findexe('svn')
1106 self._exe = util.findexe('svn')
1094 if not self._exe:
1107 if not self._exe:
1095 raise error.Abort(_("'svn' executable not found for subrepo '%s'")
1108 raise error.Abort(_("'svn' executable not found for subrepo '%s'")
1096 % self._path)
1109 % self._path)
1097
1110
1098 def _svncommand(self, commands, filename='', failok=False):
1111 def _svncommand(self, commands, filename='', failok=False):
1099 cmd = [self._exe]
1112 cmd = [self._exe]
1100 extrakw = {}
1113 extrakw = {}
1101 if not self.ui.interactive():
1114 if not self.ui.interactive():
1102 # Making stdin be a pipe should prevent svn from behaving
1115 # Making stdin be a pipe should prevent svn from behaving
1103 # interactively even if we can't pass --non-interactive.
1116 # interactively even if we can't pass --non-interactive.
1104 extrakw['stdin'] = subprocess.PIPE
1117 extrakw['stdin'] = subprocess.PIPE
1105 # Starting in svn 1.5 --non-interactive is a global flag
1118 # Starting in svn 1.5 --non-interactive is a global flag
1106 # instead of being per-command, but we need to support 1.4 so
1119 # instead of being per-command, but we need to support 1.4 so
1107 # we have to be intelligent about what commands take
1120 # we have to be intelligent about what commands take
1108 # --non-interactive.
1121 # --non-interactive.
1109 if commands[0] in ('update', 'checkout', 'commit'):
1122 if commands[0] in ('update', 'checkout', 'commit'):
1110 cmd.append('--non-interactive')
1123 cmd.append('--non-interactive')
1111 cmd.extend(commands)
1124 cmd.extend(commands)
1112 if filename is not None:
1125 if filename is not None:
1113 path = self.wvfs.reljoin(self._ctx.repo().origroot,
1126 path = self.wvfs.reljoin(self._ctx.repo().origroot,
1114 self._path, filename)
1127 self._path, filename)
1115 cmd.append(path)
1128 cmd.append(path)
1116 env = dict(encoding.environ)
1129 env = dict(encoding.environ)
1117 # Avoid localized output, preserve current locale for everything else.
1130 # Avoid localized output, preserve current locale for everything else.
1118 lc_all = env.get('LC_ALL')
1131 lc_all = env.get('LC_ALL')
1119 if lc_all:
1132 if lc_all:
1120 env['LANG'] = lc_all
1133 env['LANG'] = lc_all
1121 del env['LC_ALL']
1134 del env['LC_ALL']
1122 env['LC_MESSAGES'] = 'C'
1135 env['LC_MESSAGES'] = 'C'
1123 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds,
1136 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds,
1124 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
1137 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
1125 universal_newlines=True, env=env, **extrakw)
1138 universal_newlines=True, env=env, **extrakw)
1126 stdout, stderr = p.communicate()
1139 stdout, stderr = p.communicate()
1127 stderr = stderr.strip()
1140 stderr = stderr.strip()
1128 if not failok:
1141 if not failok:
1129 if p.returncode:
1142 if p.returncode:
1130 raise error.Abort(stderr or 'exited with code %d'
1143 raise error.Abort(stderr or 'exited with code %d'
1131 % p.returncode)
1144 % p.returncode)
1132 if stderr:
1145 if stderr:
1133 self.ui.warn(stderr + '\n')
1146 self.ui.warn(stderr + '\n')
1134 return stdout, stderr
1147 return stdout, stderr
1135
1148
1136 @propertycache
1149 @propertycache
1137 def _svnversion(self):
1150 def _svnversion(self):
1138 output, err = self._svncommand(['--version', '--quiet'], filename=None)
1151 output, err = self._svncommand(['--version', '--quiet'], filename=None)
1139 m = re.search(r'^(\d+)\.(\d+)', output)
1152 m = re.search(r'^(\d+)\.(\d+)', output)
1140 if not m:
1153 if not m:
1141 raise error.Abort(_('cannot retrieve svn tool version'))
1154 raise error.Abort(_('cannot retrieve svn tool version'))
1142 return (int(m.group(1)), int(m.group(2)))
1155 return (int(m.group(1)), int(m.group(2)))
1143
1156
1144 def _wcrevs(self):
1157 def _wcrevs(self):
1145 # Get the working directory revision as well as the last
1158 # Get the working directory revision as well as the last
1146 # commit revision so we can compare the subrepo state with
1159 # commit revision so we can compare the subrepo state with
1147 # both. We used to store the working directory one.
1160 # both. We used to store the working directory one.
1148 output, err = self._svncommand(['info', '--xml'])
1161 output, err = self._svncommand(['info', '--xml'])
1149 doc = xml.dom.minidom.parseString(output)
1162 doc = xml.dom.minidom.parseString(output)
1150 entries = doc.getElementsByTagName('entry')
1163 entries = doc.getElementsByTagName('entry')
1151 lastrev, rev = '0', '0'
1164 lastrev, rev = '0', '0'
1152 if entries:
1165 if entries:
1153 rev = str(entries[0].getAttribute('revision')) or '0'
1166 rev = str(entries[0].getAttribute('revision')) or '0'
1154 commits = entries[0].getElementsByTagName('commit')
1167 commits = entries[0].getElementsByTagName('commit')
1155 if commits:
1168 if commits:
1156 lastrev = str(commits[0].getAttribute('revision')) or '0'
1169 lastrev = str(commits[0].getAttribute('revision')) or '0'
1157 return (lastrev, rev)
1170 return (lastrev, rev)
1158
1171
1159 def _wcrev(self):
1172 def _wcrev(self):
1160 return self._wcrevs()[0]
1173 return self._wcrevs()[0]
1161
1174
1162 def _wcchanged(self):
1175 def _wcchanged(self):
1163 """Return (changes, extchanges, missing) where changes is True
1176 """Return (changes, extchanges, missing) where changes is True
1164 if the working directory was changed, extchanges is
1177 if the working directory was changed, extchanges is
1165 True if any of these changes concern an external entry and missing
1178 True if any of these changes concern an external entry and missing
1166 is True if any change is a missing entry.
1179 is True if any change is a missing entry.
1167 """
1180 """
1168 output, err = self._svncommand(['status', '--xml'])
1181 output, err = self._svncommand(['status', '--xml'])
1169 externals, changes, missing = [], [], []
1182 externals, changes, missing = [], [], []
1170 doc = xml.dom.minidom.parseString(output)
1183 doc = xml.dom.minidom.parseString(output)
1171 for e in doc.getElementsByTagName('entry'):
1184 for e in doc.getElementsByTagName('entry'):
1172 s = e.getElementsByTagName('wc-status')
1185 s = e.getElementsByTagName('wc-status')
1173 if not s:
1186 if not s:
1174 continue
1187 continue
1175 item = s[0].getAttribute('item')
1188 item = s[0].getAttribute('item')
1176 props = s[0].getAttribute('props')
1189 props = s[0].getAttribute('props')
1177 path = e.getAttribute('path')
1190 path = e.getAttribute('path')
1178 if item == 'external':
1191 if item == 'external':
1179 externals.append(path)
1192 externals.append(path)
1180 elif item == 'missing':
1193 elif item == 'missing':
1181 missing.append(path)
1194 missing.append(path)
1182 if (item not in ('', 'normal', 'unversioned', 'external')
1195 if (item not in ('', 'normal', 'unversioned', 'external')
1183 or props not in ('', 'none', 'normal')):
1196 or props not in ('', 'none', 'normal')):
1184 changes.append(path)
1197 changes.append(path)
1185 for path in changes:
1198 for path in changes:
1186 for ext in externals:
1199 for ext in externals:
1187 if path == ext or path.startswith(ext + pycompat.ossep):
1200 if path == ext or path.startswith(ext + pycompat.ossep):
1188 return True, True, bool(missing)
1201 return True, True, bool(missing)
1189 return bool(changes), False, bool(missing)
1202 return bool(changes), False, bool(missing)
1190
1203
1191 def dirty(self, ignoreupdate=False):
1204 def dirty(self, ignoreupdate=False):
1192 if not self._wcchanged()[0]:
1205 if not self._wcchanged()[0]:
1193 if self._state[1] in self._wcrevs() or ignoreupdate:
1206 if self._state[1] in self._wcrevs() or ignoreupdate:
1194 return False
1207 return False
1195 return True
1208 return True
1196
1209
1197 def basestate(self):
1210 def basestate(self):
1198 lastrev, rev = self._wcrevs()
1211 lastrev, rev = self._wcrevs()
1199 if lastrev != rev:
1212 if lastrev != rev:
1200 # Last committed rev is not the same than rev. We would
1213 # Last committed rev is not the same than rev. We would
1201 # like to take lastrev but we do not know if the subrepo
1214 # like to take lastrev but we do not know if the subrepo
1202 # URL exists at lastrev. Test it and fallback to rev it
1215 # URL exists at lastrev. Test it and fallback to rev it
1203 # is not there.
1216 # is not there.
1204 try:
1217 try:
1205 self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)])
1218 self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)])
1206 return lastrev
1219 return lastrev
1207 except error.Abort:
1220 except error.Abort:
1208 pass
1221 pass
1209 return rev
1222 return rev
1210
1223
1211 @annotatesubrepoerror
1224 @annotatesubrepoerror
1212 def commit(self, text, user, date):
1225 def commit(self, text, user, date):
1213 # user and date are out of our hands since svn is centralized
1226 # user and date are out of our hands since svn is centralized
1214 changed, extchanged, missing = self._wcchanged()
1227 changed, extchanged, missing = self._wcchanged()
1215 if not changed:
1228 if not changed:
1216 return self.basestate()
1229 return self.basestate()
1217 if extchanged:
1230 if extchanged:
1218 # Do not try to commit externals
1231 # Do not try to commit externals
1219 raise error.Abort(_('cannot commit svn externals'))
1232 raise error.Abort(_('cannot commit svn externals'))
1220 if missing:
1233 if missing:
1221 # svn can commit with missing entries but aborting like hg
1234 # svn can commit with missing entries but aborting like hg
1222 # seems a better approach.
1235 # seems a better approach.
1223 raise error.Abort(_('cannot commit missing svn entries'))
1236 raise error.Abort(_('cannot commit missing svn entries'))
1224 commitinfo, err = self._svncommand(['commit', '-m', text])
1237 commitinfo, err = self._svncommand(['commit', '-m', text])
1225 self.ui.status(commitinfo)
1238 self.ui.status(commitinfo)
1226 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
1239 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
1227 if not newrev:
1240 if not newrev:
1228 if not commitinfo.strip():
1241 if not commitinfo.strip():
1229 # Sometimes, our definition of "changed" differs from
1242 # Sometimes, our definition of "changed" differs from
1230 # svn one. For instance, svn ignores missing files
1243 # svn one. For instance, svn ignores missing files
1231 # when committing. If there are only missing files, no
1244 # when committing. If there are only missing files, no
1232 # commit is made, no output and no error code.
1245 # commit is made, no output and no error code.
1233 raise error.Abort(_('failed to commit svn changes'))
1246 raise error.Abort(_('failed to commit svn changes'))
1234 raise error.Abort(commitinfo.splitlines()[-1])
1247 raise error.Abort(commitinfo.splitlines()[-1])
1235 newrev = newrev.groups()[0]
1248 newrev = newrev.groups()[0]
1236 self.ui.status(self._svncommand(['update', '-r', newrev])[0])
1249 self.ui.status(self._svncommand(['update', '-r', newrev])[0])
1237 return newrev
1250 return newrev
1238
1251
1239 @annotatesubrepoerror
1252 @annotatesubrepoerror
1240 def remove(self):
1253 def remove(self):
1241 if self.dirty():
1254 if self.dirty():
1242 self.ui.warn(_('not removing repo %s because '
1255 self.ui.warn(_('not removing repo %s because '
1243 'it has changes.\n') % self._path)
1256 'it has changes.\n') % self._path)
1244 return
1257 return
1245 self.ui.note(_('removing subrepo %s\n') % self._path)
1258 self.ui.note(_('removing subrepo %s\n') % self._path)
1246
1259
1247 self.wvfs.rmtree(forcibly=True)
1260 self.wvfs.rmtree(forcibly=True)
1248 try:
1261 try:
1249 pwvfs = self._ctx.repo().wvfs
1262 pwvfs = self._ctx.repo().wvfs
1250 pwvfs.removedirs(pwvfs.dirname(self._path))
1263 pwvfs.removedirs(pwvfs.dirname(self._path))
1251 except OSError:
1264 except OSError:
1252 pass
1265 pass
1253
1266
1254 @annotatesubrepoerror
1267 @annotatesubrepoerror
1255 def get(self, state, overwrite=False):
1268 def get(self, state, overwrite=False):
1256 if overwrite:
1269 if overwrite:
1257 self._svncommand(['revert', '--recursive'])
1270 self._svncommand(['revert', '--recursive'])
1258 args = ['checkout']
1271 args = ['checkout']
1259 if self._svnversion >= (1, 5):
1272 if self._svnversion >= (1, 5):
1260 args.append('--force')
1273 args.append('--force')
1261 # The revision must be specified at the end of the URL to properly
1274 # The revision must be specified at the end of the URL to properly
1262 # update to a directory which has since been deleted and recreated.
1275 # update to a directory which has since been deleted and recreated.
1263 args.append('%s@%s' % (state[0], state[1]))
1276 args.append('%s@%s' % (state[0], state[1]))
1264 status, err = self._svncommand(args, failok=True)
1277 status, err = self._svncommand(args, failok=True)
1265 _sanitize(self.ui, self.wvfs, '.svn')
1278 _sanitize(self.ui, self.wvfs, '.svn')
1266 if not re.search('Checked out revision [0-9]+.', status):
1279 if not re.search('Checked out revision [0-9]+.', status):
1267 if ('is already a working copy for a different URL' in err
1280 if ('is already a working copy for a different URL' in err
1268 and (self._wcchanged()[:2] == (False, False))):
1281 and (self._wcchanged()[:2] == (False, False))):
1269 # obstructed but clean working copy, so just blow it away.
1282 # obstructed but clean working copy, so just blow it away.
1270 self.remove()
1283 self.remove()
1271 self.get(state, overwrite=False)
1284 self.get(state, overwrite=False)
1272 return
1285 return
1273 raise error.Abort((status or err).splitlines()[-1])
1286 raise error.Abort((status or err).splitlines()[-1])
1274 self.ui.status(status)
1287 self.ui.status(status)
1275
1288
1276 @annotatesubrepoerror
1289 @annotatesubrepoerror
1277 def merge(self, state):
1290 def merge(self, state):
1278 old = self._state[1]
1291 old = self._state[1]
1279 new = state[1]
1292 new = state[1]
1280 wcrev = self._wcrev()
1293 wcrev = self._wcrev()
1281 if new != wcrev:
1294 if new != wcrev:
1282 dirty = old == wcrev or self._wcchanged()[0]
1295 dirty = old == wcrev or self._wcchanged()[0]
1283 if _updateprompt(self.ui, self, dirty, wcrev, new):
1296 if _updateprompt(self.ui, self, dirty, wcrev, new):
1284 self.get(state, False)
1297 self.get(state, False)
1285
1298
1286 def push(self, opts):
1299 def push(self, opts):
1287 # push is a no-op for SVN
1300 # push is a no-op for SVN
1288 return True
1301 return True
1289
1302
1290 @annotatesubrepoerror
1303 @annotatesubrepoerror
1291 def files(self):
1304 def files(self):
1292 output = self._svncommand(['list', '--recursive', '--xml'])[0]
1305 output = self._svncommand(['list', '--recursive', '--xml'])[0]
1293 doc = xml.dom.minidom.parseString(output)
1306 doc = xml.dom.minidom.parseString(output)
1294 paths = []
1307 paths = []
1295 for e in doc.getElementsByTagName('entry'):
1308 for e in doc.getElementsByTagName('entry'):
1296 kind = str(e.getAttribute('kind'))
1309 kind = str(e.getAttribute('kind'))
1297 if kind != 'file':
1310 if kind != 'file':
1298 continue
1311 continue
1299 name = ''.join(c.data for c
1312 name = ''.join(c.data for c
1300 in e.getElementsByTagName('name')[0].childNodes
1313 in e.getElementsByTagName('name')[0].childNodes
1301 if c.nodeType == c.TEXT_NODE)
1314 if c.nodeType == c.TEXT_NODE)
1302 paths.append(name.encode('utf-8'))
1315 paths.append(name.encode('utf-8'))
1303 return paths
1316 return paths
1304
1317
1305 def filedata(self, name, decode):
1318 def filedata(self, name, decode):
1306 return self._svncommand(['cat'], name)[0]
1319 return self._svncommand(['cat'], name)[0]
1307
1320
1308
1321
1309 class gitsubrepo(abstractsubrepo):
1322 class gitsubrepo(abstractsubrepo):
1310 def __init__(self, ctx, path, state, allowcreate):
1323 def __init__(self, ctx, path, state, allowcreate):
1311 super(gitsubrepo, self).__init__(ctx, path)
1324 super(gitsubrepo, self).__init__(ctx, path)
1312 self._state = state
1325 self._state = state
1313 self._abspath = ctx.repo().wjoin(path)
1326 self._abspath = ctx.repo().wjoin(path)
1314 self._subparent = ctx.repo()
1327 self._subparent = ctx.repo()
1315 self._ensuregit()
1328 self._ensuregit()
1316
1329
1317 def _ensuregit(self):
1330 def _ensuregit(self):
1318 try:
1331 try:
1319 self._gitexecutable = 'git'
1332 self._gitexecutable = 'git'
1320 out, err = self._gitnodir(['--version'])
1333 out, err = self._gitnodir(['--version'])
1321 except OSError as e:
1334 except OSError as e:
1322 genericerror = _("error executing git for subrepo '%s': %s")
1335 genericerror = _("error executing git for subrepo '%s': %s")
1323 notfoundhint = _("check git is installed and in your PATH")
1336 notfoundhint = _("check git is installed and in your PATH")
1324 if e.errno != errno.ENOENT:
1337 if e.errno != errno.ENOENT:
1325 raise error.Abort(genericerror % (self._path, e.strerror))
1338 raise error.Abort(genericerror % (self._path, e.strerror))
1326 elif pycompat.osname == 'nt':
1339 elif pycompat.osname == 'nt':
1327 try:
1340 try:
1328 self._gitexecutable = 'git.cmd'
1341 self._gitexecutable = 'git.cmd'
1329 out, err = self._gitnodir(['--version'])
1342 out, err = self._gitnodir(['--version'])
1330 except OSError as e2:
1343 except OSError as e2:
1331 if e2.errno == errno.ENOENT:
1344 if e2.errno == errno.ENOENT:
1332 raise error.Abort(_("couldn't find 'git' or 'git.cmd'"
1345 raise error.Abort(_("couldn't find 'git' or 'git.cmd'"
1333 " for subrepo '%s'") % self._path,
1346 " for subrepo '%s'") % self._path,
1334 hint=notfoundhint)
1347 hint=notfoundhint)
1335 else:
1348 else:
1336 raise error.Abort(genericerror % (self._path,
1349 raise error.Abort(genericerror % (self._path,
1337 e2.strerror))
1350 e2.strerror))
1338 else:
1351 else:
1339 raise error.Abort(_("couldn't find git for subrepo '%s'")
1352 raise error.Abort(_("couldn't find git for subrepo '%s'")
1340 % self._path, hint=notfoundhint)
1353 % self._path, hint=notfoundhint)
1341 versionstatus = self._checkversion(out)
1354 versionstatus = self._checkversion(out)
1342 if versionstatus == 'unknown':
1355 if versionstatus == 'unknown':
1343 self.ui.warn(_('cannot retrieve git version\n'))
1356 self.ui.warn(_('cannot retrieve git version\n'))
1344 elif versionstatus == 'abort':
1357 elif versionstatus == 'abort':
1345 raise error.Abort(_('git subrepo requires at least 1.6.0 or later'))
1358 raise error.Abort(_('git subrepo requires at least 1.6.0 or later'))
1346 elif versionstatus == 'warning':
1359 elif versionstatus == 'warning':
1347 self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
1360 self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
1348
1361
1349 @staticmethod
1362 @staticmethod
1350 def _gitversion(out):
1363 def _gitversion(out):
1351 m = re.search(r'^git version (\d+)\.(\d+)\.(\d+)', out)
1364 m = re.search(r'^git version (\d+)\.(\d+)\.(\d+)', out)
1352 if m:
1365 if m:
1353 return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
1366 return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
1354
1367
1355 m = re.search(r'^git version (\d+)\.(\d+)', out)
1368 m = re.search(r'^git version (\d+)\.(\d+)', out)
1356 if m:
1369 if m:
1357 return (int(m.group(1)), int(m.group(2)), 0)
1370 return (int(m.group(1)), int(m.group(2)), 0)
1358
1371
1359 return -1
1372 return -1
1360
1373
1361 @staticmethod
1374 @staticmethod
1362 def _checkversion(out):
1375 def _checkversion(out):
1363 '''ensure git version is new enough
1376 '''ensure git version is new enough
1364
1377
1365 >>> _checkversion = gitsubrepo._checkversion
1378 >>> _checkversion = gitsubrepo._checkversion
1366 >>> _checkversion('git version 1.6.0')
1379 >>> _checkversion('git version 1.6.0')
1367 'ok'
1380 'ok'
1368 >>> _checkversion('git version 1.8.5')
1381 >>> _checkversion('git version 1.8.5')
1369 'ok'
1382 'ok'
1370 >>> _checkversion('git version 1.4.0')
1383 >>> _checkversion('git version 1.4.0')
1371 'abort'
1384 'abort'
1372 >>> _checkversion('git version 1.5.0')
1385 >>> _checkversion('git version 1.5.0')
1373 'warning'
1386 'warning'
1374 >>> _checkversion('git version 1.9-rc0')
1387 >>> _checkversion('git version 1.9-rc0')
1375 'ok'
1388 'ok'
1376 >>> _checkversion('git version 1.9.0.265.g81cdec2')
1389 >>> _checkversion('git version 1.9.0.265.g81cdec2')
1377 'ok'
1390 'ok'
1378 >>> _checkversion('git version 1.9.0.GIT')
1391 >>> _checkversion('git version 1.9.0.GIT')
1379 'ok'
1392 'ok'
1380 >>> _checkversion('git version 12345')
1393 >>> _checkversion('git version 12345')
1381 'unknown'
1394 'unknown'
1382 >>> _checkversion('no')
1395 >>> _checkversion('no')
1383 'unknown'
1396 'unknown'
1384 '''
1397 '''
1385 version = gitsubrepo._gitversion(out)
1398 version = gitsubrepo._gitversion(out)
1386 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
1399 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
1387 # despite the docstring comment. For now, error on 1.4.0, warn on
1400 # despite the docstring comment. For now, error on 1.4.0, warn on
1388 # 1.5.0 but attempt to continue.
1401 # 1.5.0 but attempt to continue.
1389 if version == -1:
1402 if version == -1:
1390 return 'unknown'
1403 return 'unknown'
1391 if version < (1, 5, 0):
1404 if version < (1, 5, 0):
1392 return 'abort'
1405 return 'abort'
1393 elif version < (1, 6, 0):
1406 elif version < (1, 6, 0):
1394 return 'warning'
1407 return 'warning'
1395 return 'ok'
1408 return 'ok'
1396
1409
1397 def _gitcommand(self, commands, env=None, stream=False):
1410 def _gitcommand(self, commands, env=None, stream=False):
1398 return self._gitdir(commands, env=env, stream=stream)[0]
1411 return self._gitdir(commands, env=env, stream=stream)[0]
1399
1412
1400 def _gitdir(self, commands, env=None, stream=False):
1413 def _gitdir(self, commands, env=None, stream=False):
1401 return self._gitnodir(commands, env=env, stream=stream,
1414 return self._gitnodir(commands, env=env, stream=stream,
1402 cwd=self._abspath)
1415 cwd=self._abspath)
1403
1416
1404 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
1417 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
1405 """Calls the git command
1418 """Calls the git command
1406
1419
1407 The methods tries to call the git command. versions prior to 1.6.0
1420 The methods tries to call the git command. versions prior to 1.6.0
1408 are not supported and very probably fail.
1421 are not supported and very probably fail.
1409 """
1422 """
1410 self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
1423 self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
1411 if env is None:
1424 if env is None:
1412 env = encoding.environ.copy()
1425 env = encoding.environ.copy()
1413 # disable localization for Git output (issue5176)
1426 # disable localization for Git output (issue5176)
1414 env['LC_ALL'] = 'C'
1427 env['LC_ALL'] = 'C'
1415 # fix for Git CVE-2015-7545
1428 # fix for Git CVE-2015-7545
1416 if 'GIT_ALLOW_PROTOCOL' not in env:
1429 if 'GIT_ALLOW_PROTOCOL' not in env:
1417 env['GIT_ALLOW_PROTOCOL'] = 'file:git:http:https:ssh'
1430 env['GIT_ALLOW_PROTOCOL'] = 'file:git:http:https:ssh'
1418 # unless ui.quiet is set, print git's stderr,
1431 # unless ui.quiet is set, print git's stderr,
1419 # which is mostly progress and useful info
1432 # which is mostly progress and useful info
1420 errpipe = None
1433 errpipe = None
1421 if self.ui.quiet:
1434 if self.ui.quiet:
1422 errpipe = open(os.devnull, 'w')
1435 errpipe = open(os.devnull, 'w')
1423 if self.ui._colormode and len(commands) and commands[0] == "diff":
1436 if self.ui._colormode and len(commands) and commands[0] == "diff":
1424 # insert the argument in the front,
1437 # insert the argument in the front,
1425 # the end of git diff arguments is used for paths
1438 # the end of git diff arguments is used for paths
1426 commands.insert(1, '--color')
1439 commands.insert(1, '--color')
1427 p = subprocess.Popen([self._gitexecutable] + commands, bufsize=-1,
1440 p = subprocess.Popen([self._gitexecutable] + commands, bufsize=-1,
1428 cwd=cwd, env=env, close_fds=util.closefds,
1441 cwd=cwd, env=env, close_fds=util.closefds,
1429 stdout=subprocess.PIPE, stderr=errpipe)
1442 stdout=subprocess.PIPE, stderr=errpipe)
1430 if stream:
1443 if stream:
1431 return p.stdout, None
1444 return p.stdout, None
1432
1445
1433 retdata = p.stdout.read().strip()
1446 retdata = p.stdout.read().strip()
1434 # wait for the child to exit to avoid race condition.
1447 # wait for the child to exit to avoid race condition.
1435 p.wait()
1448 p.wait()
1436
1449
1437 if p.returncode != 0 and p.returncode != 1:
1450 if p.returncode != 0 and p.returncode != 1:
1438 # there are certain error codes that are ok
1451 # there are certain error codes that are ok
1439 command = commands[0]
1452 command = commands[0]
1440 if command in ('cat-file', 'symbolic-ref'):
1453 if command in ('cat-file', 'symbolic-ref'):
1441 return retdata, p.returncode
1454 return retdata, p.returncode
1442 # for all others, abort
1455 # for all others, abort
1443 raise error.Abort(_('git %s error %d in %s') %
1456 raise error.Abort(_('git %s error %d in %s') %
1444 (command, p.returncode, self._relpath))
1457 (command, p.returncode, self._relpath))
1445
1458
1446 return retdata, p.returncode
1459 return retdata, p.returncode
1447
1460
1448 def _gitmissing(self):
1461 def _gitmissing(self):
1449 return not self.wvfs.exists('.git')
1462 return not self.wvfs.exists('.git')
1450
1463
1451 def _gitstate(self):
1464 def _gitstate(self):
1452 return self._gitcommand(['rev-parse', 'HEAD'])
1465 return self._gitcommand(['rev-parse', 'HEAD'])
1453
1466
1454 def _gitcurrentbranch(self):
1467 def _gitcurrentbranch(self):
1455 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
1468 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
1456 if err:
1469 if err:
1457 current = None
1470 current = None
1458 return current
1471 return current
1459
1472
1460 def _gitremote(self, remote):
1473 def _gitremote(self, remote):
1461 out = self._gitcommand(['remote', 'show', '-n', remote])
1474 out = self._gitcommand(['remote', 'show', '-n', remote])
1462 line = out.split('\n')[1]
1475 line = out.split('\n')[1]
1463 i = line.index('URL: ') + len('URL: ')
1476 i = line.index('URL: ') + len('URL: ')
1464 return line[i:]
1477 return line[i:]
1465
1478
1466 def _githavelocally(self, revision):
1479 def _githavelocally(self, revision):
1467 out, code = self._gitdir(['cat-file', '-e', revision])
1480 out, code = self._gitdir(['cat-file', '-e', revision])
1468 return code == 0
1481 return code == 0
1469
1482
1470 def _gitisancestor(self, r1, r2):
1483 def _gitisancestor(self, r1, r2):
1471 base = self._gitcommand(['merge-base', r1, r2])
1484 base = self._gitcommand(['merge-base', r1, r2])
1472 return base == r1
1485 return base == r1
1473
1486
1474 def _gitisbare(self):
1487 def _gitisbare(self):
1475 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
1488 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
1476
1489
1477 def _gitupdatestat(self):
1490 def _gitupdatestat(self):
1478 """This must be run before git diff-index.
1491 """This must be run before git diff-index.
1479 diff-index only looks at changes to file stat;
1492 diff-index only looks at changes to file stat;
1480 this command looks at file contents and updates the stat."""
1493 this command looks at file contents and updates the stat."""
1481 self._gitcommand(['update-index', '-q', '--refresh'])
1494 self._gitcommand(['update-index', '-q', '--refresh'])
1482
1495
1483 def _gitbranchmap(self):
1496 def _gitbranchmap(self):
1484 '''returns 2 things:
1497 '''returns 2 things:
1485 a map from git branch to revision
1498 a map from git branch to revision
1486 a map from revision to branches'''
1499 a map from revision to branches'''
1487 branch2rev = {}
1500 branch2rev = {}
1488 rev2branch = {}
1501 rev2branch = {}
1489
1502
1490 out = self._gitcommand(['for-each-ref', '--format',
1503 out = self._gitcommand(['for-each-ref', '--format',
1491 '%(objectname) %(refname)'])
1504 '%(objectname) %(refname)'])
1492 for line in out.split('\n'):
1505 for line in out.split('\n'):
1493 revision, ref = line.split(' ')
1506 revision, ref = line.split(' ')
1494 if (not ref.startswith('refs/heads/') and
1507 if (not ref.startswith('refs/heads/') and
1495 not ref.startswith('refs/remotes/')):
1508 not ref.startswith('refs/remotes/')):
1496 continue
1509 continue
1497 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
1510 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
1498 continue # ignore remote/HEAD redirects
1511 continue # ignore remote/HEAD redirects
1499 branch2rev[ref] = revision
1512 branch2rev[ref] = revision
1500 rev2branch.setdefault(revision, []).append(ref)
1513 rev2branch.setdefault(revision, []).append(ref)
1501 return branch2rev, rev2branch
1514 return branch2rev, rev2branch
1502
1515
1503 def _gittracking(self, branches):
1516 def _gittracking(self, branches):
1504 'return map of remote branch to local tracking branch'
1517 'return map of remote branch to local tracking branch'
1505 # assumes no more than one local tracking branch for each remote
1518 # assumes no more than one local tracking branch for each remote
1506 tracking = {}
1519 tracking = {}
1507 for b in branches:
1520 for b in branches:
1508 if b.startswith('refs/remotes/'):
1521 if b.startswith('refs/remotes/'):
1509 continue
1522 continue
1510 bname = b.split('/', 2)[2]
1523 bname = b.split('/', 2)[2]
1511 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
1524 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
1512 if remote:
1525 if remote:
1513 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
1526 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
1514 tracking['refs/remotes/%s/%s' %
1527 tracking['refs/remotes/%s/%s' %
1515 (remote, ref.split('/', 2)[2])] = b
1528 (remote, ref.split('/', 2)[2])] = b
1516 return tracking
1529 return tracking
1517
1530
1518 def _abssource(self, source):
1531 def _abssource(self, source):
1519 if '://' not in source:
1532 if '://' not in source:
1520 # recognize the scp syntax as an absolute source
1533 # recognize the scp syntax as an absolute source
1521 colon = source.find(':')
1534 colon = source.find(':')
1522 if colon != -1 and '/' not in source[:colon]:
1535 if colon != -1 and '/' not in source[:colon]:
1523 return source
1536 return source
1524 self._subsource = source
1537 self._subsource = source
1525 return _abssource(self)
1538 return _abssource(self)
1526
1539
1527 def _fetch(self, source, revision):
1540 def _fetch(self, source, revision):
1528 if self._gitmissing():
1541 if self._gitmissing():
1529 source = self._abssource(source)
1542 source = self._abssource(source)
1530 self.ui.status(_('cloning subrepo %s from %s\n') %
1543 self.ui.status(_('cloning subrepo %s from %s\n') %
1531 (self._relpath, source))
1544 (self._relpath, source))
1532 self._gitnodir(['clone', source, self._abspath])
1545 self._gitnodir(['clone', source, self._abspath])
1533 if self._githavelocally(revision):
1546 if self._githavelocally(revision):
1534 return
1547 return
1535 self.ui.status(_('pulling subrepo %s from %s\n') %
1548 self.ui.status(_('pulling subrepo %s from %s\n') %
1536 (self._relpath, self._gitremote('origin')))
1549 (self._relpath, self._gitremote('origin')))
1537 # try only origin: the originally cloned repo
1550 # try only origin: the originally cloned repo
1538 self._gitcommand(['fetch'])
1551 self._gitcommand(['fetch'])
1539 if not self._githavelocally(revision):
1552 if not self._githavelocally(revision):
1540 raise error.Abort(_("revision %s does not exist in subrepo %s\n") %
1553 raise error.Abort(_("revision %s does not exist in subrepo %s\n") %
1541 (revision, self._relpath))
1554 (revision, self._relpath))
1542
1555
1543 @annotatesubrepoerror
1556 @annotatesubrepoerror
1544 def dirty(self, ignoreupdate=False):
1557 def dirty(self, ignoreupdate=False):
1545 if self._gitmissing():
1558 if self._gitmissing():
1546 return self._state[1] != ''
1559 return self._state[1] != ''
1547 if self._gitisbare():
1560 if self._gitisbare():
1548 return True
1561 return True
1549 if not ignoreupdate and self._state[1] != self._gitstate():
1562 if not ignoreupdate and self._state[1] != self._gitstate():
1550 # different version checked out
1563 # different version checked out
1551 return True
1564 return True
1552 # check for staged changes or modified files; ignore untracked files
1565 # check for staged changes or modified files; ignore untracked files
1553 self._gitupdatestat()
1566 self._gitupdatestat()
1554 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1567 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1555 return code == 1
1568 return code == 1
1556
1569
1557 def basestate(self):
1570 def basestate(self):
1558 return self._gitstate()
1571 return self._gitstate()
1559
1572
1560 @annotatesubrepoerror
1573 @annotatesubrepoerror
1561 def get(self, state, overwrite=False):
1574 def get(self, state, overwrite=False):
1562 source, revision, kind = state
1575 source, revision, kind = state
1563 if not revision:
1576 if not revision:
1564 self.remove()
1577 self.remove()
1565 return
1578 return
1566 self._fetch(source, revision)
1579 self._fetch(source, revision)
1567 # if the repo was set to be bare, unbare it
1580 # if the repo was set to be bare, unbare it
1568 if self._gitisbare():
1581 if self._gitisbare():
1569 self._gitcommand(['config', 'core.bare', 'false'])
1582 self._gitcommand(['config', 'core.bare', 'false'])
1570 if self._gitstate() == revision:
1583 if self._gitstate() == revision:
1571 self._gitcommand(['reset', '--hard', 'HEAD'])
1584 self._gitcommand(['reset', '--hard', 'HEAD'])
1572 return
1585 return
1573 elif self._gitstate() == revision:
1586 elif self._gitstate() == revision:
1574 if overwrite:
1587 if overwrite:
1575 # first reset the index to unmark new files for commit, because
1588 # first reset the index to unmark new files for commit, because
1576 # reset --hard will otherwise throw away files added for commit,
1589 # reset --hard will otherwise throw away files added for commit,
1577 # not just unmark them.
1590 # not just unmark them.
1578 self._gitcommand(['reset', 'HEAD'])
1591 self._gitcommand(['reset', 'HEAD'])
1579 self._gitcommand(['reset', '--hard', 'HEAD'])
1592 self._gitcommand(['reset', '--hard', 'HEAD'])
1580 return
1593 return
1581 branch2rev, rev2branch = self._gitbranchmap()
1594 branch2rev, rev2branch = self._gitbranchmap()
1582
1595
1583 def checkout(args):
1596 def checkout(args):
1584 cmd = ['checkout']
1597 cmd = ['checkout']
1585 if overwrite:
1598 if overwrite:
1586 # first reset the index to unmark new files for commit, because
1599 # first reset the index to unmark new files for commit, because
1587 # the -f option will otherwise throw away files added for
1600 # the -f option will otherwise throw away files added for
1588 # commit, not just unmark them.
1601 # commit, not just unmark them.
1589 self._gitcommand(['reset', 'HEAD'])
1602 self._gitcommand(['reset', 'HEAD'])
1590 cmd.append('-f')
1603 cmd.append('-f')
1591 self._gitcommand(cmd + args)
1604 self._gitcommand(cmd + args)
1592 _sanitize(self.ui, self.wvfs, '.git')
1605 _sanitize(self.ui, self.wvfs, '.git')
1593
1606
1594 def rawcheckout():
1607 def rawcheckout():
1595 # no branch to checkout, check it out with no branch
1608 # no branch to checkout, check it out with no branch
1596 self.ui.warn(_('checking out detached HEAD in subrepo %s\n') %
1609 self.ui.warn(_('checking out detached HEAD in subrepo %s\n') %
1597 self._relpath)
1610 self._relpath)
1598 self.ui.warn(_('check out a git branch if you intend '
1611 self.ui.warn(_('check out a git branch if you intend '
1599 'to make changes\n'))
1612 'to make changes\n'))
1600 checkout(['-q', revision])
1613 checkout(['-q', revision])
1601
1614
1602 if revision not in rev2branch:
1615 if revision not in rev2branch:
1603 rawcheckout()
1616 rawcheckout()
1604 return
1617 return
1605 branches = rev2branch[revision]
1618 branches = rev2branch[revision]
1606 firstlocalbranch = None
1619 firstlocalbranch = None
1607 for b in branches:
1620 for b in branches:
1608 if b == 'refs/heads/master':
1621 if b == 'refs/heads/master':
1609 # master trumps all other branches
1622 # master trumps all other branches
1610 checkout(['refs/heads/master'])
1623 checkout(['refs/heads/master'])
1611 return
1624 return
1612 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1625 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1613 firstlocalbranch = b
1626 firstlocalbranch = b
1614 if firstlocalbranch:
1627 if firstlocalbranch:
1615 checkout([firstlocalbranch])
1628 checkout([firstlocalbranch])
1616 return
1629 return
1617
1630
1618 tracking = self._gittracking(branch2rev.keys())
1631 tracking = self._gittracking(branch2rev.keys())
1619 # choose a remote branch already tracked if possible
1632 # choose a remote branch already tracked if possible
1620 remote = branches[0]
1633 remote = branches[0]
1621 if remote not in tracking:
1634 if remote not in tracking:
1622 for b in branches:
1635 for b in branches:
1623 if b in tracking:
1636 if b in tracking:
1624 remote = b
1637 remote = b
1625 break
1638 break
1626
1639
1627 if remote not in tracking:
1640 if remote not in tracking:
1628 # create a new local tracking branch
1641 # create a new local tracking branch
1629 local = remote.split('/', 3)[3]
1642 local = remote.split('/', 3)[3]
1630 checkout(['-b', local, remote])
1643 checkout(['-b', local, remote])
1631 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1644 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1632 # When updating to a tracked remote branch,
1645 # When updating to a tracked remote branch,
1633 # if the local tracking branch is downstream of it,
1646 # if the local tracking branch is downstream of it,
1634 # a normal `git pull` would have performed a "fast-forward merge"
1647 # a normal `git pull` would have performed a "fast-forward merge"
1635 # which is equivalent to updating the local branch to the remote.
1648 # which is equivalent to updating the local branch to the remote.
1636 # Since we are only looking at branching at update, we need to
1649 # Since we are only looking at branching at update, we need to
1637 # detect this situation and perform this action lazily.
1650 # detect this situation and perform this action lazily.
1638 if tracking[remote] != self._gitcurrentbranch():
1651 if tracking[remote] != self._gitcurrentbranch():
1639 checkout([tracking[remote]])
1652 checkout([tracking[remote]])
1640 self._gitcommand(['merge', '--ff', remote])
1653 self._gitcommand(['merge', '--ff', remote])
1641 _sanitize(self.ui, self.wvfs, '.git')
1654 _sanitize(self.ui, self.wvfs, '.git')
1642 else:
1655 else:
1643 # a real merge would be required, just checkout the revision
1656 # a real merge would be required, just checkout the revision
1644 rawcheckout()
1657 rawcheckout()
1645
1658
1646 @annotatesubrepoerror
1659 @annotatesubrepoerror
1647 def commit(self, text, user, date):
1660 def commit(self, text, user, date):
1648 if self._gitmissing():
1661 if self._gitmissing():
1649 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1662 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1650 cmd = ['commit', '-a', '-m', text]
1663 cmd = ['commit', '-a', '-m', text]
1651 env = encoding.environ.copy()
1664 env = encoding.environ.copy()
1652 if user:
1665 if user:
1653 cmd += ['--author', user]
1666 cmd += ['--author', user]
1654 if date:
1667 if date:
1655 # git's date parser silently ignores when seconds < 1e9
1668 # git's date parser silently ignores when seconds < 1e9
1656 # convert to ISO8601
1669 # convert to ISO8601
1657 env['GIT_AUTHOR_DATE'] = util.datestr(date,
1670 env['GIT_AUTHOR_DATE'] = util.datestr(date,
1658 '%Y-%m-%dT%H:%M:%S %1%2')
1671 '%Y-%m-%dT%H:%M:%S %1%2')
1659 self._gitcommand(cmd, env=env)
1672 self._gitcommand(cmd, env=env)
1660 # make sure commit works otherwise HEAD might not exist under certain
1673 # make sure commit works otherwise HEAD might not exist under certain
1661 # circumstances
1674 # circumstances
1662 return self._gitstate()
1675 return self._gitstate()
1663
1676
1664 @annotatesubrepoerror
1677 @annotatesubrepoerror
1665 def merge(self, state):
1678 def merge(self, state):
1666 source, revision, kind = state
1679 source, revision, kind = state
1667 self._fetch(source, revision)
1680 self._fetch(source, revision)
1668 base = self._gitcommand(['merge-base', revision, self._state[1]])
1681 base = self._gitcommand(['merge-base', revision, self._state[1]])
1669 self._gitupdatestat()
1682 self._gitupdatestat()
1670 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1683 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1671
1684
1672 def mergefunc():
1685 def mergefunc():
1673 if base == revision:
1686 if base == revision:
1674 self.get(state) # fast forward merge
1687 self.get(state) # fast forward merge
1675 elif base != self._state[1]:
1688 elif base != self._state[1]:
1676 self._gitcommand(['merge', '--no-commit', revision])
1689 self._gitcommand(['merge', '--no-commit', revision])
1677 _sanitize(self.ui, self.wvfs, '.git')
1690 _sanitize(self.ui, self.wvfs, '.git')
1678
1691
1679 if self.dirty():
1692 if self.dirty():
1680 if self._gitstate() != revision:
1693 if self._gitstate() != revision:
1681 dirty = self._gitstate() == self._state[1] or code != 0
1694 dirty = self._gitstate() == self._state[1] or code != 0
1682 if _updateprompt(self.ui, self, dirty,
1695 if _updateprompt(self.ui, self, dirty,
1683 self._state[1][:7], revision[:7]):
1696 self._state[1][:7], revision[:7]):
1684 mergefunc()
1697 mergefunc()
1685 else:
1698 else:
1686 mergefunc()
1699 mergefunc()
1687
1700
1688 @annotatesubrepoerror
1701 @annotatesubrepoerror
1689 def push(self, opts):
1702 def push(self, opts):
1690 force = opts.get('force')
1703 force = opts.get('force')
1691
1704
1692 if not self._state[1]:
1705 if not self._state[1]:
1693 return True
1706 return True
1694 if self._gitmissing():
1707 if self._gitmissing():
1695 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1708 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1696 # if a branch in origin contains the revision, nothing to do
1709 # if a branch in origin contains the revision, nothing to do
1697 branch2rev, rev2branch = self._gitbranchmap()
1710 branch2rev, rev2branch = self._gitbranchmap()
1698 if self._state[1] in rev2branch:
1711 if self._state[1] in rev2branch:
1699 for b in rev2branch[self._state[1]]:
1712 for b in rev2branch[self._state[1]]:
1700 if b.startswith('refs/remotes/origin/'):
1713 if b.startswith('refs/remotes/origin/'):
1701 return True
1714 return True
1702 for b, revision in branch2rev.iteritems():
1715 for b, revision in branch2rev.iteritems():
1703 if b.startswith('refs/remotes/origin/'):
1716 if b.startswith('refs/remotes/origin/'):
1704 if self._gitisancestor(self._state[1], revision):
1717 if self._gitisancestor(self._state[1], revision):
1705 return True
1718 return True
1706 # otherwise, try to push the currently checked out branch
1719 # otherwise, try to push the currently checked out branch
1707 cmd = ['push']
1720 cmd = ['push']
1708 if force:
1721 if force:
1709 cmd.append('--force')
1722 cmd.append('--force')
1710
1723
1711 current = self._gitcurrentbranch()
1724 current = self._gitcurrentbranch()
1712 if current:
1725 if current:
1713 # determine if the current branch is even useful
1726 # determine if the current branch is even useful
1714 if not self._gitisancestor(self._state[1], current):
1727 if not self._gitisancestor(self._state[1], current):
1715 self.ui.warn(_('unrelated git branch checked out '
1728 self.ui.warn(_('unrelated git branch checked out '
1716 'in subrepo %s\n') % self._relpath)
1729 'in subrepo %s\n') % self._relpath)
1717 return False
1730 return False
1718 self.ui.status(_('pushing branch %s of subrepo %s\n') %
1731 self.ui.status(_('pushing branch %s of subrepo %s\n') %
1719 (current.split('/', 2)[2], self._relpath))
1732 (current.split('/', 2)[2], self._relpath))
1720 ret = self._gitdir(cmd + ['origin', current])
1733 ret = self._gitdir(cmd + ['origin', current])
1721 return ret[1] == 0
1734 return ret[1] == 0
1722 else:
1735 else:
1723 self.ui.warn(_('no branch checked out in subrepo %s\n'
1736 self.ui.warn(_('no branch checked out in subrepo %s\n'
1724 'cannot push revision %s\n') %
1737 'cannot push revision %s\n') %
1725 (self._relpath, self._state[1]))
1738 (self._relpath, self._state[1]))
1726 return False
1739 return False
1727
1740
1728 @annotatesubrepoerror
1741 @annotatesubrepoerror
1729 def add(self, ui, match, prefix, explicitonly, **opts):
1742 def add(self, ui, match, prefix, explicitonly, **opts):
1730 if self._gitmissing():
1743 if self._gitmissing():
1731 return []
1744 return []
1732
1745
1733 (modified, added, removed,
1746 (modified, added, removed,
1734 deleted, unknown, ignored, clean) = self.status(None, unknown=True,
1747 deleted, unknown, ignored, clean) = self.status(None, unknown=True,
1735 clean=True)
1748 clean=True)
1736
1749
1737 tracked = set()
1750 tracked = set()
1738 # dirstates 'amn' warn, 'r' is added again
1751 # dirstates 'amn' warn, 'r' is added again
1739 for l in (modified, added, deleted, clean):
1752 for l in (modified, added, deleted, clean):
1740 tracked.update(l)
1753 tracked.update(l)
1741
1754
1742 # Unknown files not of interest will be rejected by the matcher
1755 # Unknown files not of interest will be rejected by the matcher
1743 files = unknown
1756 files = unknown
1744 files.extend(match.files())
1757 files.extend(match.files())
1745
1758
1746 rejected = []
1759 rejected = []
1747
1760
1748 files = [f for f in sorted(set(files)) if match(f)]
1761 files = [f for f in sorted(set(files)) if match(f)]
1749 for f in files:
1762 for f in files:
1750 exact = match.exact(f)
1763 exact = match.exact(f)
1751 command = ["add"]
1764 command = ["add"]
1752 if exact:
1765 if exact:
1753 command.append("-f") #should be added, even if ignored
1766 command.append("-f") #should be added, even if ignored
1754 if ui.verbose or not exact:
1767 if ui.verbose or not exact:
1755 ui.status(_('adding %s\n') % match.rel(f))
1768 ui.status(_('adding %s\n') % match.rel(f))
1756
1769
1757 if f in tracked: # hg prints 'adding' even if already tracked
1770 if f in tracked: # hg prints 'adding' even if already tracked
1758 if exact:
1771 if exact:
1759 rejected.append(f)
1772 rejected.append(f)
1760 continue
1773 continue
1761 if not opts.get('dry_run'):
1774 if not opts.get('dry_run'):
1762 self._gitcommand(command + [f])
1775 self._gitcommand(command + [f])
1763
1776
1764 for f in rejected:
1777 for f in rejected:
1765 ui.warn(_("%s already tracked!\n") % match.abs(f))
1778 ui.warn(_("%s already tracked!\n") % match.abs(f))
1766
1779
1767 return rejected
1780 return rejected
1768
1781
1769 @annotatesubrepoerror
1782 @annotatesubrepoerror
1770 def remove(self):
1783 def remove(self):
1771 if self._gitmissing():
1784 if self._gitmissing():
1772 return
1785 return
1773 if self.dirty():
1786 if self.dirty():
1774 self.ui.warn(_('not removing repo %s because '
1787 self.ui.warn(_('not removing repo %s because '
1775 'it has changes.\n') % self._relpath)
1788 'it has changes.\n') % self._relpath)
1776 return
1789 return
1777 # we can't fully delete the repository as it may contain
1790 # we can't fully delete the repository as it may contain
1778 # local-only history
1791 # local-only history
1779 self.ui.note(_('removing subrepo %s\n') % self._relpath)
1792 self.ui.note(_('removing subrepo %s\n') % self._relpath)
1780 self._gitcommand(['config', 'core.bare', 'true'])
1793 self._gitcommand(['config', 'core.bare', 'true'])
1781 for f, kind in self.wvfs.readdir():
1794 for f, kind in self.wvfs.readdir():
1782 if f == '.git':
1795 if f == '.git':
1783 continue
1796 continue
1784 if kind == stat.S_IFDIR:
1797 if kind == stat.S_IFDIR:
1785 self.wvfs.rmtree(f)
1798 self.wvfs.rmtree(f)
1786 else:
1799 else:
1787 self.wvfs.unlink(f)
1800 self.wvfs.unlink(f)
1788
1801
1789 def archive(self, archiver, prefix, match=None, decode=True):
1802 def archive(self, archiver, prefix, match=None, decode=True):
1790 total = 0
1803 total = 0
1791 source, revision = self._state
1804 source, revision = self._state
1792 if not revision:
1805 if not revision:
1793 return total
1806 return total
1794 self._fetch(source, revision)
1807 self._fetch(source, revision)
1795
1808
1796 # Parse git's native archive command.
1809 # Parse git's native archive command.
1797 # This should be much faster than manually traversing the trees
1810 # This should be much faster than manually traversing the trees
1798 # and objects with many subprocess calls.
1811 # and objects with many subprocess calls.
1799 tarstream = self._gitcommand(['archive', revision], stream=True)
1812 tarstream = self._gitcommand(['archive', revision], stream=True)
1800 tar = tarfile.open(fileobj=tarstream, mode='r|')
1813 tar = tarfile.open(fileobj=tarstream, mode='r|')
1801 relpath = subrelpath(self)
1814 relpath = subrelpath(self)
1802 self.ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files'))
1815 self.ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files'))
1803 for i, info in enumerate(tar):
1816 for i, info in enumerate(tar):
1804 if info.isdir():
1817 if info.isdir():
1805 continue
1818 continue
1806 if match and not match(info.name):
1819 if match and not match(info.name):
1807 continue
1820 continue
1808 if info.issym():
1821 if info.issym():
1809 data = info.linkname
1822 data = info.linkname
1810 else:
1823 else:
1811 data = tar.extractfile(info).read()
1824 data = tar.extractfile(info).read()
1812 archiver.addfile(prefix + self._path + '/' + info.name,
1825 archiver.addfile(prefix + self._path + '/' + info.name,
1813 info.mode, info.issym(), data)
1826 info.mode, info.issym(), data)
1814 total += 1
1827 total += 1
1815 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
1828 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
1816 unit=_('files'))
1829 unit=_('files'))
1817 self.ui.progress(_('archiving (%s)') % relpath, None)
1830 self.ui.progress(_('archiving (%s)') % relpath, None)
1818 return total
1831 return total
1819
1832
1820
1833
1821 @annotatesubrepoerror
1834 @annotatesubrepoerror
1822 def cat(self, match, prefix, **opts):
1835 def cat(self, match, prefix, **opts):
1823 rev = self._state[1]
1836 rev = self._state[1]
1824 if match.anypats():
1837 if match.anypats():
1825 return 1 #No support for include/exclude yet
1838 return 1 #No support for include/exclude yet
1826
1839
1827 if not match.files():
1840 if not match.files():
1828 return 1
1841 return 1
1829
1842
1830 for f in match.files():
1843 for f in match.files():
1831 output = self._gitcommand(["show", "%s:%s" % (rev, f)])
1844 output = self._gitcommand(["show", "%s:%s" % (rev, f)])
1832 fp = cmdutil.makefileobj(self._subparent, opts.get('output'),
1845 fp = cmdutil.makefileobj(self._subparent, opts.get('output'),
1833 self._ctx.node(),
1846 self._ctx.node(),
1834 pathname=self.wvfs.reljoin(prefix, f))
1847 pathname=self.wvfs.reljoin(prefix, f))
1835 fp.write(output)
1848 fp.write(output)
1836 fp.close()
1849 fp.close()
1837 return 0
1850 return 0
1838
1851
1839
1852
1840 @annotatesubrepoerror
1853 @annotatesubrepoerror
1841 def status(self, rev2, **opts):
1854 def status(self, rev2, **opts):
1842 rev1 = self._state[1]
1855 rev1 = self._state[1]
1843 if self._gitmissing() or not rev1:
1856 if self._gitmissing() or not rev1:
1844 # if the repo is missing, return no results
1857 # if the repo is missing, return no results
1845 return scmutil.status([], [], [], [], [], [], [])
1858 return scmutil.status([], [], [], [], [], [], [])
1846 modified, added, removed = [], [], []
1859 modified, added, removed = [], [], []
1847 self._gitupdatestat()
1860 self._gitupdatestat()
1848 if rev2:
1861 if rev2:
1849 command = ['diff-tree', '--no-renames', '-r', rev1, rev2]
1862 command = ['diff-tree', '--no-renames', '-r', rev1, rev2]
1850 else:
1863 else:
1851 command = ['diff-index', '--no-renames', rev1]
1864 command = ['diff-index', '--no-renames', rev1]
1852 out = self._gitcommand(command)
1865 out = self._gitcommand(command)
1853 for line in out.split('\n'):
1866 for line in out.split('\n'):
1854 tab = line.find('\t')
1867 tab = line.find('\t')
1855 if tab == -1:
1868 if tab == -1:
1856 continue
1869 continue
1857 status, f = line[tab - 1], line[tab + 1:]
1870 status, f = line[tab - 1], line[tab + 1:]
1858 if status == 'M':
1871 if status == 'M':
1859 modified.append(f)
1872 modified.append(f)
1860 elif status == 'A':
1873 elif status == 'A':
1861 added.append(f)
1874 added.append(f)
1862 elif status == 'D':
1875 elif status == 'D':
1863 removed.append(f)
1876 removed.append(f)
1864
1877
1865 deleted, unknown, ignored, clean = [], [], [], []
1878 deleted, unknown, ignored, clean = [], [], [], []
1866
1879
1867 command = ['status', '--porcelain', '-z']
1880 command = ['status', '--porcelain', '-z']
1868 if opts.get('unknown'):
1881 if opts.get('unknown'):
1869 command += ['--untracked-files=all']
1882 command += ['--untracked-files=all']
1870 if opts.get('ignored'):
1883 if opts.get('ignored'):
1871 command += ['--ignored']
1884 command += ['--ignored']
1872 out = self._gitcommand(command)
1885 out = self._gitcommand(command)
1873
1886
1874 changedfiles = set()
1887 changedfiles = set()
1875 changedfiles.update(modified)
1888 changedfiles.update(modified)
1876 changedfiles.update(added)
1889 changedfiles.update(added)
1877 changedfiles.update(removed)
1890 changedfiles.update(removed)
1878 for line in out.split('\0'):
1891 for line in out.split('\0'):
1879 if not line:
1892 if not line:
1880 continue
1893 continue
1881 st = line[0:2]
1894 st = line[0:2]
1882 #moves and copies show 2 files on one line
1895 #moves and copies show 2 files on one line
1883 if line.find('\0') >= 0:
1896 if line.find('\0') >= 0:
1884 filename1, filename2 = line[3:].split('\0')
1897 filename1, filename2 = line[3:].split('\0')
1885 else:
1898 else:
1886 filename1 = line[3:]
1899 filename1 = line[3:]
1887 filename2 = None
1900 filename2 = None
1888
1901
1889 changedfiles.add(filename1)
1902 changedfiles.add(filename1)
1890 if filename2:
1903 if filename2:
1891 changedfiles.add(filename2)
1904 changedfiles.add(filename2)
1892
1905
1893 if st == '??':
1906 if st == '??':
1894 unknown.append(filename1)
1907 unknown.append(filename1)
1895 elif st == '!!':
1908 elif st == '!!':
1896 ignored.append(filename1)
1909 ignored.append(filename1)
1897
1910
1898 if opts.get('clean'):
1911 if opts.get('clean'):
1899 out = self._gitcommand(['ls-files'])
1912 out = self._gitcommand(['ls-files'])
1900 for f in out.split('\n'):
1913 for f in out.split('\n'):
1901 if not f in changedfiles:
1914 if not f in changedfiles:
1902 clean.append(f)
1915 clean.append(f)
1903
1916
1904 return scmutil.status(modified, added, removed, deleted,
1917 return scmutil.status(modified, added, removed, deleted,
1905 unknown, ignored, clean)
1918 unknown, ignored, clean)
1906
1919
1907 @annotatesubrepoerror
1920 @annotatesubrepoerror
1908 def diff(self, ui, diffopts, node2, match, prefix, **opts):
1921 def diff(self, ui, diffopts, node2, match, prefix, **opts):
1909 node1 = self._state[1]
1922 node1 = self._state[1]
1910 cmd = ['diff', '--no-renames']
1923 cmd = ['diff', '--no-renames']
1911 if opts['stat']:
1924 if opts['stat']:
1912 cmd.append('--stat')
1925 cmd.append('--stat')
1913 else:
1926 else:
1914 # for Git, this also implies '-p'
1927 # for Git, this also implies '-p'
1915 cmd.append('-U%d' % diffopts.context)
1928 cmd.append('-U%d' % diffopts.context)
1916
1929
1917 gitprefix = self.wvfs.reljoin(prefix, self._path)
1930 gitprefix = self.wvfs.reljoin(prefix, self._path)
1918
1931
1919 if diffopts.noprefix:
1932 if diffopts.noprefix:
1920 cmd.extend(['--src-prefix=%s/' % gitprefix,
1933 cmd.extend(['--src-prefix=%s/' % gitprefix,
1921 '--dst-prefix=%s/' % gitprefix])
1934 '--dst-prefix=%s/' % gitprefix])
1922 else:
1935 else:
1923 cmd.extend(['--src-prefix=a/%s/' % gitprefix,
1936 cmd.extend(['--src-prefix=a/%s/' % gitprefix,
1924 '--dst-prefix=b/%s/' % gitprefix])
1937 '--dst-prefix=b/%s/' % gitprefix])
1925
1938
1926 if diffopts.ignorews:
1939 if diffopts.ignorews:
1927 cmd.append('--ignore-all-space')
1940 cmd.append('--ignore-all-space')
1928 if diffopts.ignorewsamount:
1941 if diffopts.ignorewsamount:
1929 cmd.append('--ignore-space-change')
1942 cmd.append('--ignore-space-change')
1930 if self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4) \
1943 if self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4) \
1931 and diffopts.ignoreblanklines:
1944 and diffopts.ignoreblanklines:
1932 cmd.append('--ignore-blank-lines')
1945 cmd.append('--ignore-blank-lines')
1933
1946
1934 cmd.append(node1)
1947 cmd.append(node1)
1935 if node2:
1948 if node2:
1936 cmd.append(node2)
1949 cmd.append(node2)
1937
1950
1938 output = ""
1951 output = ""
1939 if match.always():
1952 if match.always():
1940 output += self._gitcommand(cmd) + '\n'
1953 output += self._gitcommand(cmd) + '\n'
1941 else:
1954 else:
1942 st = self.status(node2)[:3]
1955 st = self.status(node2)[:3]
1943 files = [f for sublist in st for f in sublist]
1956 files = [f for sublist in st for f in sublist]
1944 for f in files:
1957 for f in files:
1945 if match(f):
1958 if match(f):
1946 output += self._gitcommand(cmd + ['--', f]) + '\n'
1959 output += self._gitcommand(cmd + ['--', f]) + '\n'
1947
1960
1948 if output.strip():
1961 if output.strip():
1949 ui.write(output)
1962 ui.write(output)
1950
1963
1951 @annotatesubrepoerror
1964 @annotatesubrepoerror
1952 def revert(self, substate, *pats, **opts):
1965 def revert(self, substate, *pats, **opts):
1953 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1966 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1954 if not opts.get('no_backup'):
1967 if not opts.get('no_backup'):
1955 status = self.status(None)
1968 status = self.status(None)
1956 names = status.modified
1969 names = status.modified
1957 for name in names:
1970 for name in names:
1958 bakname = scmutil.origpath(self.ui, self._subparent, name)
1971 bakname = scmutil.origpath(self.ui, self._subparent, name)
1959 self.ui.note(_('saving current version of %s as %s\n') %
1972 self.ui.note(_('saving current version of %s as %s\n') %
1960 (name, bakname))
1973 (name, bakname))
1961 self.wvfs.rename(name, bakname)
1974 self.wvfs.rename(name, bakname)
1962
1975
1963 if not opts.get('dry_run'):
1976 if not opts.get('dry_run'):
1964 self.get(substate, overwrite=True)
1977 self.get(substate, overwrite=True)
1965 return []
1978 return []
1966
1979
1967 def shortid(self, revid):
1980 def shortid(self, revid):
1968 return revid[:7]
1981 return revid[:7]
1969
1982
1970 types = {
1983 types = {
1971 'hg': hgsubrepo,
1984 'hg': hgsubrepo,
1972 'svn': svnsubrepo,
1985 'svn': svnsubrepo,
1973 'git': gitsubrepo,
1986 'git': gitsubrepo,
1974 }
1987 }
@@ -1,375 +1,377 b''
1 Show all commands except debug commands
1 Show all commands except debug commands
2 $ hg debugcomplete
2 $ hg debugcomplete
3 add
3 add
4 addremove
4 addremove
5 annotate
5 annotate
6 archive
6 archive
7 backout
7 backout
8 bisect
8 bisect
9 bookmarks
9 bookmarks
10 branch
10 branch
11 branches
11 branches
12 bundle
12 bundle
13 cat
13 cat
14 clone
14 clone
15 commit
15 commit
16 config
16 config
17 copy
17 copy
18 diff
18 diff
19 export
19 export
20 files
20 files
21 forget
21 forget
22 graft
22 graft
23 grep
23 grep
24 heads
24 heads
25 help
25 help
26 identify
26 identify
27 import
27 import
28 incoming
28 incoming
29 init
29 init
30 locate
30 locate
31 log
31 log
32 manifest
32 manifest
33 merge
33 merge
34 outgoing
34 outgoing
35 parents
35 parents
36 paths
36 paths
37 phase
37 phase
38 pull
38 pull
39 push
39 push
40 recover
40 recover
41 remove
41 remove
42 rename
42 rename
43 resolve
43 resolve
44 revert
44 revert
45 rollback
45 rollback
46 root
46 root
47 serve
47 serve
48 status
48 status
49 summary
49 summary
50 tag
50 tag
51 tags
51 tags
52 tip
52 tip
53 unbundle
53 unbundle
54 update
54 update
55 verify
55 verify
56 version
56 version
57
57
58 Show all commands that start with "a"
58 Show all commands that start with "a"
59 $ hg debugcomplete a
59 $ hg debugcomplete a
60 add
60 add
61 addremove
61 addremove
62 annotate
62 annotate
63 archive
63 archive
64
64
65 Do not show debug commands if there are other candidates
65 Do not show debug commands if there are other candidates
66 $ hg debugcomplete d
66 $ hg debugcomplete d
67 diff
67 diff
68
68
69 Show debug commands if there are no other candidates
69 Show debug commands if there are no other candidates
70 $ hg debugcomplete debug
70 $ hg debugcomplete debug
71 debugancestor
71 debugancestor
72 debugapplystreamclonebundle
72 debugapplystreamclonebundle
73 debugbuilddag
73 debugbuilddag
74 debugbundle
74 debugbundle
75 debugcheckstate
75 debugcheckstate
76 debugcolor
76 debugcolor
77 debugcommands
77 debugcommands
78 debugcomplete
78 debugcomplete
79 debugconfig
79 debugconfig
80 debugcreatestreamclonebundle
80 debugcreatestreamclonebundle
81 debugdag
81 debugdag
82 debugdata
82 debugdata
83 debugdate
83 debugdate
84 debugdeltachain
84 debugdeltachain
85 debugdirstate
85 debugdirstate
86 debugdiscovery
86 debugdiscovery
87 debugextensions
87 debugextensions
88 debugfileset
88 debugfileset
89 debugfsinfo
89 debugfsinfo
90 debuggetbundle
90 debuggetbundle
91 debugignore
91 debugignore
92 debugindex
92 debugindex
93 debugindexdot
93 debugindexdot
94 debuginstall
94 debuginstall
95 debugknown
95 debugknown
96 debuglabelcomplete
96 debuglabelcomplete
97 debuglocks
97 debuglocks
98 debugmergestate
98 debugmergestate
99 debugnamecomplete
99 debugnamecomplete
100 debugobsolete
100 debugobsolete
101 debugpathcomplete
101 debugpathcomplete
102 debugpushkey
102 debugpushkey
103 debugpvec
103 debugpvec
104 debugrebuilddirstate
104 debugrebuilddirstate
105 debugrebuildfncache
105 debugrebuildfncache
106 debugrename
106 debugrename
107 debugrevlog
107 debugrevlog
108 debugrevspec
108 debugrevspec
109 debugsetparents
109 debugsetparents
110 debugsub
110 debugsub
111 debugsuccessorssets
111 debugsuccessorssets
112 debugtemplate
112 debugtemplate
113 debugupgraderepo
113 debugupgraderepo
114 debugwalk
114 debugwalk
115 debugwireargs
115 debugwireargs
116
116
117 Do not show the alias of a debug command if there are other candidates
117 Do not show the alias of a debug command if there are other candidates
118 (this should hide rawcommit)
118 (this should hide rawcommit)
119 $ hg debugcomplete r
119 $ hg debugcomplete r
120 recover
120 recover
121 remove
121 remove
122 rename
122 rename
123 resolve
123 resolve
124 revert
124 revert
125 rollback
125 rollback
126 root
126 root
127 Show the alias of a debug command if there are no other candidates
127 Show the alias of a debug command if there are no other candidates
128 $ hg debugcomplete rawc
128 $ hg debugcomplete rawc
129
129
130
130
131 Show the global options
131 Show the global options
132 $ hg debugcomplete --options | sort
132 $ hg debugcomplete --options | sort
133 --color
133 --color
134 --config
134 --config
135 --cwd
135 --cwd
136 --debug
136 --debug
137 --debugger
137 --debugger
138 --encoding
138 --encoding
139 --encodingmode
139 --encodingmode
140 --help
140 --help
141 --hidden
141 --hidden
142 --noninteractive
142 --noninteractive
143 --pager
143 --pager
144 --profile
144 --profile
145 --quiet
145 --quiet
146 --repository
146 --repository
147 --time
147 --time
148 --traceback
148 --traceback
149 --verbose
149 --verbose
150 --version
150 --version
151 -R
151 -R
152 -h
152 -h
153 -q
153 -q
154 -v
154 -v
155 -y
155 -y
156
156
157 Show the options for the "serve" command
157 Show the options for the "serve" command
158 $ hg debugcomplete --options serve | sort
158 $ hg debugcomplete --options serve | sort
159 --accesslog
159 --accesslog
160 --address
160 --address
161 --certificate
161 --certificate
162 --cmdserver
162 --cmdserver
163 --color
163 --color
164 --config
164 --config
165 --cwd
165 --cwd
166 --daemon
166 --daemon
167 --daemon-postexec
167 --daemon-postexec
168 --debug
168 --debug
169 --debugger
169 --debugger
170 --encoding
170 --encoding
171 --encodingmode
171 --encodingmode
172 --errorlog
172 --errorlog
173 --help
173 --help
174 --hidden
174 --hidden
175 --ipv6
175 --ipv6
176 --name
176 --name
177 --noninteractive
177 --noninteractive
178 --pager
178 --pager
179 --pid-file
179 --pid-file
180 --port
180 --port
181 --prefix
181 --prefix
182 --profile
182 --profile
183 --quiet
183 --quiet
184 --repository
184 --repository
185 --stdio
185 --stdio
186 --style
186 --style
187 --subrepos
187 --templates
188 --templates
188 --time
189 --time
189 --traceback
190 --traceback
190 --verbose
191 --verbose
191 --version
192 --version
192 --web-conf
193 --web-conf
193 -6
194 -6
194 -A
195 -A
195 -E
196 -E
196 -R
197 -R
198 -S
197 -a
199 -a
198 -d
200 -d
199 -h
201 -h
200 -n
202 -n
201 -p
203 -p
202 -q
204 -q
203 -t
205 -t
204 -v
206 -v
205 -y
207 -y
206
208
207 Show an error if we use --options with an ambiguous abbreviation
209 Show an error if we use --options with an ambiguous abbreviation
208 $ hg debugcomplete --options s
210 $ hg debugcomplete --options s
209 hg: command 's' is ambiguous:
211 hg: command 's' is ambiguous:
210 serve showconfig status summary
212 serve showconfig status summary
211 [255]
213 [255]
212
214
213 Show all commands + options
215 Show all commands + options
214 $ hg debugcommands
216 $ hg debugcommands
215 add: include, exclude, subrepos, dry-run
217 add: include, exclude, subrepos, dry-run
216 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
218 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
217 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
219 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
218 commit: addremove, close-branch, amend, secret, edit, interactive, include, exclude, message, logfile, date, user, subrepos
220 commit: addremove, close-branch, amend, secret, edit, interactive, include, exclude, message, logfile, date, user, subrepos
219 diff: rev, change, text, git, binary, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, root, include, exclude, subrepos
221 diff: rev, change, text, git, binary, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, root, include, exclude, subrepos
220 export: output, switch-parent, rev, text, git, binary, nodates
222 export: output, switch-parent, rev, text, git, binary, nodates
221 forget: include, exclude
223 forget: include, exclude
222 init: ssh, remotecmd, insecure
224 init: ssh, remotecmd, insecure
223 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
225 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
224 merge: force, rev, preview, tool
226 merge: force, rev, preview, tool
225 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
227 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
226 push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
228 push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
227 remove: after, force, subrepos, include, exclude
229 remove: after, force, subrepos, include, exclude
228 serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate
230 serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, subrepos
229 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos, template
231 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos, template
230 summary: remote
232 summary: remote
231 update: clean, check, merge, date, rev, tool
233 update: clean, check, merge, date, rev, tool
232 addremove: similarity, subrepos, include, exclude, dry-run
234 addremove: similarity, subrepos, include, exclude, dry-run
233 archive: no-decode, prefix, rev, type, subrepos, include, exclude
235 archive: no-decode, prefix, rev, type, subrepos, include, exclude
234 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
236 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
235 bisect: reset, good, bad, skip, extend, command, noupdate
237 bisect: reset, good, bad, skip, extend, command, noupdate
236 bookmarks: force, rev, delete, rename, inactive, template
238 bookmarks: force, rev, delete, rename, inactive, template
237 branch: force, clean
239 branch: force, clean
238 branches: active, closed, template
240 branches: active, closed, template
239 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
241 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
240 cat: output, rev, decode, include, exclude
242 cat: output, rev, decode, include, exclude
241 config: untrusted, edit, local, global, template
243 config: untrusted, edit, local, global, template
242 copy: after, force, include, exclude, dry-run
244 copy: after, force, include, exclude, dry-run
243 debugancestor:
245 debugancestor:
244 debugapplystreamclonebundle:
246 debugapplystreamclonebundle:
245 debugbuilddag: mergeable-file, overwritten-file, new-file
247 debugbuilddag: mergeable-file, overwritten-file, new-file
246 debugbundle: all, spec
248 debugbundle: all, spec
247 debugcheckstate:
249 debugcheckstate:
248 debugcolor: style
250 debugcolor: style
249 debugcommands:
251 debugcommands:
250 debugcomplete: options
252 debugcomplete: options
251 debugcreatestreamclonebundle:
253 debugcreatestreamclonebundle:
252 debugdag: tags, branches, dots, spaces
254 debugdag: tags, branches, dots, spaces
253 debugdata: changelog, manifest, dir
255 debugdata: changelog, manifest, dir
254 debugdate: extended
256 debugdate: extended
255 debugdeltachain: changelog, manifest, dir, template
257 debugdeltachain: changelog, manifest, dir, template
256 debugdirstate: nodates, datesort
258 debugdirstate: nodates, datesort
257 debugdiscovery: old, nonheads, ssh, remotecmd, insecure
259 debugdiscovery: old, nonheads, ssh, remotecmd, insecure
258 debugextensions: template
260 debugextensions: template
259 debugfileset: rev
261 debugfileset: rev
260 debugfsinfo:
262 debugfsinfo:
261 debuggetbundle: head, common, type
263 debuggetbundle: head, common, type
262 debugignore:
264 debugignore:
263 debugindex: changelog, manifest, dir, format
265 debugindex: changelog, manifest, dir, format
264 debugindexdot: changelog, manifest, dir
266 debugindexdot: changelog, manifest, dir
265 debuginstall: template
267 debuginstall: template
266 debugknown:
268 debugknown:
267 debuglabelcomplete:
269 debuglabelcomplete:
268 debuglocks: force-lock, force-wlock
270 debuglocks: force-lock, force-wlock
269 debugmergestate:
271 debugmergestate:
270 debugnamecomplete:
272 debugnamecomplete:
271 debugobsolete: flags, record-parents, rev, index, delete, date, user, template
273 debugobsolete: flags, record-parents, rev, index, delete, date, user, template
272 debugpathcomplete: full, normal, added, removed
274 debugpathcomplete: full, normal, added, removed
273 debugpushkey:
275 debugpushkey:
274 debugpvec:
276 debugpvec:
275 debugrebuilddirstate: rev, minimal
277 debugrebuilddirstate: rev, minimal
276 debugrebuildfncache:
278 debugrebuildfncache:
277 debugrename: rev
279 debugrename: rev
278 debugrevlog: changelog, manifest, dir, dump
280 debugrevlog: changelog, manifest, dir, dump
279 debugrevspec: optimize, show-stage, no-optimized, verify-optimized
281 debugrevspec: optimize, show-stage, no-optimized, verify-optimized
280 debugsetparents:
282 debugsetparents:
281 debugsub: rev
283 debugsub: rev
282 debugsuccessorssets:
284 debugsuccessorssets:
283 debugtemplate: rev, define
285 debugtemplate: rev, define
284 debugupgraderepo: optimize, run
286 debugupgraderepo: optimize, run
285 debugwalk: include, exclude
287 debugwalk: include, exclude
286 debugwireargs: three, four, five, ssh, remotecmd, insecure
288 debugwireargs: three, four, five, ssh, remotecmd, insecure
287 files: rev, print0, include, exclude, template, subrepos
289 files: rev, print0, include, exclude, template, subrepos
288 graft: rev, continue, edit, log, force, currentdate, currentuser, date, user, tool, dry-run
290 graft: rev, continue, edit, log, force, currentdate, currentuser, date, user, tool, dry-run
289 grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, template, include, exclude
291 grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, template, include, exclude
290 heads: rev, topo, active, closed, style, template
292 heads: rev, topo, active, closed, style, template
291 help: extension, command, keyword, system
293 help: extension, command, keyword, system
292 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure
294 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure
293 import: strip, base, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
295 import: strip, base, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
294 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
296 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
295 locate: rev, print0, fullpath, include, exclude
297 locate: rev, print0, fullpath, include, exclude
296 manifest: rev, all, template
298 manifest: rev, all, template
297 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
299 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
298 parents: rev, style, template
300 parents: rev, style, template
299 paths: template
301 paths: template
300 phase: public, draft, secret, force, rev
302 phase: public, draft, secret, force, rev
301 recover:
303 recover:
302 rename: after, force, include, exclude, dry-run
304 rename: after, force, include, exclude, dry-run
303 resolve: all, list, mark, unmark, no-status, tool, include, exclude, template
305 resolve: all, list, mark, unmark, no-status, tool, include, exclude, template
304 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
306 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
305 rollback: dry-run, force
307 rollback: dry-run, force
306 root:
308 root:
307 tag: force, local, rev, remove, edit, message, date, user
309 tag: force, local, rev, remove, edit, message, date, user
308 tags: template
310 tags: template
309 tip: patch, git, style, template
311 tip: patch, git, style, template
310 unbundle: update
312 unbundle: update
311 verify:
313 verify:
312 version: template
314 version: template
313
315
314 $ hg init a
316 $ hg init a
315 $ cd a
317 $ cd a
316 $ echo fee > fee
318 $ echo fee > fee
317 $ hg ci -q -Amfee
319 $ hg ci -q -Amfee
318 $ hg tag fee
320 $ hg tag fee
319 $ mkdir fie
321 $ mkdir fie
320 $ echo dead > fie/dead
322 $ echo dead > fie/dead
321 $ echo live > fie/live
323 $ echo live > fie/live
322 $ hg bookmark fo
324 $ hg bookmark fo
323 $ hg branch -q fie
325 $ hg branch -q fie
324 $ hg ci -q -Amfie
326 $ hg ci -q -Amfie
325 $ echo fo > fo
327 $ echo fo > fo
326 $ hg branch -qf default
328 $ hg branch -qf default
327 $ hg ci -q -Amfo
329 $ hg ci -q -Amfo
328 $ echo Fum > Fum
330 $ echo Fum > Fum
329 $ hg ci -q -AmFum
331 $ hg ci -q -AmFum
330 $ hg bookmark Fum
332 $ hg bookmark Fum
331
333
332 Test debugpathcomplete
334 Test debugpathcomplete
333
335
334 $ hg debugpathcomplete f
336 $ hg debugpathcomplete f
335 fee
337 fee
336 fie
338 fie
337 fo
339 fo
338 $ hg debugpathcomplete -f f
340 $ hg debugpathcomplete -f f
339 fee
341 fee
340 fie/dead
342 fie/dead
341 fie/live
343 fie/live
342 fo
344 fo
343
345
344 $ hg rm Fum
346 $ hg rm Fum
345 $ hg debugpathcomplete -r F
347 $ hg debugpathcomplete -r F
346 Fum
348 Fum
347
349
348 Test debugnamecomplete
350 Test debugnamecomplete
349
351
350 $ hg debugnamecomplete
352 $ hg debugnamecomplete
351 Fum
353 Fum
352 default
354 default
353 fee
355 fee
354 fie
356 fie
355 fo
357 fo
356 tip
358 tip
357 $ hg debugnamecomplete f
359 $ hg debugnamecomplete f
358 fee
360 fee
359 fie
361 fie
360 fo
362 fo
361
363
362 Test debuglabelcomplete, a deprecated name for debugnamecomplete that is still
364 Test debuglabelcomplete, a deprecated name for debugnamecomplete that is still
363 used for completions in some shells.
365 used for completions in some shells.
364
366
365 $ hg debuglabelcomplete
367 $ hg debuglabelcomplete
366 Fum
368 Fum
367 default
369 default
368 fee
370 fee
369 fie
371 fie
370 fo
372 fo
371 tip
373 tip
372 $ hg debuglabelcomplete f
374 $ hg debuglabelcomplete f
373 fee
375 fee
374 fie
376 fie
375 fo
377 fo
@@ -1,1097 +1,1134 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extdiff]
2 > [extdiff]
3 > # for portability:
3 > # for portability:
4 > pdiff = sh "$RUNTESTDIR/pdiff"
4 > pdiff = sh "$RUNTESTDIR/pdiff"
5 > [progress]
5 > [progress]
6 > disable=False
6 > disable=False
7 > assume-tty = 1
7 > assume-tty = 1
8 > delay = 0
8 > delay = 0
9 > # set changedelay really large so we don't see nested topics
9 > # set changedelay really large so we don't see nested topics
10 > changedelay = 30000
10 > changedelay = 30000
11 > format = topic bar number
11 > format = topic bar number
12 > refresh = 0
12 > refresh = 0
13 > width = 60
13 > width = 60
14 > EOF
14 > EOF
15
15
16 Preparing the subrepository 'sub2'
16 Preparing the subrepository 'sub2'
17
17
18 $ hg init sub2
18 $ hg init sub2
19 $ echo sub2 > sub2/sub2
19 $ echo sub2 > sub2/sub2
20 $ hg add -R sub2
20 $ hg add -R sub2
21 adding sub2/sub2 (glob)
21 adding sub2/sub2 (glob)
22 $ hg commit -R sub2 -m "sub2 import"
22 $ hg commit -R sub2 -m "sub2 import"
23
23
24 Preparing the 'sub1' repo which depends on the subrepo 'sub2'
24 Preparing the 'sub1' repo which depends on the subrepo 'sub2'
25
25
26 $ hg init sub1
26 $ hg init sub1
27 $ echo sub1 > sub1/sub1
27 $ echo sub1 > sub1/sub1
28 $ echo "sub2 = ../sub2" > sub1/.hgsub
28 $ echo "sub2 = ../sub2" > sub1/.hgsub
29 $ hg clone sub2 sub1/sub2
29 $ hg clone sub2 sub1/sub2
30 \r (no-eol) (esc)
30 \r (no-eol) (esc)
31 linking [ <=> ] 1\r (no-eol) (esc)
31 linking [ <=> ] 1\r (no-eol) (esc)
32 linking [ <=> ] 2\r (no-eol) (esc)
32 linking [ <=> ] 2\r (no-eol) (esc)
33 linking [ <=> ] 3\r (no-eol) (esc)
33 linking [ <=> ] 3\r (no-eol) (esc)
34 linking [ <=> ] 4\r (no-eol) (esc)
34 linking [ <=> ] 4\r (no-eol) (esc)
35 linking [ <=> ] 5\r (no-eol) (esc)
35 linking [ <=> ] 5\r (no-eol) (esc)
36 linking [ <=> ] 6\r (no-eol) (esc)
36 linking [ <=> ] 6\r (no-eol) (esc)
37 \r (no-eol) (esc)
37 \r (no-eol) (esc)
38 \r (no-eol) (esc)
38 \r (no-eol) (esc)
39 updating [===========================================>] 1/1\r (no-eol) (esc)
39 updating [===========================================>] 1/1\r (no-eol) (esc)
40 \r (no-eol) (esc)
40 \r (no-eol) (esc)
41 updating to branch default
41 updating to branch default
42 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
42 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
43 $ hg add -R sub1
43 $ hg add -R sub1
44 adding sub1/.hgsub (glob)
44 adding sub1/.hgsub (glob)
45 adding sub1/sub1 (glob)
45 adding sub1/sub1 (glob)
46 $ hg commit -R sub1 -m "sub1 import"
46 $ hg commit -R sub1 -m "sub1 import"
47
47
48 Preparing the 'main' repo which depends on the subrepo 'sub1'
48 Preparing the 'main' repo which depends on the subrepo 'sub1'
49
49
50 $ hg init main
50 $ hg init main
51 $ echo main > main/main
51 $ echo main > main/main
52 $ echo "sub1 = ../sub1" > main/.hgsub
52 $ echo "sub1 = ../sub1" > main/.hgsub
53 $ hg clone sub1 main/sub1
53 $ hg clone sub1 main/sub1
54 \r (no-eol) (esc)
54 \r (no-eol) (esc)
55 linking [ <=> ] 1\r (no-eol) (esc)
55 linking [ <=> ] 1\r (no-eol) (esc)
56 linking [ <=> ] 2\r (no-eol) (esc)
56 linking [ <=> ] 2\r (no-eol) (esc)
57 linking [ <=> ] 3\r (no-eol) (esc)
57 linking [ <=> ] 3\r (no-eol) (esc)
58 linking [ <=> ] 4\r (no-eol) (esc)
58 linking [ <=> ] 4\r (no-eol) (esc)
59 linking [ <=> ] 5\r (no-eol) (esc)
59 linking [ <=> ] 5\r (no-eol) (esc)
60 linking [ <=> ] 6\r (no-eol) (esc)
60 linking [ <=> ] 6\r (no-eol) (esc)
61 linking [ <=> ] 7\r (no-eol) (esc)
61 linking [ <=> ] 7\r (no-eol) (esc)
62 linking [ <=> ] 8\r (no-eol) (esc)
62 linking [ <=> ] 8\r (no-eol) (esc)
63 \r (no-eol) (esc)
63 \r (no-eol) (esc)
64 \r (no-eol) (esc)
64 \r (no-eol) (esc)
65 updating [===========================================>] 3/3\r (no-eol) (esc)
65 updating [===========================================>] 3/3\r (no-eol) (esc)
66 updating [===========================================>] 1/1\r (no-eol) (esc)
66 updating [===========================================>] 1/1\r (no-eol) (esc)
67 \r (no-eol) (esc)
67 \r (no-eol) (esc)
68 updating to branch default
68 updating to branch default
69 cloning subrepo sub2 from $TESTTMP/sub2
69 cloning subrepo sub2 from $TESTTMP/sub2
70 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
70 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
71 $ hg add -R main
71 $ hg add -R main
72 adding main/.hgsub (glob)
72 adding main/.hgsub (glob)
73 adding main/main (glob)
73 adding main/main (glob)
74 $ hg commit -R main -m "main import"
74 $ hg commit -R main -m "main import"
75
75
76 #if serve
77
78 Unfortunately, subrepos not at their nominal location cannot be cloned. But
79 they are still served from their location within the local repository. The only
80 reason why 'main' can be cloned via the filesystem is because 'sub1' and 'sub2'
81 are also available as siblings of 'main'.
82
83 $ hg serve -R main --debug -S -p $HGPORT -d --pid-file=hg1.pid -E error.log -A access.log
84 adding = $TESTTMP/main (glob)
85 adding sub1 = $TESTTMP/main/sub1 (glob)
86 adding sub1/sub2 = $TESTTMP/main/sub1/sub2 (glob)
87 listening at http://*:$HGPORT/ (bound to *:$HGPORT) (glob) (?)
88 adding = $TESTTMP/main (glob) (?)
89 adding sub1 = $TESTTMP/main/sub1 (glob) (?)
90 adding sub1/sub2 = $TESTTMP/main/sub1/sub2 (glob) (?)
91 $ cat hg1.pid >> $DAEMON_PIDS
92
93 $ hg clone http://localhost:$HGPORT httpclone --config progress.disable=True
94 requesting all changes
95 adding changesets
96 adding manifests
97 adding file changes
98 added 1 changesets with 3 changes to 3 files
99 updating to branch default
100 abort: HTTP Error 404: Not Found
101 [255]
102
103 $ cat access.log
104 * "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
105 * "GET /?cmd=batch HTTP/1.1" 200 - * (glob)
106 * "GET /?cmd=getbundle HTTP/1.1" 200 - * (glob)
107 * "GET /../sub1?cmd=capabilities HTTP/1.1" 404 - (glob)
108
109 $ killdaemons.py
110 $ rm hg1.pid error.log access.log
111 #endif
112
76 Cleaning both repositories, just as a clone -U
113 Cleaning both repositories, just as a clone -U
77
114
78 $ hg up -C -R sub2 null
115 $ hg up -C -R sub2 null
79 \r (no-eol) (esc)
116 \r (no-eol) (esc)
80 updating [===========================================>] 1/1\r (no-eol) (esc)
117 updating [===========================================>] 1/1\r (no-eol) (esc)
81 \r (no-eol) (esc)
118 \r (no-eol) (esc)
82 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
119 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
83 $ hg up -C -R sub1 null
120 $ hg up -C -R sub1 null
84 \r (no-eol) (esc)
121 \r (no-eol) (esc)
85 updating [===========================================>] 1/1\r (no-eol) (esc)
122 updating [===========================================>] 1/1\r (no-eol) (esc)
86 \r (no-eol) (esc)
123 \r (no-eol) (esc)
87 \r (no-eol) (esc)
124 \r (no-eol) (esc)
88 updating [===========================================>] 3/3\r (no-eol) (esc)
125 updating [===========================================>] 3/3\r (no-eol) (esc)
89 \r (no-eol) (esc)
126 \r (no-eol) (esc)
90 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
127 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
91 $ hg up -C -R main null
128 $ hg up -C -R main null
92 \r (no-eol) (esc)
129 \r (no-eol) (esc)
93 updating [===========================================>] 1/1\r (no-eol) (esc)
130 updating [===========================================>] 1/1\r (no-eol) (esc)
94 \r (no-eol) (esc)
131 \r (no-eol) (esc)
95 \r (no-eol) (esc)
132 \r (no-eol) (esc)
96 updating [===========================================>] 3/3\r (no-eol) (esc)
133 updating [===========================================>] 3/3\r (no-eol) (esc)
97 \r (no-eol) (esc)
134 \r (no-eol) (esc)
98 \r (no-eol) (esc)
135 \r (no-eol) (esc)
99 updating [===========================================>] 3/3\r (no-eol) (esc)
136 updating [===========================================>] 3/3\r (no-eol) (esc)
100 \r (no-eol) (esc)
137 \r (no-eol) (esc)
101 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
138 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
102 $ rm -rf main/sub1
139 $ rm -rf main/sub1
103 $ rm -rf sub1/sub2
140 $ rm -rf sub1/sub2
104
141
105 Clone main
142 Clone main
106
143
107 $ hg --config extensions.largefiles= clone main cloned
144 $ hg --config extensions.largefiles= clone main cloned
108 \r (no-eol) (esc)
145 \r (no-eol) (esc)
109 linking [ <=> ] 1\r (no-eol) (esc)
146 linking [ <=> ] 1\r (no-eol) (esc)
110 linking [ <=> ] 2\r (no-eol) (esc)
147 linking [ <=> ] 2\r (no-eol) (esc)
111 linking [ <=> ] 3\r (no-eol) (esc)
148 linking [ <=> ] 3\r (no-eol) (esc)
112 linking [ <=> ] 4\r (no-eol) (esc)
149 linking [ <=> ] 4\r (no-eol) (esc)
113 linking [ <=> ] 5\r (no-eol) (esc)
150 linking [ <=> ] 5\r (no-eol) (esc)
114 linking [ <=> ] 6\r (no-eol) (esc)
151 linking [ <=> ] 6\r (no-eol) (esc)
115 linking [ <=> ] 7\r (no-eol) (esc)
152 linking [ <=> ] 7\r (no-eol) (esc)
116 linking [ <=> ] 8\r (no-eol) (esc)
153 linking [ <=> ] 8\r (no-eol) (esc)
117 \r (no-eol) (esc)
154 \r (no-eol) (esc)
118 \r (no-eol) (esc)
155 \r (no-eol) (esc)
119 updating [===========================================>] 3/3\r (no-eol) (esc)
156 updating [===========================================>] 3/3\r (no-eol) (esc)
120 updating [===========================================>] 3/3\r (no-eol) (esc)
157 updating [===========================================>] 3/3\r (no-eol) (esc)
121 updating [===========================================>] 1/1\r (no-eol) (esc)
158 updating [===========================================>] 1/1\r (no-eol) (esc)
122 \r (no-eol) (esc)
159 \r (no-eol) (esc)
123 updating to branch default
160 updating to branch default
124 cloning subrepo sub1 from $TESTTMP/sub1
161 cloning subrepo sub1 from $TESTTMP/sub1
125 cloning subrepo sub1/sub2 from $TESTTMP/sub2 (glob)
162 cloning subrepo sub1/sub2 from $TESTTMP/sub2 (glob)
126 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
163 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
127
164
128 Largefiles is NOT enabled in the clone if the source repo doesn't require it
165 Largefiles is NOT enabled in the clone if the source repo doesn't require it
129 $ cat cloned/.hg/hgrc
166 $ cat cloned/.hg/hgrc
130 # example repository config (see 'hg help config' for more info)
167 # example repository config (see 'hg help config' for more info)
131 [paths]
168 [paths]
132 default = $TESTTMP/main (glob)
169 default = $TESTTMP/main (glob)
133
170
134 # path aliases to other clones of this repo in URLs or filesystem paths
171 # path aliases to other clones of this repo in URLs or filesystem paths
135 # (see 'hg help config.paths' for more info)
172 # (see 'hg help config.paths' for more info)
136 #
173 #
137 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
174 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
138 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
175 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
139 # my-clone = /home/jdoe/jdoes-clone
176 # my-clone = /home/jdoe/jdoes-clone
140
177
141 [ui]
178 [ui]
142 # name and email (local to this repository, optional), e.g.
179 # name and email (local to this repository, optional), e.g.
143 # username = Jane Doe <jdoe@example.com>
180 # username = Jane Doe <jdoe@example.com>
144
181
145 Checking cloned repo ids
182 Checking cloned repo ids
146
183
147 $ printf "cloned " ; hg id -R cloned
184 $ printf "cloned " ; hg id -R cloned
148 cloned 7f491f53a367 tip
185 cloned 7f491f53a367 tip
149 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
186 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
150 cloned/sub1 fc3b4ce2696f tip
187 cloned/sub1 fc3b4ce2696f tip
151 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
188 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
152 cloned/sub1/sub2 c57a0840e3ba tip
189 cloned/sub1/sub2 c57a0840e3ba tip
153
190
154 debugsub output for main and sub1
191 debugsub output for main and sub1
155
192
156 $ hg debugsub -R cloned
193 $ hg debugsub -R cloned
157 path sub1
194 path sub1
158 source ../sub1
195 source ../sub1
159 revision fc3b4ce2696f7741438c79207583768f2ce6b0dd
196 revision fc3b4ce2696f7741438c79207583768f2ce6b0dd
160 $ hg debugsub -R cloned/sub1
197 $ hg debugsub -R cloned/sub1
161 path sub2
198 path sub2
162 source ../sub2
199 source ../sub2
163 revision c57a0840e3badd667ef3c3ef65471609acb2ba3c
200 revision c57a0840e3badd667ef3c3ef65471609acb2ba3c
164
201
165 Modifying deeply nested 'sub2'
202 Modifying deeply nested 'sub2'
166
203
167 $ echo modified > cloned/sub1/sub2/sub2
204 $ echo modified > cloned/sub1/sub2/sub2
168 $ hg commit --subrepos -m "deep nested modif should trigger a commit" -R cloned
205 $ hg commit --subrepos -m "deep nested modif should trigger a commit" -R cloned
169 committing subrepository sub1
206 committing subrepository sub1
170 committing subrepository sub1/sub2 (glob)
207 committing subrepository sub1/sub2 (glob)
171
208
172 Checking modified node ids
209 Checking modified node ids
173
210
174 $ printf "cloned " ; hg id -R cloned
211 $ printf "cloned " ; hg id -R cloned
175 cloned ffe6649062fe tip
212 cloned ffe6649062fe tip
176 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
213 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
177 cloned/sub1 2ecb03bf44a9 tip
214 cloned/sub1 2ecb03bf44a9 tip
178 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
215 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
179 cloned/sub1/sub2 53dd3430bcaf tip
216 cloned/sub1/sub2 53dd3430bcaf tip
180
217
181 debugsub output for main and sub1
218 debugsub output for main and sub1
182
219
183 $ hg debugsub -R cloned
220 $ hg debugsub -R cloned
184 path sub1
221 path sub1
185 source ../sub1
222 source ../sub1
186 revision 2ecb03bf44a94e749e8669481dd9069526ce7cb9
223 revision 2ecb03bf44a94e749e8669481dd9069526ce7cb9
187 $ hg debugsub -R cloned/sub1
224 $ hg debugsub -R cloned/sub1
188 path sub2
225 path sub2
189 source ../sub2
226 source ../sub2
190 revision 53dd3430bcaf5ab4a7c48262bcad6d441f510487
227 revision 53dd3430bcaf5ab4a7c48262bcad6d441f510487
191
228
192 Check that deep archiving works
229 Check that deep archiving works
193
230
194 $ cd cloned
231 $ cd cloned
195 $ echo 'test' > sub1/sub2/test.txt
232 $ echo 'test' > sub1/sub2/test.txt
196 $ hg --config extensions.largefiles=! add sub1/sub2/test.txt
233 $ hg --config extensions.largefiles=! add sub1/sub2/test.txt
197 $ mkdir sub1/sub2/folder
234 $ mkdir sub1/sub2/folder
198 $ echo 'subfolder' > sub1/sub2/folder/test.txt
235 $ echo 'subfolder' > sub1/sub2/folder/test.txt
199 $ hg ci -ASm "add test.txt"
236 $ hg ci -ASm "add test.txt"
200 adding sub1/sub2/folder/test.txt
237 adding sub1/sub2/folder/test.txt
201 committing subrepository sub1
238 committing subrepository sub1
202 committing subrepository sub1/sub2 (glob)
239 committing subrepository sub1/sub2 (glob)
203
240
204 .. but first take a detour through some deep removal testing
241 .. but first take a detour through some deep removal testing
205
242
206 $ hg remove -S -I 're:.*.txt' .
243 $ hg remove -S -I 're:.*.txt' .
207 \r (no-eol) (esc)
244 \r (no-eol) (esc)
208 searching [==========================================>] 1/1\r (no-eol) (esc)
245 searching [==========================================>] 1/1\r (no-eol) (esc)
209 searching [==========================================>] 1/1\r (no-eol) (esc)
246 searching [==========================================>] 1/1\r (no-eol) (esc)
210 \r (no-eol) (esc)
247 \r (no-eol) (esc)
211 \r (no-eol) (esc)
248 \r (no-eol) (esc)
212 deleting [=====================> ] 1/2\r (no-eol) (esc)
249 deleting [=====================> ] 1/2\r (no-eol) (esc)
213 \r (no-eol) (esc)
250 \r (no-eol) (esc)
214 \r (no-eol) (esc)
251 \r (no-eol) (esc)
215 deleting [===========================================>] 2/2\r (no-eol) (esc)
252 deleting [===========================================>] 2/2\r (no-eol) (esc)
216 \r (no-eol) (esc)
253 \r (no-eol) (esc)
217 removing sub1/sub2/folder/test.txt (glob)
254 removing sub1/sub2/folder/test.txt (glob)
218 removing sub1/sub2/test.txt (glob)
255 removing sub1/sub2/test.txt (glob)
219 $ hg status -S
256 $ hg status -S
220 R sub1/sub2/folder/test.txt
257 R sub1/sub2/folder/test.txt
221 R sub1/sub2/test.txt
258 R sub1/sub2/test.txt
222 $ hg update -Cq
259 $ hg update -Cq
223 $ hg remove -I 're:.*.txt' sub1
260 $ hg remove -I 're:.*.txt' sub1
224 \r (no-eol) (esc)
261 \r (no-eol) (esc)
225 searching [==========================================>] 1/1\r (no-eol) (esc)
262 searching [==========================================>] 1/1\r (no-eol) (esc)
226 \r (no-eol) (esc)
263 \r (no-eol) (esc)
227 \r (no-eol) (esc)
264 \r (no-eol) (esc)
228 deleting [===========================================>] 1/1\r (no-eol) (esc)
265 deleting [===========================================>] 1/1\r (no-eol) (esc)
229 \r (no-eol) (esc)
266 \r (no-eol) (esc)
230 $ hg status -S
267 $ hg status -S
231 $ hg remove sub1/sub2/folder/test.txt
268 $ hg remove sub1/sub2/folder/test.txt
232 \r (no-eol) (esc)
269 \r (no-eol) (esc)
233 searching [==========================================>] 1/1\r (no-eol) (esc)
270 searching [==========================================>] 1/1\r (no-eol) (esc)
234 searching [==========================================>] 1/1\r (no-eol) (esc)
271 searching [==========================================>] 1/1\r (no-eol) (esc)
235 \r (no-eol) (esc)
272 \r (no-eol) (esc)
236 \r (no-eol) (esc)
273 \r (no-eol) (esc)
237 deleting [===========================================>] 1/1\r (no-eol) (esc)
274 deleting [===========================================>] 1/1\r (no-eol) (esc)
238 \r (no-eol) (esc)
275 \r (no-eol) (esc)
239 \r (no-eol) (esc)
276 \r (no-eol) (esc)
240 deleting [===========================================>] 1/1\r (no-eol) (esc)
277 deleting [===========================================>] 1/1\r (no-eol) (esc)
241 \r (no-eol) (esc)
278 \r (no-eol) (esc)
242 \r (no-eol) (esc)
279 \r (no-eol) (esc)
243 deleting [===========================================>] 1/1\r (no-eol) (esc)
280 deleting [===========================================>] 1/1\r (no-eol) (esc)
244 \r (no-eol) (esc)
281 \r (no-eol) (esc)
245 $ hg remove sub1/.hgsubstate
282 $ hg remove sub1/.hgsubstate
246 \r (no-eol) (esc)
283 \r (no-eol) (esc)
247 searching [==========================================>] 1/1\r (no-eol) (esc)
284 searching [==========================================>] 1/1\r (no-eol) (esc)
248 \r (no-eol) (esc)
285 \r (no-eol) (esc)
249 \r (no-eol) (esc)
286 \r (no-eol) (esc)
250 deleting [===========================================>] 1/1\r (no-eol) (esc)
287 deleting [===========================================>] 1/1\r (no-eol) (esc)
251 \r (no-eol) (esc)
288 \r (no-eol) (esc)
252 \r (no-eol) (esc)
289 \r (no-eol) (esc)
253 deleting [===========================================>] 1/1\r (no-eol) (esc)
290 deleting [===========================================>] 1/1\r (no-eol) (esc)
254 \r (no-eol) (esc)
291 \r (no-eol) (esc)
255 $ mv sub1/.hgsub sub1/x.hgsub
292 $ mv sub1/.hgsub sub1/x.hgsub
256 $ hg status -S
293 $ hg status -S
257 warning: subrepo spec file 'sub1/.hgsub' not found
294 warning: subrepo spec file 'sub1/.hgsub' not found
258 R sub1/.hgsubstate
295 R sub1/.hgsubstate
259 R sub1/sub2/folder/test.txt
296 R sub1/sub2/folder/test.txt
260 ! sub1/.hgsub
297 ! sub1/.hgsub
261 ? sub1/x.hgsub
298 ? sub1/x.hgsub
262 $ mv sub1/x.hgsub sub1/.hgsub
299 $ mv sub1/x.hgsub sub1/.hgsub
263 $ hg update -Cq
300 $ hg update -Cq
264 $ touch sub1/foo
301 $ touch sub1/foo
265 $ hg forget sub1/sub2/folder/test.txt
302 $ hg forget sub1/sub2/folder/test.txt
266 $ rm sub1/sub2/test.txt
303 $ rm sub1/sub2/test.txt
267
304
268 Test relative path printing + subrepos
305 Test relative path printing + subrepos
269 $ mkdir -p foo/bar
306 $ mkdir -p foo/bar
270 $ cd foo
307 $ cd foo
271 $ touch bar/abc
308 $ touch bar/abc
272 $ hg addremove -S ..
309 $ hg addremove -S ..
273 \r (no-eol) (esc)
310 \r (no-eol) (esc)
274 searching for exact renames [ ] 0/1\r (no-eol) (esc)
311 searching for exact renames [ ] 0/1\r (no-eol) (esc)
275 \r (no-eol) (esc)
312 \r (no-eol) (esc)
276 adding ../sub1/sub2/folder/test.txt (glob)
313 adding ../sub1/sub2/folder/test.txt (glob)
277 removing ../sub1/sub2/test.txt (glob)
314 removing ../sub1/sub2/test.txt (glob)
278 adding ../sub1/foo (glob)
315 adding ../sub1/foo (glob)
279 adding bar/abc (glob)
316 adding bar/abc (glob)
280 $ cd ..
317 $ cd ..
281 $ hg status -S
318 $ hg status -S
282 A foo/bar/abc
319 A foo/bar/abc
283 A sub1/foo
320 A sub1/foo
284 R sub1/sub2/test.txt
321 R sub1/sub2/test.txt
285
322
286 Archive wdir() with subrepos
323 Archive wdir() with subrepos
287 $ hg rm main
324 $ hg rm main
288 \r (no-eol) (esc)
325 \r (no-eol) (esc)
289 deleting [===========================================>] 1/1\r (no-eol) (esc)
326 deleting [===========================================>] 1/1\r (no-eol) (esc)
290 \r (no-eol) (esc)
327 \r (no-eol) (esc)
291 $ hg archive -S -r 'wdir()' ../wdir
328 $ hg archive -S -r 'wdir()' ../wdir
292 \r (no-eol) (esc)
329 \r (no-eol) (esc)
293 archiving [ ] 0/3\r (no-eol) (esc)
330 archiving [ ] 0/3\r (no-eol) (esc)
294 archiving [=============> ] 1/3\r (no-eol) (esc)
331 archiving [=============> ] 1/3\r (no-eol) (esc)
295 archiving [===========================> ] 2/3\r (no-eol) (esc)
332 archiving [===========================> ] 2/3\r (no-eol) (esc)
296 archiving [==========================================>] 3/3\r (no-eol) (esc)
333 archiving [==========================================>] 3/3\r (no-eol) (esc)
297 \r (no-eol) (esc)
334 \r (no-eol) (esc)
298 \r (no-eol) (esc)
335 \r (no-eol) (esc)
299 archiving (sub1) [ ] 0/4\r (no-eol) (esc)
336 archiving (sub1) [ ] 0/4\r (no-eol) (esc)
300 archiving (sub1) [========> ] 1/4\r (no-eol) (esc)
337 archiving (sub1) [========> ] 1/4\r (no-eol) (esc)
301 archiving (sub1) [=================> ] 2/4\r (no-eol) (esc)
338 archiving (sub1) [=================> ] 2/4\r (no-eol) (esc)
302 archiving (sub1) [==========================> ] 3/4\r (no-eol) (esc)
339 archiving (sub1) [==========================> ] 3/4\r (no-eol) (esc)
303 archiving (sub1) [===================================>] 4/4\r (no-eol) (esc)
340 archiving (sub1) [===================================>] 4/4\r (no-eol) (esc)
304 \r (no-eol) (esc)
341 \r (no-eol) (esc)
305 \r (no-eol) (esc)
342 \r (no-eol) (esc)
306 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (glob) (esc)
343 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (glob) (esc)
307 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (glob) (esc)
344 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (glob) (esc)
308 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (glob) (esc)
345 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (glob) (esc)
309 \r (no-eol) (esc)
346 \r (no-eol) (esc)
310 $ diff -r . ../wdir | egrep -v '\.hg$|^Common subdirectories:'
347 $ diff -r . ../wdir | egrep -v '\.hg$|^Common subdirectories:'
311 Only in ../wdir: .hg_archival.txt
348 Only in ../wdir: .hg_archival.txt
312
349
313 $ find ../wdir -type f | sort
350 $ find ../wdir -type f | sort
314 ../wdir/.hg_archival.txt
351 ../wdir/.hg_archival.txt
315 ../wdir/.hgsub
352 ../wdir/.hgsub
316 ../wdir/.hgsubstate
353 ../wdir/.hgsubstate
317 ../wdir/foo/bar/abc
354 ../wdir/foo/bar/abc
318 ../wdir/sub1/.hgsub
355 ../wdir/sub1/.hgsub
319 ../wdir/sub1/.hgsubstate
356 ../wdir/sub1/.hgsubstate
320 ../wdir/sub1/foo
357 ../wdir/sub1/foo
321 ../wdir/sub1/sub1
358 ../wdir/sub1/sub1
322 ../wdir/sub1/sub2/folder/test.txt
359 ../wdir/sub1/sub2/folder/test.txt
323 ../wdir/sub1/sub2/sub2
360 ../wdir/sub1/sub2/sub2
324
361
325 $ cat ../wdir/.hg_archival.txt
362 $ cat ../wdir/.hg_archival.txt
326 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
363 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
327 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd+
364 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd+
328 branch: default
365 branch: default
329 latesttag: null
366 latesttag: null
330 latesttagdistance: 4
367 latesttagdistance: 4
331 changessincelatesttag: 4
368 changessincelatesttag: 4
332
369
333 Attempting to archive 'wdir()' with a missing file is handled gracefully
370 Attempting to archive 'wdir()' with a missing file is handled gracefully
334 $ rm sub1/sub1
371 $ rm sub1/sub1
335 $ rm -r ../wdir
372 $ rm -r ../wdir
336 $ hg archive -v -S -r 'wdir()' ../wdir
373 $ hg archive -v -S -r 'wdir()' ../wdir
337 \r (no-eol) (esc)
374 \r (no-eol) (esc)
338 archiving [ ] 0/3\r (no-eol) (esc)
375 archiving [ ] 0/3\r (no-eol) (esc)
339 archiving [=============> ] 1/3\r (no-eol) (esc)
376 archiving [=============> ] 1/3\r (no-eol) (esc)
340 archiving [===========================> ] 2/3\r (no-eol) (esc)
377 archiving [===========================> ] 2/3\r (no-eol) (esc)
341 archiving [==========================================>] 3/3\r (no-eol) (esc)
378 archiving [==========================================>] 3/3\r (no-eol) (esc)
342 \r (no-eol) (esc)
379 \r (no-eol) (esc)
343 \r (no-eol) (esc)
380 \r (no-eol) (esc)
344 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
381 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
345 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
382 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
346 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
383 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
347 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
384 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
348 \r (no-eol) (esc)
385 \r (no-eol) (esc)
349 \r (no-eol) (esc)
386 \r (no-eol) (esc)
350 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (glob) (esc)
387 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (glob) (esc)
351 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (glob) (esc)
388 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (glob) (esc)
352 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (glob) (esc)
389 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (glob) (esc)
353 \r (no-eol) (esc)
390 \r (no-eol) (esc)
354 $ find ../wdir -type f | sort
391 $ find ../wdir -type f | sort
355 ../wdir/.hg_archival.txt
392 ../wdir/.hg_archival.txt
356 ../wdir/.hgsub
393 ../wdir/.hgsub
357 ../wdir/.hgsubstate
394 ../wdir/.hgsubstate
358 ../wdir/foo/bar/abc
395 ../wdir/foo/bar/abc
359 ../wdir/sub1/.hgsub
396 ../wdir/sub1/.hgsub
360 ../wdir/sub1/.hgsubstate
397 ../wdir/sub1/.hgsubstate
361 ../wdir/sub1/foo
398 ../wdir/sub1/foo
362 ../wdir/sub1/sub2/folder/test.txt
399 ../wdir/sub1/sub2/folder/test.txt
363 ../wdir/sub1/sub2/sub2
400 ../wdir/sub1/sub2/sub2
364
401
365 Continue relative path printing + subrepos
402 Continue relative path printing + subrepos
366 $ hg update -Cq
403 $ hg update -Cq
367 $ rm -r ../wdir
404 $ rm -r ../wdir
368 $ hg archive -S -r 'wdir()' ../wdir
405 $ hg archive -S -r 'wdir()' ../wdir
369 \r (no-eol) (esc)
406 \r (no-eol) (esc)
370 archiving [ ] 0/3\r (no-eol) (esc)
407 archiving [ ] 0/3\r (no-eol) (esc)
371 archiving [=============> ] 1/3\r (no-eol) (esc)
408 archiving [=============> ] 1/3\r (no-eol) (esc)
372 archiving [===========================> ] 2/3\r (no-eol) (esc)
409 archiving [===========================> ] 2/3\r (no-eol) (esc)
373 archiving [==========================================>] 3/3\r (no-eol) (esc)
410 archiving [==========================================>] 3/3\r (no-eol) (esc)
374 \r (no-eol) (esc)
411 \r (no-eol) (esc)
375 \r (no-eol) (esc)
412 \r (no-eol) (esc)
376 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
413 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
377 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
414 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
378 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
415 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
379 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
416 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
380 \r (no-eol) (esc)
417 \r (no-eol) (esc)
381 \r (no-eol) (esc)
418 \r (no-eol) (esc)
382 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (glob) (esc)
419 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (glob) (esc)
383 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (glob) (esc)
420 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (glob) (esc)
384 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (glob) (esc)
421 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (glob) (esc)
385 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (glob) (esc)
422 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (glob) (esc)
386 \r (no-eol) (esc)
423 \r (no-eol) (esc)
387 $ cat ../wdir/.hg_archival.txt
424 $ cat ../wdir/.hg_archival.txt
388 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
425 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
389 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd
426 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd
390 branch: default
427 branch: default
391 latesttag: null
428 latesttag: null
392 latesttagdistance: 4
429 latesttagdistance: 4
393 changessincelatesttag: 4
430 changessincelatesttag: 4
394
431
395 $ touch sub1/sub2/folder/bar
432 $ touch sub1/sub2/folder/bar
396 $ hg addremove sub1/sub2
433 $ hg addremove sub1/sub2
397 adding sub1/sub2/folder/bar (glob)
434 adding sub1/sub2/folder/bar (glob)
398 $ hg status -S
435 $ hg status -S
399 A sub1/sub2/folder/bar
436 A sub1/sub2/folder/bar
400 ? foo/bar/abc
437 ? foo/bar/abc
401 ? sub1/foo
438 ? sub1/foo
402 $ hg update -Cq
439 $ hg update -Cq
403 $ hg addremove sub1
440 $ hg addremove sub1
404 adding sub1/sub2/folder/bar (glob)
441 adding sub1/sub2/folder/bar (glob)
405 adding sub1/foo (glob)
442 adding sub1/foo (glob)
406 $ hg update -Cq
443 $ hg update -Cq
407 $ rm sub1/sub2/folder/test.txt
444 $ rm sub1/sub2/folder/test.txt
408 $ rm sub1/sub2/test.txt
445 $ rm sub1/sub2/test.txt
409 $ hg ci -ASm "remove test.txt"
446 $ hg ci -ASm "remove test.txt"
410 adding sub1/sub2/folder/bar
447 adding sub1/sub2/folder/bar
411 removing sub1/sub2/folder/test.txt
448 removing sub1/sub2/folder/test.txt
412 removing sub1/sub2/test.txt
449 removing sub1/sub2/test.txt
413 adding sub1/foo
450 adding sub1/foo
414 adding foo/bar/abc
451 adding foo/bar/abc
415 committing subrepository sub1
452 committing subrepository sub1
416 committing subrepository sub1/sub2 (glob)
453 committing subrepository sub1/sub2 (glob)
417
454
418 $ hg forget sub1/sub2/sub2
455 $ hg forget sub1/sub2/sub2
419 $ echo x > sub1/sub2/x.txt
456 $ echo x > sub1/sub2/x.txt
420 $ hg add sub1/sub2/x.txt
457 $ hg add sub1/sub2/x.txt
421
458
422 Files sees uncommitted adds and removes in subrepos
459 Files sees uncommitted adds and removes in subrepos
423 $ hg files -S
460 $ hg files -S
424 .hgsub
461 .hgsub
425 .hgsubstate
462 .hgsubstate
426 foo/bar/abc (glob)
463 foo/bar/abc (glob)
427 main
464 main
428 sub1/.hgsub (glob)
465 sub1/.hgsub (glob)
429 sub1/.hgsubstate (glob)
466 sub1/.hgsubstate (glob)
430 sub1/foo (glob)
467 sub1/foo (glob)
431 sub1/sub1 (glob)
468 sub1/sub1 (glob)
432 sub1/sub2/folder/bar (glob)
469 sub1/sub2/folder/bar (glob)
433 sub1/sub2/x.txt (glob)
470 sub1/sub2/x.txt (glob)
434
471
435 $ hg files -S "set:eol('dos') or eol('unix') or size('<= 0')"
472 $ hg files -S "set:eol('dos') or eol('unix') or size('<= 0')"
436 .hgsub
473 .hgsub
437 .hgsubstate
474 .hgsubstate
438 foo/bar/abc (glob)
475 foo/bar/abc (glob)
439 main
476 main
440 sub1/.hgsub (glob)
477 sub1/.hgsub (glob)
441 sub1/.hgsubstate (glob)
478 sub1/.hgsubstate (glob)
442 sub1/foo (glob)
479 sub1/foo (glob)
443 sub1/sub1 (glob)
480 sub1/sub1 (glob)
444 sub1/sub2/folder/bar (glob)
481 sub1/sub2/folder/bar (glob)
445 sub1/sub2/x.txt (glob)
482 sub1/sub2/x.txt (glob)
446
483
447 $ hg files -r '.^' -S "set:eol('dos') or eol('unix')"
484 $ hg files -r '.^' -S "set:eol('dos') or eol('unix')"
448 .hgsub
485 .hgsub
449 .hgsubstate
486 .hgsubstate
450 main
487 main
451 sub1/.hgsub (glob)
488 sub1/.hgsub (glob)
452 sub1/.hgsubstate (glob)
489 sub1/.hgsubstate (glob)
453 sub1/sub1 (glob)
490 sub1/sub1 (glob)
454 sub1/sub2/folder/test.txt (glob)
491 sub1/sub2/folder/test.txt (glob)
455 sub1/sub2/sub2 (glob)
492 sub1/sub2/sub2 (glob)
456 sub1/sub2/test.txt (glob)
493 sub1/sub2/test.txt (glob)
457
494
458 $ hg files sub1
495 $ hg files sub1
459 sub1/.hgsub (glob)
496 sub1/.hgsub (glob)
460 sub1/.hgsubstate (glob)
497 sub1/.hgsubstate (glob)
461 sub1/foo (glob)
498 sub1/foo (glob)
462 sub1/sub1 (glob)
499 sub1/sub1 (glob)
463 sub1/sub2/folder/bar (glob)
500 sub1/sub2/folder/bar (glob)
464 sub1/sub2/x.txt (glob)
501 sub1/sub2/x.txt (glob)
465
502
466 $ hg files sub1/sub2
503 $ hg files sub1/sub2
467 sub1/sub2/folder/bar (glob)
504 sub1/sub2/folder/bar (glob)
468 sub1/sub2/x.txt (glob)
505 sub1/sub2/x.txt (glob)
469
506
470 $ hg files
507 $ hg files
471 .hgsub
508 .hgsub
472 .hgsubstate
509 .hgsubstate
473 foo/bar/abc (glob)
510 foo/bar/abc (glob)
474 main
511 main
475
512
476 $ hg files -S -r '.^' sub1/sub2/folder
513 $ hg files -S -r '.^' sub1/sub2/folder
477 sub1/sub2/folder/test.txt (glob)
514 sub1/sub2/folder/test.txt (glob)
478
515
479 $ hg files -S -r '.^' sub1/sub2/missing
516 $ hg files -S -r '.^' sub1/sub2/missing
480 sub1/sub2/missing: no such file in rev 78026e779ea6 (glob)
517 sub1/sub2/missing: no such file in rev 78026e779ea6 (glob)
481 [1]
518 [1]
482
519
483 $ hg files -r '.^' sub1/
520 $ hg files -r '.^' sub1/
484 sub1/.hgsub (glob)
521 sub1/.hgsub (glob)
485 sub1/.hgsubstate (glob)
522 sub1/.hgsubstate (glob)
486 sub1/sub1 (glob)
523 sub1/sub1 (glob)
487 sub1/sub2/folder/test.txt (glob)
524 sub1/sub2/folder/test.txt (glob)
488 sub1/sub2/sub2 (glob)
525 sub1/sub2/sub2 (glob)
489 sub1/sub2/test.txt (glob)
526 sub1/sub2/test.txt (glob)
490
527
491 $ hg files -r '.^' sub1/sub2
528 $ hg files -r '.^' sub1/sub2
492 sub1/sub2/folder/test.txt (glob)
529 sub1/sub2/folder/test.txt (glob)
493 sub1/sub2/sub2 (glob)
530 sub1/sub2/sub2 (glob)
494 sub1/sub2/test.txt (glob)
531 sub1/sub2/test.txt (glob)
495
532
496 $ hg rollback -q
533 $ hg rollback -q
497 $ hg up -Cq
534 $ hg up -Cq
498
535
499 $ hg --config extensions.largefiles=! archive -S ../archive_all
536 $ hg --config extensions.largefiles=! archive -S ../archive_all
500 \r (no-eol) (esc)
537 \r (no-eol) (esc)
501 archiving [ ] 0/3\r (no-eol) (esc)
538 archiving [ ] 0/3\r (no-eol) (esc)
502 archiving [=============> ] 1/3\r (no-eol) (esc)
539 archiving [=============> ] 1/3\r (no-eol) (esc)
503 archiving [===========================> ] 2/3\r (no-eol) (esc)
540 archiving [===========================> ] 2/3\r (no-eol) (esc)
504 archiving [==========================================>] 3/3\r (no-eol) (esc)
541 archiving [==========================================>] 3/3\r (no-eol) (esc)
505 \r (no-eol) (esc)
542 \r (no-eol) (esc)
506 \r (no-eol) (esc)
543 \r (no-eol) (esc)
507 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
544 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
508 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
545 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
509 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
546 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
510 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
547 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
511 \r (no-eol) (esc)
548 \r (no-eol) (esc)
512 \r (no-eol) (esc)
549 \r (no-eol) (esc)
513 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (glob) (esc)
550 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (glob) (esc)
514 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (glob) (esc)
551 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (glob) (esc)
515 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (glob) (esc)
552 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (glob) (esc)
516 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (glob) (esc)
553 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (glob) (esc)
517 \r (no-eol) (esc)
554 \r (no-eol) (esc)
518 $ find ../archive_all | sort
555 $ find ../archive_all | sort
519 ../archive_all
556 ../archive_all
520 ../archive_all/.hg_archival.txt
557 ../archive_all/.hg_archival.txt
521 ../archive_all/.hgsub
558 ../archive_all/.hgsub
522 ../archive_all/.hgsubstate
559 ../archive_all/.hgsubstate
523 ../archive_all/main
560 ../archive_all/main
524 ../archive_all/sub1
561 ../archive_all/sub1
525 ../archive_all/sub1/.hgsub
562 ../archive_all/sub1/.hgsub
526 ../archive_all/sub1/.hgsubstate
563 ../archive_all/sub1/.hgsubstate
527 ../archive_all/sub1/sub1
564 ../archive_all/sub1/sub1
528 ../archive_all/sub1/sub2
565 ../archive_all/sub1/sub2
529 ../archive_all/sub1/sub2/folder
566 ../archive_all/sub1/sub2/folder
530 ../archive_all/sub1/sub2/folder/test.txt
567 ../archive_all/sub1/sub2/folder/test.txt
531 ../archive_all/sub1/sub2/sub2
568 ../archive_all/sub1/sub2/sub2
532 ../archive_all/sub1/sub2/test.txt
569 ../archive_all/sub1/sub2/test.txt
533
570
534 Check that archive -X works in deep subrepos
571 Check that archive -X works in deep subrepos
535
572
536 $ hg --config extensions.largefiles=! archive -S -X '**test*' ../archive_exclude
573 $ hg --config extensions.largefiles=! archive -S -X '**test*' ../archive_exclude
537 \r (no-eol) (esc)
574 \r (no-eol) (esc)
538 archiving [ ] 0/3\r (no-eol) (esc)
575 archiving [ ] 0/3\r (no-eol) (esc)
539 archiving [=============> ] 1/3\r (no-eol) (esc)
576 archiving [=============> ] 1/3\r (no-eol) (esc)
540 archiving [===========================> ] 2/3\r (no-eol) (esc)
577 archiving [===========================> ] 2/3\r (no-eol) (esc)
541 archiving [==========================================>] 3/3\r (no-eol) (esc)
578 archiving [==========================================>] 3/3\r (no-eol) (esc)
542 \r (no-eol) (esc)
579 \r (no-eol) (esc)
543 \r (no-eol) (esc)
580 \r (no-eol) (esc)
544 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
581 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
545 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
582 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
546 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
583 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
547 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
584 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
548 \r (no-eol) (esc)
585 \r (no-eol) (esc)
549 \r (no-eol) (esc)
586 \r (no-eol) (esc)
550 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (glob) (esc)
587 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (glob) (esc)
551 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (glob) (esc)
588 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (glob) (esc)
552 \r (no-eol) (esc)
589 \r (no-eol) (esc)
553 $ find ../archive_exclude | sort
590 $ find ../archive_exclude | sort
554 ../archive_exclude
591 ../archive_exclude
555 ../archive_exclude/.hg_archival.txt
592 ../archive_exclude/.hg_archival.txt
556 ../archive_exclude/.hgsub
593 ../archive_exclude/.hgsub
557 ../archive_exclude/.hgsubstate
594 ../archive_exclude/.hgsubstate
558 ../archive_exclude/main
595 ../archive_exclude/main
559 ../archive_exclude/sub1
596 ../archive_exclude/sub1
560 ../archive_exclude/sub1/.hgsub
597 ../archive_exclude/sub1/.hgsub
561 ../archive_exclude/sub1/.hgsubstate
598 ../archive_exclude/sub1/.hgsubstate
562 ../archive_exclude/sub1/sub1
599 ../archive_exclude/sub1/sub1
563 ../archive_exclude/sub1/sub2
600 ../archive_exclude/sub1/sub2
564 ../archive_exclude/sub1/sub2/sub2
601 ../archive_exclude/sub1/sub2/sub2
565
602
566 $ hg --config extensions.largefiles=! archive -S -I '**test*' ../archive_include
603 $ hg --config extensions.largefiles=! archive -S -I '**test*' ../archive_include
567 \r (no-eol) (esc)
604 \r (no-eol) (esc)
568 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
605 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
569 \r (no-eol) (esc)
606 \r (no-eol) (esc)
570 \r (no-eol) (esc)
607 \r (no-eol) (esc)
571 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (glob) (esc)
608 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (glob) (esc)
572 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (glob) (esc)
609 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (glob) (esc)
573 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (glob) (esc)
610 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (glob) (esc)
574 \r (no-eol) (esc)
611 \r (no-eol) (esc)
575 $ find ../archive_include | sort
612 $ find ../archive_include | sort
576 ../archive_include
613 ../archive_include
577 ../archive_include/sub1
614 ../archive_include/sub1
578 ../archive_include/sub1/sub2
615 ../archive_include/sub1/sub2
579 ../archive_include/sub1/sub2/folder
616 ../archive_include/sub1/sub2/folder
580 ../archive_include/sub1/sub2/folder/test.txt
617 ../archive_include/sub1/sub2/folder/test.txt
581 ../archive_include/sub1/sub2/test.txt
618 ../archive_include/sub1/sub2/test.txt
582
619
583 Check that deep archive works with largefiles (which overrides hgsubrepo impl)
620 Check that deep archive works with largefiles (which overrides hgsubrepo impl)
584 This also tests the repo.ui regression in 43fb170a23bd, and that lf subrepo
621 This also tests the repo.ui regression in 43fb170a23bd, and that lf subrepo
585 subrepos are archived properly.
622 subrepos are archived properly.
586 Note that add --large through a subrepo currently adds the file as a normal file
623 Note that add --large through a subrepo currently adds the file as a normal file
587
624
588 $ echo "large" > sub1/sub2/large.bin
625 $ echo "large" > sub1/sub2/large.bin
589 $ hg --config extensions.largefiles= add --large -R sub1/sub2 sub1/sub2/large.bin
626 $ hg --config extensions.largefiles= add --large -R sub1/sub2 sub1/sub2/large.bin
590 $ echo "large" > large.bin
627 $ echo "large" > large.bin
591 $ hg --config extensions.largefiles= add --large large.bin
628 $ hg --config extensions.largefiles= add --large large.bin
592 $ hg --config extensions.largefiles= ci -S -m "add large files"
629 $ hg --config extensions.largefiles= ci -S -m "add large files"
593 committing subrepository sub1
630 committing subrepository sub1
594 committing subrepository sub1/sub2 (glob)
631 committing subrepository sub1/sub2 (glob)
595
632
596 $ hg --config extensions.largefiles= archive -S ../archive_lf
633 $ hg --config extensions.largefiles= archive -S ../archive_lf
597 $ find ../archive_lf | sort
634 $ find ../archive_lf | sort
598 ../archive_lf
635 ../archive_lf
599 ../archive_lf/.hg_archival.txt
636 ../archive_lf/.hg_archival.txt
600 ../archive_lf/.hgsub
637 ../archive_lf/.hgsub
601 ../archive_lf/.hgsubstate
638 ../archive_lf/.hgsubstate
602 ../archive_lf/large.bin
639 ../archive_lf/large.bin
603 ../archive_lf/main
640 ../archive_lf/main
604 ../archive_lf/sub1
641 ../archive_lf/sub1
605 ../archive_lf/sub1/.hgsub
642 ../archive_lf/sub1/.hgsub
606 ../archive_lf/sub1/.hgsubstate
643 ../archive_lf/sub1/.hgsubstate
607 ../archive_lf/sub1/sub1
644 ../archive_lf/sub1/sub1
608 ../archive_lf/sub1/sub2
645 ../archive_lf/sub1/sub2
609 ../archive_lf/sub1/sub2/folder
646 ../archive_lf/sub1/sub2/folder
610 ../archive_lf/sub1/sub2/folder/test.txt
647 ../archive_lf/sub1/sub2/folder/test.txt
611 ../archive_lf/sub1/sub2/large.bin
648 ../archive_lf/sub1/sub2/large.bin
612 ../archive_lf/sub1/sub2/sub2
649 ../archive_lf/sub1/sub2/sub2
613 ../archive_lf/sub1/sub2/test.txt
650 ../archive_lf/sub1/sub2/test.txt
614 $ rm -rf ../archive_lf
651 $ rm -rf ../archive_lf
615
652
616 Exclude large files from main and sub-sub repo
653 Exclude large files from main and sub-sub repo
617
654
618 $ hg --config extensions.largefiles= archive -S -X '**.bin' ../archive_lf
655 $ hg --config extensions.largefiles= archive -S -X '**.bin' ../archive_lf
619 $ find ../archive_lf | sort
656 $ find ../archive_lf | sort
620 ../archive_lf
657 ../archive_lf
621 ../archive_lf/.hg_archival.txt
658 ../archive_lf/.hg_archival.txt
622 ../archive_lf/.hgsub
659 ../archive_lf/.hgsub
623 ../archive_lf/.hgsubstate
660 ../archive_lf/.hgsubstate
624 ../archive_lf/main
661 ../archive_lf/main
625 ../archive_lf/sub1
662 ../archive_lf/sub1
626 ../archive_lf/sub1/.hgsub
663 ../archive_lf/sub1/.hgsub
627 ../archive_lf/sub1/.hgsubstate
664 ../archive_lf/sub1/.hgsubstate
628 ../archive_lf/sub1/sub1
665 ../archive_lf/sub1/sub1
629 ../archive_lf/sub1/sub2
666 ../archive_lf/sub1/sub2
630 ../archive_lf/sub1/sub2/folder
667 ../archive_lf/sub1/sub2/folder
631 ../archive_lf/sub1/sub2/folder/test.txt
668 ../archive_lf/sub1/sub2/folder/test.txt
632 ../archive_lf/sub1/sub2/sub2
669 ../archive_lf/sub1/sub2/sub2
633 ../archive_lf/sub1/sub2/test.txt
670 ../archive_lf/sub1/sub2/test.txt
634 $ rm -rf ../archive_lf
671 $ rm -rf ../archive_lf
635
672
636 Exclude normal files from main and sub-sub repo
673 Exclude normal files from main and sub-sub repo
637
674
638 $ hg --config extensions.largefiles= archive -S -X '**.txt' -p '.' ../archive_lf.tgz
675 $ hg --config extensions.largefiles= archive -S -X '**.txt' -p '.' ../archive_lf.tgz
639 $ tar -tzf ../archive_lf.tgz | sort
676 $ tar -tzf ../archive_lf.tgz | sort
640 .hgsub
677 .hgsub
641 .hgsubstate
678 .hgsubstate
642 large.bin
679 large.bin
643 main
680 main
644 sub1/.hgsub
681 sub1/.hgsub
645 sub1/.hgsubstate
682 sub1/.hgsubstate
646 sub1/sub1
683 sub1/sub1
647 sub1/sub2/large.bin
684 sub1/sub2/large.bin
648 sub1/sub2/sub2
685 sub1/sub2/sub2
649
686
650 Include normal files from within a largefiles subrepo
687 Include normal files from within a largefiles subrepo
651
688
652 $ hg --config extensions.largefiles= archive -S -I '**.txt' ../archive_lf
689 $ hg --config extensions.largefiles= archive -S -I '**.txt' ../archive_lf
653 $ find ../archive_lf | sort
690 $ find ../archive_lf | sort
654 ../archive_lf
691 ../archive_lf
655 ../archive_lf/.hg_archival.txt
692 ../archive_lf/.hg_archival.txt
656 ../archive_lf/sub1
693 ../archive_lf/sub1
657 ../archive_lf/sub1/sub2
694 ../archive_lf/sub1/sub2
658 ../archive_lf/sub1/sub2/folder
695 ../archive_lf/sub1/sub2/folder
659 ../archive_lf/sub1/sub2/folder/test.txt
696 ../archive_lf/sub1/sub2/folder/test.txt
660 ../archive_lf/sub1/sub2/test.txt
697 ../archive_lf/sub1/sub2/test.txt
661 $ rm -rf ../archive_lf
698 $ rm -rf ../archive_lf
662
699
663 Include large files from within a largefiles subrepo
700 Include large files from within a largefiles subrepo
664
701
665 $ hg --config extensions.largefiles= archive -S -I '**.bin' ../archive_lf
702 $ hg --config extensions.largefiles= archive -S -I '**.bin' ../archive_lf
666 $ find ../archive_lf | sort
703 $ find ../archive_lf | sort
667 ../archive_lf
704 ../archive_lf
668 ../archive_lf/large.bin
705 ../archive_lf/large.bin
669 ../archive_lf/sub1
706 ../archive_lf/sub1
670 ../archive_lf/sub1/sub2
707 ../archive_lf/sub1/sub2
671 ../archive_lf/sub1/sub2/large.bin
708 ../archive_lf/sub1/sub2/large.bin
672 $ rm -rf ../archive_lf
709 $ rm -rf ../archive_lf
673
710
674 Find an exact largefile match in a largefiles subrepo
711 Find an exact largefile match in a largefiles subrepo
675
712
676 $ hg --config extensions.largefiles= archive -S -I 'sub1/sub2/large.bin' ../archive_lf
713 $ hg --config extensions.largefiles= archive -S -I 'sub1/sub2/large.bin' ../archive_lf
677 $ find ../archive_lf | sort
714 $ find ../archive_lf | sort
678 ../archive_lf
715 ../archive_lf
679 ../archive_lf/sub1
716 ../archive_lf/sub1
680 ../archive_lf/sub1/sub2
717 ../archive_lf/sub1/sub2
681 ../archive_lf/sub1/sub2/large.bin
718 ../archive_lf/sub1/sub2/large.bin
682 $ rm -rf ../archive_lf
719 $ rm -rf ../archive_lf
683
720
684 The local repo enables largefiles if a largefiles repo is cloned
721 The local repo enables largefiles if a largefiles repo is cloned
685 $ hg showconfig extensions
722 $ hg showconfig extensions
686 abort: repository requires features unknown to this Mercurial: largefiles!
723 abort: repository requires features unknown to this Mercurial: largefiles!
687 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
724 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
688 [255]
725 [255]
689 $ hg --config extensions.largefiles= clone -qU . ../lfclone
726 $ hg --config extensions.largefiles= clone -qU . ../lfclone
690 $ cat ../lfclone/.hg/hgrc
727 $ cat ../lfclone/.hg/hgrc
691 # example repository config (see 'hg help config' for more info)
728 # example repository config (see 'hg help config' for more info)
692 [paths]
729 [paths]
693 default = $TESTTMP/cloned (glob)
730 default = $TESTTMP/cloned (glob)
694
731
695 # path aliases to other clones of this repo in URLs or filesystem paths
732 # path aliases to other clones of this repo in URLs or filesystem paths
696 # (see 'hg help config.paths' for more info)
733 # (see 'hg help config.paths' for more info)
697 #
734 #
698 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
735 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
699 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
736 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
700 # my-clone = /home/jdoe/jdoes-clone
737 # my-clone = /home/jdoe/jdoes-clone
701
738
702 [ui]
739 [ui]
703 # name and email (local to this repository, optional), e.g.
740 # name and email (local to this repository, optional), e.g.
704 # username = Jane Doe <jdoe@example.com>
741 # username = Jane Doe <jdoe@example.com>
705
742
706 [extensions]
743 [extensions]
707 largefiles=
744 largefiles=
708
745
709 Find an exact match to a standin (should archive nothing)
746 Find an exact match to a standin (should archive nothing)
710 $ hg --config extensions.largefiles= archive -S -I 'sub/sub2/.hglf/large.bin' ../archive_lf
747 $ hg --config extensions.largefiles= archive -S -I 'sub/sub2/.hglf/large.bin' ../archive_lf
711 $ find ../archive_lf 2> /dev/null | sort
748 $ find ../archive_lf 2> /dev/null | sort
712
749
713 $ cat >> $HGRCPATH <<EOF
750 $ cat >> $HGRCPATH <<EOF
714 > [extensions]
751 > [extensions]
715 > largefiles=
752 > largefiles=
716 > [largefiles]
753 > [largefiles]
717 > patterns=glob:**.dat
754 > patterns=glob:**.dat
718 > EOF
755 > EOF
719
756
720 Test forget through a deep subrepo with the largefiles extension, both a
757 Test forget through a deep subrepo with the largefiles extension, both a
721 largefile and a normal file. Then a largefile that hasn't been committed yet.
758 largefile and a normal file. Then a largefile that hasn't been committed yet.
722 $ touch sub1/sub2/untracked.txt
759 $ touch sub1/sub2/untracked.txt
723 $ touch sub1/sub2/large.dat
760 $ touch sub1/sub2/large.dat
724 $ hg forget sub1/sub2/large.bin sub1/sub2/test.txt sub1/sub2/untracked.txt
761 $ hg forget sub1/sub2/large.bin sub1/sub2/test.txt sub1/sub2/untracked.txt
725 not removing sub1/sub2/untracked.txt: file is already untracked (glob)
762 not removing sub1/sub2/untracked.txt: file is already untracked (glob)
726 [1]
763 [1]
727 $ hg add --large --dry-run -v sub1/sub2/untracked.txt
764 $ hg add --large --dry-run -v sub1/sub2/untracked.txt
728 adding sub1/sub2/untracked.txt as a largefile (glob)
765 adding sub1/sub2/untracked.txt as a largefile (glob)
729 $ hg add --large -v sub1/sub2/untracked.txt
766 $ hg add --large -v sub1/sub2/untracked.txt
730 adding sub1/sub2/untracked.txt as a largefile (glob)
767 adding sub1/sub2/untracked.txt as a largefile (glob)
731 $ hg add --normal -v sub1/sub2/large.dat
768 $ hg add --normal -v sub1/sub2/large.dat
732 adding sub1/sub2/large.dat (glob)
769 adding sub1/sub2/large.dat (glob)
733 $ hg forget -v sub1/sub2/untracked.txt
770 $ hg forget -v sub1/sub2/untracked.txt
734 removing sub1/sub2/untracked.txt (glob)
771 removing sub1/sub2/untracked.txt (glob)
735 $ hg status -S
772 $ hg status -S
736 A sub1/sub2/large.dat
773 A sub1/sub2/large.dat
737 R sub1/sub2/large.bin
774 R sub1/sub2/large.bin
738 R sub1/sub2/test.txt
775 R sub1/sub2/test.txt
739 ? foo/bar/abc
776 ? foo/bar/abc
740 ? sub1/sub2/untracked.txt
777 ? sub1/sub2/untracked.txt
741 ? sub1/sub2/x.txt
778 ? sub1/sub2/x.txt
742 $ hg add sub1/sub2
779 $ hg add sub1/sub2
743
780
744 $ hg archive -S -r 'wdir()' ../wdir2
781 $ hg archive -S -r 'wdir()' ../wdir2
745 $ diff -r . ../wdir2 | egrep -v '\.hg$|^Common subdirectories:'
782 $ diff -r . ../wdir2 | egrep -v '\.hg$|^Common subdirectories:'
746 Only in ../wdir2: .hg_archival.txt
783 Only in ../wdir2: .hg_archival.txt
747 Only in .: .hglf
784 Only in .: .hglf
748 Only in .: foo
785 Only in .: foo
749 Only in ./sub1/sub2: large.bin
786 Only in ./sub1/sub2: large.bin
750 Only in ./sub1/sub2: test.txt
787 Only in ./sub1/sub2: test.txt
751 Only in ./sub1/sub2: untracked.txt
788 Only in ./sub1/sub2: untracked.txt
752 Only in ./sub1/sub2: x.txt
789 Only in ./sub1/sub2: x.txt
753 $ find ../wdir2 -type f | sort
790 $ find ../wdir2 -type f | sort
754 ../wdir2/.hg_archival.txt
791 ../wdir2/.hg_archival.txt
755 ../wdir2/.hgsub
792 ../wdir2/.hgsub
756 ../wdir2/.hgsubstate
793 ../wdir2/.hgsubstate
757 ../wdir2/large.bin
794 ../wdir2/large.bin
758 ../wdir2/main
795 ../wdir2/main
759 ../wdir2/sub1/.hgsub
796 ../wdir2/sub1/.hgsub
760 ../wdir2/sub1/.hgsubstate
797 ../wdir2/sub1/.hgsubstate
761 ../wdir2/sub1/sub1
798 ../wdir2/sub1/sub1
762 ../wdir2/sub1/sub2/folder/test.txt
799 ../wdir2/sub1/sub2/folder/test.txt
763 ../wdir2/sub1/sub2/large.dat
800 ../wdir2/sub1/sub2/large.dat
764 ../wdir2/sub1/sub2/sub2
801 ../wdir2/sub1/sub2/sub2
765 $ hg status -S -mac -n | sort
802 $ hg status -S -mac -n | sort
766 .hgsub
803 .hgsub
767 .hgsubstate
804 .hgsubstate
768 large.bin
805 large.bin
769 main
806 main
770 sub1/.hgsub
807 sub1/.hgsub
771 sub1/.hgsubstate
808 sub1/.hgsubstate
772 sub1/sub1
809 sub1/sub1
773 sub1/sub2/folder/test.txt
810 sub1/sub2/folder/test.txt
774 sub1/sub2/large.dat
811 sub1/sub2/large.dat
775 sub1/sub2/sub2
812 sub1/sub2/sub2
776
813
777 $ hg ci -Sqm 'forget testing'
814 $ hg ci -Sqm 'forget testing'
778
815
779 Test 'wdir()' modified file archiving with largefiles
816 Test 'wdir()' modified file archiving with largefiles
780 $ echo 'mod' > main
817 $ echo 'mod' > main
781 $ echo 'mod' > large.bin
818 $ echo 'mod' > large.bin
782 $ echo 'mod' > sub1/sub2/large.dat
819 $ echo 'mod' > sub1/sub2/large.dat
783 $ hg archive -S -r 'wdir()' ../wdir3
820 $ hg archive -S -r 'wdir()' ../wdir3
784 $ diff -r . ../wdir3 | egrep -v '\.hg$|^Common subdirectories'
821 $ diff -r . ../wdir3 | egrep -v '\.hg$|^Common subdirectories'
785 Only in ../wdir3: .hg_archival.txt
822 Only in ../wdir3: .hg_archival.txt
786 Only in .: .hglf
823 Only in .: .hglf
787 Only in .: foo
824 Only in .: foo
788 Only in ./sub1/sub2: large.bin
825 Only in ./sub1/sub2: large.bin
789 Only in ./sub1/sub2: test.txt
826 Only in ./sub1/sub2: test.txt
790 Only in ./sub1/sub2: untracked.txt
827 Only in ./sub1/sub2: untracked.txt
791 Only in ./sub1/sub2: x.txt
828 Only in ./sub1/sub2: x.txt
792 $ find ../wdir3 -type f | sort
829 $ find ../wdir3 -type f | sort
793 ../wdir3/.hg_archival.txt
830 ../wdir3/.hg_archival.txt
794 ../wdir3/.hgsub
831 ../wdir3/.hgsub
795 ../wdir3/.hgsubstate
832 ../wdir3/.hgsubstate
796 ../wdir3/large.bin
833 ../wdir3/large.bin
797 ../wdir3/main
834 ../wdir3/main
798 ../wdir3/sub1/.hgsub
835 ../wdir3/sub1/.hgsub
799 ../wdir3/sub1/.hgsubstate
836 ../wdir3/sub1/.hgsubstate
800 ../wdir3/sub1/sub1
837 ../wdir3/sub1/sub1
801 ../wdir3/sub1/sub2/folder/test.txt
838 ../wdir3/sub1/sub2/folder/test.txt
802 ../wdir3/sub1/sub2/large.dat
839 ../wdir3/sub1/sub2/large.dat
803 ../wdir3/sub1/sub2/sub2
840 ../wdir3/sub1/sub2/sub2
804 $ hg up -Cq
841 $ hg up -Cq
805
842
806 Test issue4330: commit a directory where only normal files have changed
843 Test issue4330: commit a directory where only normal files have changed
807 $ touch foo/bar/large.dat
844 $ touch foo/bar/large.dat
808 $ hg add --large foo/bar/large.dat
845 $ hg add --large foo/bar/large.dat
809 $ hg ci -m 'add foo/bar/large.dat'
846 $ hg ci -m 'add foo/bar/large.dat'
810 $ touch a.txt
847 $ touch a.txt
811 $ touch a.dat
848 $ touch a.dat
812 $ hg add -v foo/bar/abc a.txt a.dat
849 $ hg add -v foo/bar/abc a.txt a.dat
813 adding a.dat as a largefile
850 adding a.dat as a largefile
814 adding a.txt
851 adding a.txt
815 adding foo/bar/abc (glob)
852 adding foo/bar/abc (glob)
816 $ hg ci -m 'dir commit with only normal file deltas' foo/bar
853 $ hg ci -m 'dir commit with only normal file deltas' foo/bar
817 $ hg status
854 $ hg status
818 A a.dat
855 A a.dat
819 A a.txt
856 A a.txt
820
857
821 Test a directory commit with a changed largefile and a changed normal file
858 Test a directory commit with a changed largefile and a changed normal file
822 $ echo changed > foo/bar/large.dat
859 $ echo changed > foo/bar/large.dat
823 $ echo changed > foo/bar/abc
860 $ echo changed > foo/bar/abc
824 $ hg ci -m 'dir commit with normal and lf file deltas' foo
861 $ hg ci -m 'dir commit with normal and lf file deltas' foo
825 $ hg status
862 $ hg status
826 A a.dat
863 A a.dat
827 A a.txt
864 A a.txt
828
865
829 $ hg ci -m "add a.*"
866 $ hg ci -m "add a.*"
830 $ hg mv a.dat b.dat
867 $ hg mv a.dat b.dat
831 $ hg mv foo/bar/abc foo/bar/def
868 $ hg mv foo/bar/abc foo/bar/def
832 $ hg status -C
869 $ hg status -C
833 A b.dat
870 A b.dat
834 a.dat
871 a.dat
835 A foo/bar/def
872 A foo/bar/def
836 foo/bar/abc
873 foo/bar/abc
837 R a.dat
874 R a.dat
838 R foo/bar/abc
875 R foo/bar/abc
839
876
840 $ hg ci -m "move large and normal"
877 $ hg ci -m "move large and normal"
841 $ hg status -C --rev '.^' --rev .
878 $ hg status -C --rev '.^' --rev .
842 A b.dat
879 A b.dat
843 a.dat
880 a.dat
844 A foo/bar/def
881 A foo/bar/def
845 foo/bar/abc
882 foo/bar/abc
846 R a.dat
883 R a.dat
847 R foo/bar/abc
884 R foo/bar/abc
848
885
849
886
850 $ echo foo > main
887 $ echo foo > main
851 $ hg ci -m "mod parent only"
888 $ hg ci -m "mod parent only"
852 $ hg init sub3
889 $ hg init sub3
853 $ echo "sub3 = sub3" >> .hgsub
890 $ echo "sub3 = sub3" >> .hgsub
854 $ echo xyz > sub3/a.txt
891 $ echo xyz > sub3/a.txt
855 $ hg add sub3/a.txt
892 $ hg add sub3/a.txt
856 $ hg ci -Sm "add sub3"
893 $ hg ci -Sm "add sub3"
857 committing subrepository sub3
894 committing subrepository sub3
858 $ cat .hgsub | grep -v sub3 > .hgsub1
895 $ cat .hgsub | grep -v sub3 > .hgsub1
859 $ mv .hgsub1 .hgsub
896 $ mv .hgsub1 .hgsub
860 $ hg ci -m "remove sub3"
897 $ hg ci -m "remove sub3"
861
898
862 $ hg log -r "subrepo()" --style compact
899 $ hg log -r "subrepo()" --style compact
863 0 7f491f53a367 1970-01-01 00:00 +0000 test
900 0 7f491f53a367 1970-01-01 00:00 +0000 test
864 main import
901 main import
865
902
866 1 ffe6649062fe 1970-01-01 00:00 +0000 test
903 1 ffe6649062fe 1970-01-01 00:00 +0000 test
867 deep nested modif should trigger a commit
904 deep nested modif should trigger a commit
868
905
869 2 9bb10eebee29 1970-01-01 00:00 +0000 test
906 2 9bb10eebee29 1970-01-01 00:00 +0000 test
870 add test.txt
907 add test.txt
871
908
872 3 7c64f035294f 1970-01-01 00:00 +0000 test
909 3 7c64f035294f 1970-01-01 00:00 +0000 test
873 add large files
910 add large files
874
911
875 4 f734a59e2e35 1970-01-01 00:00 +0000 test
912 4 f734a59e2e35 1970-01-01 00:00 +0000 test
876 forget testing
913 forget testing
877
914
878 11 9685a22af5db 1970-01-01 00:00 +0000 test
915 11 9685a22af5db 1970-01-01 00:00 +0000 test
879 add sub3
916 add sub3
880
917
881 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
918 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
882 remove sub3
919 remove sub3
883
920
884 $ hg log -r "subrepo('sub3')" --style compact
921 $ hg log -r "subrepo('sub3')" --style compact
885 11 9685a22af5db 1970-01-01 00:00 +0000 test
922 11 9685a22af5db 1970-01-01 00:00 +0000 test
886 add sub3
923 add sub3
887
924
888 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
925 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
889 remove sub3
926 remove sub3
890
927
891 $ hg log -r "subrepo('bogus')" --style compact
928 $ hg log -r "subrepo('bogus')" --style compact
892
929
893
930
894 Test .hgsubstate in the R state
931 Test .hgsubstate in the R state
895
932
896 $ hg rm .hgsub .hgsubstate
933 $ hg rm .hgsub .hgsubstate
897 \r (no-eol) (esc)
934 \r (no-eol) (esc)
898 deleting [=====================> ] 1/2\r (no-eol) (esc)
935 deleting [=====================> ] 1/2\r (no-eol) (esc)
899 deleting [===========================================>] 2/2\r (no-eol) (esc)
936 deleting [===========================================>] 2/2\r (no-eol) (esc)
900 \r (no-eol) (esc)
937 \r (no-eol) (esc)
901 $ hg ci -m 'trash subrepo tracking'
938 $ hg ci -m 'trash subrepo tracking'
902
939
903 $ hg log -r "subrepo('re:sub\d+')" --style compact
940 $ hg log -r "subrepo('re:sub\d+')" --style compact
904 0 7f491f53a367 1970-01-01 00:00 +0000 test
941 0 7f491f53a367 1970-01-01 00:00 +0000 test
905 main import
942 main import
906
943
907 1 ffe6649062fe 1970-01-01 00:00 +0000 test
944 1 ffe6649062fe 1970-01-01 00:00 +0000 test
908 deep nested modif should trigger a commit
945 deep nested modif should trigger a commit
909
946
910 2 9bb10eebee29 1970-01-01 00:00 +0000 test
947 2 9bb10eebee29 1970-01-01 00:00 +0000 test
911 add test.txt
948 add test.txt
912
949
913 3 7c64f035294f 1970-01-01 00:00 +0000 test
950 3 7c64f035294f 1970-01-01 00:00 +0000 test
914 add large files
951 add large files
915
952
916 4 f734a59e2e35 1970-01-01 00:00 +0000 test
953 4 f734a59e2e35 1970-01-01 00:00 +0000 test
917 forget testing
954 forget testing
918
955
919 11 9685a22af5db 1970-01-01 00:00 +0000 test
956 11 9685a22af5db 1970-01-01 00:00 +0000 test
920 add sub3
957 add sub3
921
958
922 12 2e0485b475b9 1970-01-01 00:00 +0000 test
959 12 2e0485b475b9 1970-01-01 00:00 +0000 test
923 remove sub3
960 remove sub3
924
961
925 13[tip] a68b2c361653 1970-01-01 00:00 +0000 test
962 13[tip] a68b2c361653 1970-01-01 00:00 +0000 test
926 trash subrepo tracking
963 trash subrepo tracking
927
964
928
965
929 Restore the trashed subrepo tracking
966 Restore the trashed subrepo tracking
930
967
931 $ hg rollback -q
968 $ hg rollback -q
932 $ hg update -Cq .
969 $ hg update -Cq .
933
970
934 Interaction with extdiff, largefiles and subrepos
971 Interaction with extdiff, largefiles and subrepos
935
972
936 $ hg --config extensions.extdiff= pdiff -S
973 $ hg --config extensions.extdiff= pdiff -S
937
974
938 $ hg --config extensions.extdiff= pdiff -r '.^' -S
975 $ hg --config extensions.extdiff= pdiff -r '.^' -S
939 \r (no-eol) (esc)
976 \r (no-eol) (esc)
940 archiving [ ] 0/2\r (no-eol) (esc)
977 archiving [ ] 0/2\r (no-eol) (esc)
941 archiving [====================> ] 1/2\r (no-eol) (esc)
978 archiving [====================> ] 1/2\r (no-eol) (esc)
942 archiving [==========================================>] 2/2\r (no-eol) (esc)
979 archiving [==========================================>] 2/2\r (no-eol) (esc)
943 \r (no-eol) (esc)
980 \r (no-eol) (esc)
944 \r (no-eol) (esc)
981 \r (no-eol) (esc)
945 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
982 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
946 \r (no-eol) (esc)
983 \r (no-eol) (esc)
947 \r (no-eol) (esc)
984 \r (no-eol) (esc)
948 archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (glob) (esc)
985 archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (glob) (esc)
949 \r (no-eol) (esc)
986 \r (no-eol) (esc)
950 \r (no-eol) (esc)
987 \r (no-eol) (esc)
951 archiving (sub3) [ <=> ] 0\r (no-eol) (esc)
988 archiving (sub3) [ <=> ] 0\r (no-eol) (esc)
952 \r (no-eol) (esc)
989 \r (no-eol) (esc)
953 \r (no-eol) (esc)
990 \r (no-eol) (esc)
954 archiving [ ] 0/2\r (no-eol) (esc)
991 archiving [ ] 0/2\r (no-eol) (esc)
955 archiving [====================> ] 1/2\r (no-eol) (esc)
992 archiving [====================> ] 1/2\r (no-eol) (esc)
956 archiving [==========================================>] 2/2\r (no-eol) (esc)
993 archiving [==========================================>] 2/2\r (no-eol) (esc)
957 \r (no-eol) (esc)
994 \r (no-eol) (esc)
958 \r (no-eol) (esc)
995 \r (no-eol) (esc)
959 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
996 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
960 \r (no-eol) (esc)
997 \r (no-eol) (esc)
961 \r (no-eol) (esc)
998 \r (no-eol) (esc)
962 archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (glob) (esc)
999 archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (glob) (esc)
963 \r (no-eol) (esc)
1000 \r (no-eol) (esc)
964 diff -Nru cloned.*/.hgsub cloned/.hgsub (glob)
1001 diff -Nru cloned.*/.hgsub cloned/.hgsub (glob)
965 --- cloned.*/.hgsub * (glob)
1002 --- cloned.*/.hgsub * (glob)
966 +++ cloned/.hgsub * (glob)
1003 +++ cloned/.hgsub * (glob)
967 @@ -1,2 +1* @@ (glob)
1004 @@ -1,2 +1* @@ (glob)
968 sub1 = ../sub1
1005 sub1 = ../sub1
969 -sub3 = sub3
1006 -sub3 = sub3
970 diff -Nru cloned.*/.hgsubstate cloned/.hgsubstate (glob)
1007 diff -Nru cloned.*/.hgsubstate cloned/.hgsubstate (glob)
971 --- cloned.*/.hgsubstate * (glob)
1008 --- cloned.*/.hgsubstate * (glob)
972 +++ cloned/.hgsubstate * (glob)
1009 +++ cloned/.hgsubstate * (glob)
973 @@ -1,2 +1* @@ (glob)
1010 @@ -1,2 +1* @@ (glob)
974 7a36fa02b66e61f27f3d4a822809f159479b8ab2 sub1
1011 7a36fa02b66e61f27f3d4a822809f159479b8ab2 sub1
975 -b1a26de6f2a045a9f079323693614ee322f1ff7e sub3
1012 -b1a26de6f2a045a9f079323693614ee322f1ff7e sub3
976 [1]
1013 [1]
977
1014
978 $ hg --config extensions.extdiff= pdiff -r 0 -r '.^' -S
1015 $ hg --config extensions.extdiff= pdiff -r 0 -r '.^' -S
979 \r (no-eol) (esc)
1016 \r (no-eol) (esc)
980 archiving [ ] 0/3\r (no-eol) (esc)
1017 archiving [ ] 0/3\r (no-eol) (esc)
981 archiving [=============> ] 1/3\r (no-eol) (esc)
1018 archiving [=============> ] 1/3\r (no-eol) (esc)
982 archiving [===========================> ] 2/3\r (no-eol) (esc)
1019 archiving [===========================> ] 2/3\r (no-eol) (esc)
983 archiving [==========================================>] 3/3\r (no-eol) (esc)
1020 archiving [==========================================>] 3/3\r (no-eol) (esc)
984 \r (no-eol) (esc)
1021 \r (no-eol) (esc)
985 \r (no-eol) (esc)
1022 \r (no-eol) (esc)
986 archiving (sub1) [ ] 0/1\r (no-eol) (esc)
1023 archiving (sub1) [ ] 0/1\r (no-eol) (esc)
987 archiving (sub1) [===================================>] 1/1\r (no-eol) (esc)
1024 archiving (sub1) [===================================>] 1/1\r (no-eol) (esc)
988 \r (no-eol) (esc)
1025 \r (no-eol) (esc)
989 \r (no-eol) (esc)
1026 \r (no-eol) (esc)
990 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (glob) (esc)
1027 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (glob) (esc)
991 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (glob) (esc)
1028 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (glob) (esc)
992 \r (no-eol) (esc)
1029 \r (no-eol) (esc)
993 \r (no-eol) (esc)
1030 \r (no-eol) (esc)
994 archiving [ ] 0/8\r (no-eol) (esc)
1031 archiving [ ] 0/8\r (no-eol) (esc)
995 archiving [====> ] 1/8\r (no-eol) (esc)
1032 archiving [====> ] 1/8\r (no-eol) (esc)
996 archiving [=========> ] 2/8\r (no-eol) (esc)
1033 archiving [=========> ] 2/8\r (no-eol) (esc)
997 archiving [===============> ] 3/8\r (no-eol) (esc)
1034 archiving [===============> ] 3/8\r (no-eol) (esc)
998 archiving [====================> ] 4/8\r (no-eol) (esc)
1035 archiving [====================> ] 4/8\r (no-eol) (esc)
999 archiving [=========================> ] 5/8\r (no-eol) (esc)
1036 archiving [=========================> ] 5/8\r (no-eol) (esc)
1000 archiving [===============================> ] 6/8\r (no-eol) (esc)
1037 archiving [===============================> ] 6/8\r (no-eol) (esc)
1001 archiving [====================================> ] 7/8\r (no-eol) (esc)
1038 archiving [====================================> ] 7/8\r (no-eol) (esc)
1002 archiving [==========================================>] 8/8\r (no-eol) (esc)
1039 archiving [==========================================>] 8/8\r (no-eol) (esc)
1003 \r (no-eol) (esc)
1040 \r (no-eol) (esc)
1004 \r (no-eol) (esc)
1041 \r (no-eol) (esc)
1005 archiving (sub1) [ ] 0/1\r (no-eol) (esc)
1042 archiving (sub1) [ ] 0/1\r (no-eol) (esc)
1006 archiving (sub1) [===================================>] 1/1\r (no-eol) (esc)
1043 archiving (sub1) [===================================>] 1/1\r (no-eol) (esc)
1007 \r (no-eol) (esc)
1044 \r (no-eol) (esc)
1008 \r (no-eol) (esc)
1045 \r (no-eol) (esc)
1009 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (glob) (esc)
1046 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (glob) (esc)
1010 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (glob) (esc)
1047 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (glob) (esc)
1011 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (glob) (esc)
1048 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (glob) (esc)
1012 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (glob) (esc)
1049 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (glob) (esc)
1013 \r (no-eol) (esc)
1050 \r (no-eol) (esc)
1014 \r (no-eol) (esc)
1051 \r (no-eol) (esc)
1015 archiving (sub3) [ ] 0/1\r (no-eol) (esc)
1052 archiving (sub3) [ ] 0/1\r (no-eol) (esc)
1016 archiving (sub3) [===================================>] 1/1\r (no-eol) (esc)
1053 archiving (sub3) [===================================>] 1/1\r (no-eol) (esc)
1017 \r (no-eol) (esc)
1054 \r (no-eol) (esc)
1018 diff -Nru cloned.*/.hglf/b.dat cloned.*/.hglf/b.dat (glob)
1055 diff -Nru cloned.*/.hglf/b.dat cloned.*/.hglf/b.dat (glob)
1019 --- cloned.*/.hglf/b.dat * (glob)
1056 --- cloned.*/.hglf/b.dat * (glob)
1020 +++ cloned.*/.hglf/b.dat * (glob)
1057 +++ cloned.*/.hglf/b.dat * (glob)
1021 @@ -*,0 +1* @@ (glob)
1058 @@ -*,0 +1* @@ (glob)
1022 +da39a3ee5e6b4b0d3255bfef95601890afd80709
1059 +da39a3ee5e6b4b0d3255bfef95601890afd80709
1023 diff -Nru cloned.*/.hglf/foo/bar/large.dat cloned.*/.hglf/foo/bar/large.dat (glob)
1060 diff -Nru cloned.*/.hglf/foo/bar/large.dat cloned.*/.hglf/foo/bar/large.dat (glob)
1024 --- cloned.*/.hglf/foo/bar/large.dat * (glob)
1061 --- cloned.*/.hglf/foo/bar/large.dat * (glob)
1025 +++ cloned.*/.hglf/foo/bar/large.dat * (glob)
1062 +++ cloned.*/.hglf/foo/bar/large.dat * (glob)
1026 @@ -*,0 +1* @@ (glob)
1063 @@ -*,0 +1* @@ (glob)
1027 +2f6933b5ee0f5fdd823d9717d8729f3c2523811b
1064 +2f6933b5ee0f5fdd823d9717d8729f3c2523811b
1028 diff -Nru cloned.*/.hglf/large.bin cloned.*/.hglf/large.bin (glob)
1065 diff -Nru cloned.*/.hglf/large.bin cloned.*/.hglf/large.bin (glob)
1029 --- cloned.*/.hglf/large.bin * (glob)
1066 --- cloned.*/.hglf/large.bin * (glob)
1030 +++ cloned.*/.hglf/large.bin * (glob)
1067 +++ cloned.*/.hglf/large.bin * (glob)
1031 @@ -*,0 +1* @@ (glob)
1068 @@ -*,0 +1* @@ (glob)
1032 +7f7097b041ccf68cc5561e9600da4655d21c6d18
1069 +7f7097b041ccf68cc5561e9600da4655d21c6d18
1033 diff -Nru cloned.*/.hgsub cloned.*/.hgsub (glob)
1070 diff -Nru cloned.*/.hgsub cloned.*/.hgsub (glob)
1034 --- cloned.*/.hgsub * (glob)
1071 --- cloned.*/.hgsub * (glob)
1035 +++ cloned.*/.hgsub * (glob)
1072 +++ cloned.*/.hgsub * (glob)
1036 @@ -1* +1,2 @@ (glob)
1073 @@ -1* +1,2 @@ (glob)
1037 sub1 = ../sub1
1074 sub1 = ../sub1
1038 +sub3 = sub3
1075 +sub3 = sub3
1039 diff -Nru cloned.*/.hgsubstate cloned.*/.hgsubstate (glob)
1076 diff -Nru cloned.*/.hgsubstate cloned.*/.hgsubstate (glob)
1040 --- cloned.*/.hgsubstate * (glob)
1077 --- cloned.*/.hgsubstate * (glob)
1041 +++ cloned.*/.hgsubstate * (glob)
1078 +++ cloned.*/.hgsubstate * (glob)
1042 @@ -1* +1,2 @@ (glob)
1079 @@ -1* +1,2 @@ (glob)
1043 -fc3b4ce2696f7741438c79207583768f2ce6b0dd sub1
1080 -fc3b4ce2696f7741438c79207583768f2ce6b0dd sub1
1044 +7a36fa02b66e61f27f3d4a822809f159479b8ab2 sub1
1081 +7a36fa02b66e61f27f3d4a822809f159479b8ab2 sub1
1045 +b1a26de6f2a045a9f079323693614ee322f1ff7e sub3
1082 +b1a26de6f2a045a9f079323693614ee322f1ff7e sub3
1046 diff -Nru cloned.*/foo/bar/def cloned.*/foo/bar/def (glob)
1083 diff -Nru cloned.*/foo/bar/def cloned.*/foo/bar/def (glob)
1047 --- cloned.*/foo/bar/def * (glob)
1084 --- cloned.*/foo/bar/def * (glob)
1048 +++ cloned.*/foo/bar/def * (glob)
1085 +++ cloned.*/foo/bar/def * (glob)
1049 @@ -*,0 +1* @@ (glob)
1086 @@ -*,0 +1* @@ (glob)
1050 +changed
1087 +changed
1051 diff -Nru cloned.*/main cloned.*/main (glob)
1088 diff -Nru cloned.*/main cloned.*/main (glob)
1052 --- cloned.*/main * (glob)
1089 --- cloned.*/main * (glob)
1053 +++ cloned.*/main * (glob)
1090 +++ cloned.*/main * (glob)
1054 @@ -1* +1* @@ (glob)
1091 @@ -1* +1* @@ (glob)
1055 -main
1092 -main
1056 +foo
1093 +foo
1057 diff -Nru cloned.*/sub1/.hgsubstate cloned.*/sub1/.hgsubstate (glob)
1094 diff -Nru cloned.*/sub1/.hgsubstate cloned.*/sub1/.hgsubstate (glob)
1058 --- cloned.*/sub1/.hgsubstate * (glob)
1095 --- cloned.*/sub1/.hgsubstate * (glob)
1059 +++ cloned.*/sub1/.hgsubstate * (glob)
1096 +++ cloned.*/sub1/.hgsubstate * (glob)
1060 @@ -1* +1* @@ (glob)
1097 @@ -1* +1* @@ (glob)
1061 -c57a0840e3badd667ef3c3ef65471609acb2ba3c sub2
1098 -c57a0840e3badd667ef3c3ef65471609acb2ba3c sub2
1062 +c77908c81ccea3794a896c79e98b0e004aee2e9e sub2
1099 +c77908c81ccea3794a896c79e98b0e004aee2e9e sub2
1063 diff -Nru cloned.*/sub1/sub2/folder/test.txt cloned.*/sub1/sub2/folder/test.txt (glob)
1100 diff -Nru cloned.*/sub1/sub2/folder/test.txt cloned.*/sub1/sub2/folder/test.txt (glob)
1064 --- cloned.*/sub1/sub2/folder/test.txt * (glob)
1101 --- cloned.*/sub1/sub2/folder/test.txt * (glob)
1065 +++ cloned.*/sub1/sub2/folder/test.txt * (glob)
1102 +++ cloned.*/sub1/sub2/folder/test.txt * (glob)
1066 @@ -*,0 +1* @@ (glob)
1103 @@ -*,0 +1* @@ (glob)
1067 +subfolder
1104 +subfolder
1068 diff -Nru cloned.*/sub1/sub2/sub2 cloned.*/sub1/sub2/sub2 (glob)
1105 diff -Nru cloned.*/sub1/sub2/sub2 cloned.*/sub1/sub2/sub2 (glob)
1069 --- cloned.*/sub1/sub2/sub2 * (glob)
1106 --- cloned.*/sub1/sub2/sub2 * (glob)
1070 +++ cloned.*/sub1/sub2/sub2 * (glob)
1107 +++ cloned.*/sub1/sub2/sub2 * (glob)
1071 @@ -1* +1* @@ (glob)
1108 @@ -1* +1* @@ (glob)
1072 -sub2
1109 -sub2
1073 +modified
1110 +modified
1074 diff -Nru cloned.*/sub3/a.txt cloned.*/sub3/a.txt (glob)
1111 diff -Nru cloned.*/sub3/a.txt cloned.*/sub3/a.txt (glob)
1075 --- cloned.*/sub3/a.txt * (glob)
1112 --- cloned.*/sub3/a.txt * (glob)
1076 +++ cloned.*/sub3/a.txt * (glob)
1113 +++ cloned.*/sub3/a.txt * (glob)
1077 @@ -*,0 +1* @@ (glob)
1114 @@ -*,0 +1* @@ (glob)
1078 +xyz
1115 +xyz
1079 [1]
1116 [1]
1080
1117
1081 $ echo mod > sub1/sub2/sub2
1118 $ echo mod > sub1/sub2/sub2
1082 $ hg --config extensions.extdiff= pdiff -S
1119 $ hg --config extensions.extdiff= pdiff -S
1083 \r (no-eol) (esc)
1120 \r (no-eol) (esc)
1084 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
1121 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
1085 \r (no-eol) (esc)
1122 \r (no-eol) (esc)
1086 \r (no-eol) (esc)
1123 \r (no-eol) (esc)
1087 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (glob) (esc)
1124 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (glob) (esc)
1088 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (glob) (esc)
1125 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (glob) (esc)
1089 \r (no-eol) (esc)
1126 \r (no-eol) (esc)
1090 --- */cloned.*/sub1/sub2/sub2 * (glob)
1127 --- */cloned.*/sub1/sub2/sub2 * (glob)
1091 +++ */cloned/sub1/sub2/sub2 * (glob)
1128 +++ */cloned/sub1/sub2/sub2 * (glob)
1092 @@ -1* +1* @@ (glob)
1129 @@ -1* +1* @@ (glob)
1093 -modified
1130 -modified
1094 +mod
1131 +mod
1095 [1]
1132 [1]
1096
1133
1097 $ cd ..
1134 $ cd ..
@@ -1,565 +1,619 b''
1 Create test repository:
1 Create test repository:
2
2
3 $ hg init repo
3 $ hg init repo
4 $ cd repo
4 $ cd repo
5 $ echo x1 > x.txt
5 $ echo x1 > x.txt
6
6
7 $ hg init foo
7 $ hg init foo
8 $ cd foo
8 $ cd foo
9 $ echo y1 > y.txt
9 $ echo y1 > y.txt
10
10
11 $ hg init bar
11 $ hg init bar
12 $ cd bar
12 $ cd bar
13 $ echo z1 > z.txt
13 $ echo z1 > z.txt
14
14
15 $ cd ..
15 $ cd ..
16 $ echo 'bar = bar' > .hgsub
16 $ echo 'bar = bar' > .hgsub
17
17
18 $ cd ..
18 $ cd ..
19 $ echo 'foo = foo' > .hgsub
19 $ echo 'foo = foo' > .hgsub
20
20
21 Add files --- .hgsub files must go first to trigger subrepos:
21 Add files --- .hgsub files must go first to trigger subrepos:
22
22
23 $ hg add -S .hgsub
23 $ hg add -S .hgsub
24 $ hg add -S foo/.hgsub
24 $ hg add -S foo/.hgsub
25 $ hg add -S foo/bar
25 $ hg add -S foo/bar
26 adding foo/bar/z.txt (glob)
26 adding foo/bar/z.txt (glob)
27 $ hg add -S
27 $ hg add -S
28 adding x.txt
28 adding x.txt
29 adding foo/y.txt (glob)
29 adding foo/y.txt (glob)
30
30
31 Test recursive status without committing anything:
31 Test recursive status without committing anything:
32
32
33 $ hg status -S
33 $ hg status -S
34 A .hgsub
34 A .hgsub
35 A foo/.hgsub
35 A foo/.hgsub
36 A foo/bar/z.txt
36 A foo/bar/z.txt
37 A foo/y.txt
37 A foo/y.txt
38 A x.txt
38 A x.txt
39
39
40 Test recursive diff without committing anything:
40 Test recursive diff without committing anything:
41
41
42 $ hg diff --nodates -S foo
42 $ hg diff --nodates -S foo
43 diff -r 000000000000 foo/.hgsub
43 diff -r 000000000000 foo/.hgsub
44 --- /dev/null
44 --- /dev/null
45 +++ b/foo/.hgsub
45 +++ b/foo/.hgsub
46 @@ -0,0 +1,1 @@
46 @@ -0,0 +1,1 @@
47 +bar = bar
47 +bar = bar
48 diff -r 000000000000 foo/y.txt
48 diff -r 000000000000 foo/y.txt
49 --- /dev/null
49 --- /dev/null
50 +++ b/foo/y.txt
50 +++ b/foo/y.txt
51 @@ -0,0 +1,1 @@
51 @@ -0,0 +1,1 @@
52 +y1
52 +y1
53 diff -r 000000000000 foo/bar/z.txt
53 diff -r 000000000000 foo/bar/z.txt
54 --- /dev/null
54 --- /dev/null
55 +++ b/foo/bar/z.txt
55 +++ b/foo/bar/z.txt
56 @@ -0,0 +1,1 @@
56 @@ -0,0 +1,1 @@
57 +z1
57 +z1
58
58
59 Commits:
59 Commits:
60
60
61 $ hg commit -m fails
61 $ hg commit -m fails
62 abort: uncommitted changes in subrepository 'foo'
62 abort: uncommitted changes in subrepository 'foo'
63 (use --subrepos for recursive commit)
63 (use --subrepos for recursive commit)
64 [255]
64 [255]
65
65
66 The --subrepos flag overwrite the config setting:
66 The --subrepos flag overwrite the config setting:
67
67
68 $ hg commit -m 0-0-0 --config ui.commitsubrepos=No --subrepos
68 $ hg commit -m 0-0-0 --config ui.commitsubrepos=No --subrepos
69 committing subrepository foo
69 committing subrepository foo
70 committing subrepository foo/bar (glob)
70 committing subrepository foo/bar (glob)
71
71
72 $ cd foo
72 $ cd foo
73 $ echo y2 >> y.txt
73 $ echo y2 >> y.txt
74 $ hg commit -m 0-1-0
74 $ hg commit -m 0-1-0
75
75
76 $ cd bar
76 $ cd bar
77 $ echo z2 >> z.txt
77 $ echo z2 >> z.txt
78 $ hg commit -m 0-1-1
78 $ hg commit -m 0-1-1
79
79
80 $ cd ..
80 $ cd ..
81 $ hg commit -m 0-2-1
81 $ hg commit -m 0-2-1
82
82
83 $ cd ..
83 $ cd ..
84 $ hg commit -m 1-2-1
84 $ hg commit -m 1-2-1
85
85
86 Change working directory:
86 Change working directory:
87
87
88 $ echo y3 >> foo/y.txt
88 $ echo y3 >> foo/y.txt
89 $ echo z3 >> foo/bar/z.txt
89 $ echo z3 >> foo/bar/z.txt
90 $ hg status -S
90 $ hg status -S
91 M foo/bar/z.txt
91 M foo/bar/z.txt
92 M foo/y.txt
92 M foo/y.txt
93 $ hg diff --nodates -S
93 $ hg diff --nodates -S
94 diff -r d254738c5f5e foo/y.txt
94 diff -r d254738c5f5e foo/y.txt
95 --- a/foo/y.txt
95 --- a/foo/y.txt
96 +++ b/foo/y.txt
96 +++ b/foo/y.txt
97 @@ -1,2 +1,3 @@
97 @@ -1,2 +1,3 @@
98 y1
98 y1
99 y2
99 y2
100 +y3
100 +y3
101 diff -r 9647f22de499 foo/bar/z.txt
101 diff -r 9647f22de499 foo/bar/z.txt
102 --- a/foo/bar/z.txt
102 --- a/foo/bar/z.txt
103 +++ b/foo/bar/z.txt
103 +++ b/foo/bar/z.txt
104 @@ -1,2 +1,3 @@
104 @@ -1,2 +1,3 @@
105 z1
105 z1
106 z2
106 z2
107 +z3
107 +z3
108
108
109 Status call crossing repository boundaries:
109 Status call crossing repository boundaries:
110
110
111 $ hg status -S foo/bar/z.txt
111 $ hg status -S foo/bar/z.txt
112 M foo/bar/z.txt
112 M foo/bar/z.txt
113 $ hg status -S -I 'foo/?.txt'
113 $ hg status -S -I 'foo/?.txt'
114 M foo/y.txt
114 M foo/y.txt
115 $ hg status -S -I '**/?.txt'
115 $ hg status -S -I '**/?.txt'
116 M foo/bar/z.txt
116 M foo/bar/z.txt
117 M foo/y.txt
117 M foo/y.txt
118 $ hg diff --nodates -S -I '**/?.txt'
118 $ hg diff --nodates -S -I '**/?.txt'
119 diff -r d254738c5f5e foo/y.txt
119 diff -r d254738c5f5e foo/y.txt
120 --- a/foo/y.txt
120 --- a/foo/y.txt
121 +++ b/foo/y.txt
121 +++ b/foo/y.txt
122 @@ -1,2 +1,3 @@
122 @@ -1,2 +1,3 @@
123 y1
123 y1
124 y2
124 y2
125 +y3
125 +y3
126 diff -r 9647f22de499 foo/bar/z.txt
126 diff -r 9647f22de499 foo/bar/z.txt
127 --- a/foo/bar/z.txt
127 --- a/foo/bar/z.txt
128 +++ b/foo/bar/z.txt
128 +++ b/foo/bar/z.txt
129 @@ -1,2 +1,3 @@
129 @@ -1,2 +1,3 @@
130 z1
130 z1
131 z2
131 z2
132 +z3
132 +z3
133
133
134 Status from within a subdirectory:
134 Status from within a subdirectory:
135
135
136 $ mkdir dir
136 $ mkdir dir
137 $ cd dir
137 $ cd dir
138 $ echo a1 > a.txt
138 $ echo a1 > a.txt
139 $ hg status -S
139 $ hg status -S
140 M foo/bar/z.txt
140 M foo/bar/z.txt
141 M foo/y.txt
141 M foo/y.txt
142 ? dir/a.txt
142 ? dir/a.txt
143 $ hg diff --nodates -S
143 $ hg diff --nodates -S
144 diff -r d254738c5f5e foo/y.txt
144 diff -r d254738c5f5e foo/y.txt
145 --- a/foo/y.txt
145 --- a/foo/y.txt
146 +++ b/foo/y.txt
146 +++ b/foo/y.txt
147 @@ -1,2 +1,3 @@
147 @@ -1,2 +1,3 @@
148 y1
148 y1
149 y2
149 y2
150 +y3
150 +y3
151 diff -r 9647f22de499 foo/bar/z.txt
151 diff -r 9647f22de499 foo/bar/z.txt
152 --- a/foo/bar/z.txt
152 --- a/foo/bar/z.txt
153 +++ b/foo/bar/z.txt
153 +++ b/foo/bar/z.txt
154 @@ -1,2 +1,3 @@
154 @@ -1,2 +1,3 @@
155 z1
155 z1
156 z2
156 z2
157 +z3
157 +z3
158
158
159 Status with relative path:
159 Status with relative path:
160
160
161 $ hg status -S ..
161 $ hg status -S ..
162 M ../foo/bar/z.txt
162 M ../foo/bar/z.txt
163 M ../foo/y.txt
163 M ../foo/y.txt
164 ? a.txt
164 ? a.txt
165
165
166 XXX: filtering lfilesrepo.status() in 3.3-rc causes these files to be listed as
166 XXX: filtering lfilesrepo.status() in 3.3-rc causes these files to be listed as
167 added instead of modified.
167 added instead of modified.
168 $ hg status -S .. --config extensions.largefiles=
168 $ hg status -S .. --config extensions.largefiles=
169 M ../foo/bar/z.txt
169 M ../foo/bar/z.txt
170 M ../foo/y.txt
170 M ../foo/y.txt
171 ? a.txt
171 ? a.txt
172
172
173 $ hg diff --nodates -S ..
173 $ hg diff --nodates -S ..
174 diff -r d254738c5f5e foo/y.txt
174 diff -r d254738c5f5e foo/y.txt
175 --- a/foo/y.txt
175 --- a/foo/y.txt
176 +++ b/foo/y.txt
176 +++ b/foo/y.txt
177 @@ -1,2 +1,3 @@
177 @@ -1,2 +1,3 @@
178 y1
178 y1
179 y2
179 y2
180 +y3
180 +y3
181 diff -r 9647f22de499 foo/bar/z.txt
181 diff -r 9647f22de499 foo/bar/z.txt
182 --- a/foo/bar/z.txt
182 --- a/foo/bar/z.txt
183 +++ b/foo/bar/z.txt
183 +++ b/foo/bar/z.txt
184 @@ -1,2 +1,3 @@
184 @@ -1,2 +1,3 @@
185 z1
185 z1
186 z2
186 z2
187 +z3
187 +z3
188 $ cd ..
188 $ cd ..
189
189
190 Cleanup and final commit:
190 Cleanup and final commit:
191
191
192 $ rm -r dir
192 $ rm -r dir
193 $ hg commit --subrepos -m 2-3-2
193 $ hg commit --subrepos -m 2-3-2
194 committing subrepository foo
194 committing subrepository foo
195 committing subrepository foo/bar (glob)
195 committing subrepository foo/bar (glob)
196
196
197 Test explicit path commands within subrepos: add/forget
197 Test explicit path commands within subrepos: add/forget
198 $ echo z1 > foo/bar/z2.txt
198 $ echo z1 > foo/bar/z2.txt
199 $ hg status -S
199 $ hg status -S
200 ? foo/bar/z2.txt
200 ? foo/bar/z2.txt
201 $ hg add foo/bar/z2.txt
201 $ hg add foo/bar/z2.txt
202 $ hg status -S
202 $ hg status -S
203 A foo/bar/z2.txt
203 A foo/bar/z2.txt
204 $ hg forget foo/bar/z2.txt
204 $ hg forget foo/bar/z2.txt
205 $ hg status -S
205 $ hg status -S
206 ? foo/bar/z2.txt
206 ? foo/bar/z2.txt
207 $ hg forget foo/bar/z2.txt
207 $ hg forget foo/bar/z2.txt
208 not removing foo/bar/z2.txt: file is already untracked (glob)
208 not removing foo/bar/z2.txt: file is already untracked (glob)
209 [1]
209 [1]
210 $ hg status -S
210 $ hg status -S
211 ? foo/bar/z2.txt
211 ? foo/bar/z2.txt
212 $ rm foo/bar/z2.txt
212 $ rm foo/bar/z2.txt
213
213
214 Log with the relationships between repo and its subrepo:
214 Log with the relationships between repo and its subrepo:
215
215
216 $ hg log --template '{rev}:{node|short} {desc}\n'
216 $ hg log --template '{rev}:{node|short} {desc}\n'
217 2:1326fa26d0c0 2-3-2
217 2:1326fa26d0c0 2-3-2
218 1:4b3c9ff4f66b 1-2-1
218 1:4b3c9ff4f66b 1-2-1
219 0:23376cbba0d8 0-0-0
219 0:23376cbba0d8 0-0-0
220
220
221 $ hg -R foo log --template '{rev}:{node|short} {desc}\n'
221 $ hg -R foo log --template '{rev}:{node|short} {desc}\n'
222 3:65903cebad86 2-3-2
222 3:65903cebad86 2-3-2
223 2:d254738c5f5e 0-2-1
223 2:d254738c5f5e 0-2-1
224 1:8629ce7dcc39 0-1-0
224 1:8629ce7dcc39 0-1-0
225 0:af048e97ade2 0-0-0
225 0:af048e97ade2 0-0-0
226
226
227 $ hg -R foo/bar log --template '{rev}:{node|short} {desc}\n'
227 $ hg -R foo/bar log --template '{rev}:{node|short} {desc}\n'
228 2:31ecbdafd357 2-3-2
228 2:31ecbdafd357 2-3-2
229 1:9647f22de499 0-1-1
229 1:9647f22de499 0-1-1
230 0:4904098473f9 0-0-0
230 0:4904098473f9 0-0-0
231
231
232 Status between revisions:
232 Status between revisions:
233
233
234 $ hg status -S
234 $ hg status -S
235 $ hg status -S --rev 0:1
235 $ hg status -S --rev 0:1
236 M .hgsubstate
236 M .hgsubstate
237 M foo/.hgsubstate
237 M foo/.hgsubstate
238 M foo/bar/z.txt
238 M foo/bar/z.txt
239 M foo/y.txt
239 M foo/y.txt
240 $ hg diff --nodates -S -I '**/?.txt' --rev 0:1
240 $ hg diff --nodates -S -I '**/?.txt' --rev 0:1
241 diff -r af048e97ade2 -r d254738c5f5e foo/y.txt
241 diff -r af048e97ade2 -r d254738c5f5e foo/y.txt
242 --- a/foo/y.txt
242 --- a/foo/y.txt
243 +++ b/foo/y.txt
243 +++ b/foo/y.txt
244 @@ -1,1 +1,2 @@
244 @@ -1,1 +1,2 @@
245 y1
245 y1
246 +y2
246 +y2
247 diff -r 4904098473f9 -r 9647f22de499 foo/bar/z.txt
247 diff -r 4904098473f9 -r 9647f22de499 foo/bar/z.txt
248 --- a/foo/bar/z.txt
248 --- a/foo/bar/z.txt
249 +++ b/foo/bar/z.txt
249 +++ b/foo/bar/z.txt
250 @@ -1,1 +1,2 @@
250 @@ -1,1 +1,2 @@
251 z1
251 z1
252 +z2
252 +z2
253
253
254 #if serve
255 $ cd ..
256 $ hg serve -R repo --debug -S -p $HGPORT -d --pid-file=hg1.pid -E error.log -A access.log
257 adding = $TESTTMP/repo (glob)
258 adding foo = $TESTTMP/repo/foo (glob)
259 adding foo/bar = $TESTTMP/repo/foo/bar (glob)
260 listening at http://*:$HGPORT/ (bound to *:$HGPORT) (glob) (?)
261 adding = $TESTTMP/repo (glob) (?)
262 adding foo = $TESTTMP/repo/foo (glob) (?)
263 adding foo/bar = $TESTTMP/repo/foo/bar (glob) (?)
264 $ cat hg1.pid >> $DAEMON_PIDS
265
266 $ hg clone http://localhost:$HGPORT clone --config progress.disable=True
267 requesting all changes
268 adding changesets
269 adding manifests
270 adding file changes
271 added 3 changesets with 5 changes to 3 files
272 updating to branch default
273 cloning subrepo foo from http://localhost:$HGPORT/foo
274 requesting all changes
275 adding changesets
276 adding manifests
277 adding file changes
278 added 4 changesets with 7 changes to 3 files
279 cloning subrepo foo/bar from http://localhost:$HGPORT/foo/bar (glob)
280 requesting all changes
281 adding changesets
282 adding manifests
283 adding file changes
284 added 3 changesets with 3 changes to 1 files
285 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
286
287 $ cat clone/foo/bar/z.txt
288 z1
289 z2
290 z3
291
292 $ cat access.log
293 * "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
294 * "GET /?cmd=batch HTTP/1.1" 200 - * (glob)
295 * "GET /?cmd=getbundle HTTP/1.1" 200 - * (glob)
296 * "GET /foo?cmd=capabilities HTTP/1.1" 200 - (glob)
297 * "GET /foo?cmd=batch HTTP/1.1" 200 - * (glob)
298 * "GET /foo?cmd=getbundle HTTP/1.1" 200 - * (glob)
299 * "GET /foo/bar?cmd=capabilities HTTP/1.1" 200 - (glob)
300 * "GET /foo/bar?cmd=batch HTTP/1.1" 200 - * (glob)
301 * "GET /foo/bar?cmd=getbundle HTTP/1.1" 200 - * (glob)
302
303 $ killdaemons.py
304 $ rm hg1.pid error.log access.log
305 $ cd repo
306 #endif
307
254 Enable progress extension for archive tests:
308 Enable progress extension for archive tests:
255
309
256 $ cp $HGRCPATH $HGRCPATH.no-progress
310 $ cp $HGRCPATH $HGRCPATH.no-progress
257 $ cat >> $HGRCPATH <<EOF
311 $ cat >> $HGRCPATH <<EOF
258 > [progress]
312 > [progress]
259 > disable=False
313 > disable=False
260 > assume-tty = 1
314 > assume-tty = 1
261 > delay = 0
315 > delay = 0
262 > # set changedelay really large so we don't see nested topics
316 > # set changedelay really large so we don't see nested topics
263 > changedelay = 30000
317 > changedelay = 30000
264 > format = topic bar number
318 > format = topic bar number
265 > refresh = 0
319 > refresh = 0
266 > width = 60
320 > width = 60
267 > EOF
321 > EOF
268
322
269 Test archiving to a directory tree (the doubled lines in the output
323 Test archiving to a directory tree (the doubled lines in the output
270 only show up in the test output, not in real usage):
324 only show up in the test output, not in real usage):
271
325
272 $ hg archive --subrepos ../archive
326 $ hg archive --subrepos ../archive
273 \r (no-eol) (esc)
327 \r (no-eol) (esc)
274 archiving [ ] 0/3\r (no-eol) (esc)
328 archiving [ ] 0/3\r (no-eol) (esc)
275 archiving [=============> ] 1/3\r (no-eol) (esc)
329 archiving [=============> ] 1/3\r (no-eol) (esc)
276 archiving [===========================> ] 2/3\r (no-eol) (esc)
330 archiving [===========================> ] 2/3\r (no-eol) (esc)
277 archiving [==========================================>] 3/3\r (no-eol) (esc)
331 archiving [==========================================>] 3/3\r (no-eol) (esc)
278 \r (no-eol) (esc)
332 \r (no-eol) (esc)
279 \r (no-eol) (esc)
333 \r (no-eol) (esc)
280 archiving (foo) [ ] 0/3\r (no-eol) (esc)
334 archiving (foo) [ ] 0/3\r (no-eol) (esc)
281 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
335 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
282 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
336 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
283 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
337 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
284 \r (no-eol) (esc)
338 \r (no-eol) (esc)
285 \r (no-eol) (esc)
339 \r (no-eol) (esc)
286 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
340 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
287 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
341 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
288 \r (no-eol) (esc)
342 \r (no-eol) (esc)
289 $ find ../archive | sort
343 $ find ../archive | sort
290 ../archive
344 ../archive
291 ../archive/.hg_archival.txt
345 ../archive/.hg_archival.txt
292 ../archive/.hgsub
346 ../archive/.hgsub
293 ../archive/.hgsubstate
347 ../archive/.hgsubstate
294 ../archive/foo
348 ../archive/foo
295 ../archive/foo/.hgsub
349 ../archive/foo/.hgsub
296 ../archive/foo/.hgsubstate
350 ../archive/foo/.hgsubstate
297 ../archive/foo/bar
351 ../archive/foo/bar
298 ../archive/foo/bar/z.txt
352 ../archive/foo/bar/z.txt
299 ../archive/foo/y.txt
353 ../archive/foo/y.txt
300 ../archive/x.txt
354 ../archive/x.txt
301
355
302 Test archiving to zip file (unzip output is unstable):
356 Test archiving to zip file (unzip output is unstable):
303
357
304 $ hg archive --subrepos --prefix '.' ../archive.zip
358 $ hg archive --subrepos --prefix '.' ../archive.zip
305 \r (no-eol) (esc)
359 \r (no-eol) (esc)
306 archiving [ ] 0/3\r (no-eol) (esc)
360 archiving [ ] 0/3\r (no-eol) (esc)
307 archiving [=============> ] 1/3\r (no-eol) (esc)
361 archiving [=============> ] 1/3\r (no-eol) (esc)
308 archiving [===========================> ] 2/3\r (no-eol) (esc)
362 archiving [===========================> ] 2/3\r (no-eol) (esc)
309 archiving [==========================================>] 3/3\r (no-eol) (esc)
363 archiving [==========================================>] 3/3\r (no-eol) (esc)
310 \r (no-eol) (esc)
364 \r (no-eol) (esc)
311 \r (no-eol) (esc)
365 \r (no-eol) (esc)
312 archiving (foo) [ ] 0/3\r (no-eol) (esc)
366 archiving (foo) [ ] 0/3\r (no-eol) (esc)
313 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
367 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
314 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
368 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
315 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
369 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
316 \r (no-eol) (esc)
370 \r (no-eol) (esc)
317 \r (no-eol) (esc)
371 \r (no-eol) (esc)
318 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
372 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
319 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
373 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
320 \r (no-eol) (esc)
374 \r (no-eol) (esc)
321
375
322 (unzip date formating is unstable, we do not care about it and glob it out)
376 (unzip date formating is unstable, we do not care about it and glob it out)
323
377
324 $ unzip -l ../archive.zip | grep -v -- ----- | egrep -v files$
378 $ unzip -l ../archive.zip | grep -v -- ----- | egrep -v files$
325 Archive: ../archive.zip
379 Archive: ../archive.zip
326 Length [ ]* Date [ ]* Time [ ]* Name (re)
380 Length [ ]* Date [ ]* Time [ ]* Name (re)
327 172 [0-9:\- ]* .hg_archival.txt (re)
381 172 [0-9:\- ]* .hg_archival.txt (re)
328 10 [0-9:\- ]* .hgsub (re)
382 10 [0-9:\- ]* .hgsub (re)
329 45 [0-9:\- ]* .hgsubstate (re)
383 45 [0-9:\- ]* .hgsubstate (re)
330 3 [0-9:\- ]* x.txt (re)
384 3 [0-9:\- ]* x.txt (re)
331 10 [0-9:\- ]* foo/.hgsub (re)
385 10 [0-9:\- ]* foo/.hgsub (re)
332 45 [0-9:\- ]* foo/.hgsubstate (re)
386 45 [0-9:\- ]* foo/.hgsubstate (re)
333 9 [0-9:\- ]* foo/y.txt (re)
387 9 [0-9:\- ]* foo/y.txt (re)
334 9 [0-9:\- ]* foo/bar/z.txt (re)
388 9 [0-9:\- ]* foo/bar/z.txt (re)
335
389
336 Test archiving a revision that references a subrepo that is not yet
390 Test archiving a revision that references a subrepo that is not yet
337 cloned:
391 cloned:
338
392
339 #if hardlink
393 #if hardlink
340 $ hg clone -U . ../empty
394 $ hg clone -U . ../empty
341 \r (no-eol) (esc)
395 \r (no-eol) (esc)
342 linking [ <=> ] 1\r (no-eol) (esc)
396 linking [ <=> ] 1\r (no-eol) (esc)
343 linking [ <=> ] 2\r (no-eol) (esc)
397 linking [ <=> ] 2\r (no-eol) (esc)
344 linking [ <=> ] 3\r (no-eol) (esc)
398 linking [ <=> ] 3\r (no-eol) (esc)
345 linking [ <=> ] 4\r (no-eol) (esc)
399 linking [ <=> ] 4\r (no-eol) (esc)
346 linking [ <=> ] 5\r (no-eol) (esc)
400 linking [ <=> ] 5\r (no-eol) (esc)
347 linking [ <=> ] 6\r (no-eol) (esc)
401 linking [ <=> ] 6\r (no-eol) (esc)
348 linking [ <=> ] 7\r (no-eol) (esc)
402 linking [ <=> ] 7\r (no-eol) (esc)
349 linking [ <=> ] 8\r (no-eol) (esc)
403 linking [ <=> ] 8\r (no-eol) (esc)
350 \r (no-eol) (esc)
404 \r (no-eol) (esc)
351 #else
405 #else
352 $ hg clone -U . ../empty
406 $ hg clone -U . ../empty
353 \r (no-eol) (esc)
407 \r (no-eol) (esc)
354 linking [ <=> ] 1 (no-eol)
408 linking [ <=> ] 1 (no-eol)
355 #endif
409 #endif
356
410
357 $ cd ../empty
411 $ cd ../empty
358 #if hardlink
412 #if hardlink
359 $ hg archive --subrepos -r tip --prefix './' ../archive.tar.gz
413 $ hg archive --subrepos -r tip --prefix './' ../archive.tar.gz
360 \r (no-eol) (esc)
414 \r (no-eol) (esc)
361 archiving [ ] 0/3\r (no-eol) (esc)
415 archiving [ ] 0/3\r (no-eol) (esc)
362 archiving [=============> ] 1/3\r (no-eol) (esc)
416 archiving [=============> ] 1/3\r (no-eol) (esc)
363 archiving [===========================> ] 2/3\r (no-eol) (esc)
417 archiving [===========================> ] 2/3\r (no-eol) (esc)
364 archiving [==========================================>] 3/3\r (no-eol) (esc)
418 archiving [==========================================>] 3/3\r (no-eol) (esc)
365 \r (no-eol) (esc)
419 \r (no-eol) (esc)
366 \r (no-eol) (esc)
420 \r (no-eol) (esc)
367 linking [ <=> ] 1\r (no-eol) (esc)
421 linking [ <=> ] 1\r (no-eol) (esc)
368 linking [ <=> ] 2\r (no-eol) (esc)
422 linking [ <=> ] 2\r (no-eol) (esc)
369 linking [ <=> ] 3\r (no-eol) (esc)
423 linking [ <=> ] 3\r (no-eol) (esc)
370 linking [ <=> ] 4\r (no-eol) (esc)
424 linking [ <=> ] 4\r (no-eol) (esc)
371 linking [ <=> ] 5\r (no-eol) (esc)
425 linking [ <=> ] 5\r (no-eol) (esc)
372 linking [ <=> ] 6\r (no-eol) (esc)
426 linking [ <=> ] 6\r (no-eol) (esc)
373 linking [ <=> ] 7\r (no-eol) (esc)
427 linking [ <=> ] 7\r (no-eol) (esc)
374 linking [ <=> ] 8\r (no-eol) (esc)
428 linking [ <=> ] 8\r (no-eol) (esc)
375 \r (no-eol) (esc)
429 \r (no-eol) (esc)
376 \r (no-eol) (esc)
430 \r (no-eol) (esc)
377 archiving (foo) [ ] 0/3\r (no-eol) (esc)
431 archiving (foo) [ ] 0/3\r (no-eol) (esc)
378 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
432 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
379 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
433 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
380 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
434 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
381 \r (no-eol) (esc)
435 \r (no-eol) (esc)
382 \r (no-eol) (esc)
436 \r (no-eol) (esc)
383 linking [ <=> ] 1\r (no-eol) (esc)
437 linking [ <=> ] 1\r (no-eol) (esc)
384 linking [ <=> ] 2\r (no-eol) (esc)
438 linking [ <=> ] 2\r (no-eol) (esc)
385 linking [ <=> ] 3\r (no-eol) (esc)
439 linking [ <=> ] 3\r (no-eol) (esc)
386 linking [ <=> ] 4\r (no-eol) (esc)
440 linking [ <=> ] 4\r (no-eol) (esc)
387 linking [ <=> ] 5\r (no-eol) (esc)
441 linking [ <=> ] 5\r (no-eol) (esc)
388 linking [ <=> ] 6\r (no-eol) (esc)
442 linking [ <=> ] 6\r (no-eol) (esc)
389 \r (no-eol) (esc)
443 \r (no-eol) (esc)
390 \r (no-eol) (esc)
444 \r (no-eol) (esc)
391 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
445 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
392 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
446 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
393 \r (no-eol) (esc)
447 \r (no-eol) (esc)
394 cloning subrepo foo from $TESTTMP/repo/foo
448 cloning subrepo foo from $TESTTMP/repo/foo
395 cloning subrepo foo/bar from $TESTTMP/repo/foo/bar (glob)
449 cloning subrepo foo/bar from $TESTTMP/repo/foo/bar (glob)
396 #else
450 #else
397 Note there's a slight output glitch on non-hardlink systems: the last
451 Note there's a slight output glitch on non-hardlink systems: the last
398 "linking" progress topic never gets closed, leading to slight output corruption on that platform.
452 "linking" progress topic never gets closed, leading to slight output corruption on that platform.
399 $ hg archive --subrepos -r tip --prefix './' ../archive.tar.gz
453 $ hg archive --subrepos -r tip --prefix './' ../archive.tar.gz
400 \r (no-eol) (esc)
454 \r (no-eol) (esc)
401 archiving [ ] 0/3\r (no-eol) (esc)
455 archiving [ ] 0/3\r (no-eol) (esc)
402 archiving [=============> ] 1/3\r (no-eol) (esc)
456 archiving [=============> ] 1/3\r (no-eol) (esc)
403 archiving [===========================> ] 2/3\r (no-eol) (esc)
457 archiving [===========================> ] 2/3\r (no-eol) (esc)
404 archiving [==========================================>] 3/3\r (no-eol) (esc)
458 archiving [==========================================>] 3/3\r (no-eol) (esc)
405 \r (no-eol) (esc)
459 \r (no-eol) (esc)
406 \r (no-eol) (esc)
460 \r (no-eol) (esc)
407 linking [ <=> ] 1\r (no-eol) (esc)
461 linking [ <=> ] 1\r (no-eol) (esc)
408 cloning subrepo foo/bar from $TESTTMP/repo/foo/bar (glob)
462 cloning subrepo foo/bar from $TESTTMP/repo/foo/bar (glob)
409 #endif
463 #endif
410
464
411 Archive + subrepos uses '/' for all component separators
465 Archive + subrepos uses '/' for all component separators
412
466
413 $ tar -tzf ../archive.tar.gz | sort
467 $ tar -tzf ../archive.tar.gz | sort
414 .hg_archival.txt
468 .hg_archival.txt
415 .hgsub
469 .hgsub
416 .hgsubstate
470 .hgsubstate
417 foo/.hgsub
471 foo/.hgsub
418 foo/.hgsubstate
472 foo/.hgsubstate
419 foo/bar/z.txt
473 foo/bar/z.txt
420 foo/y.txt
474 foo/y.txt
421 x.txt
475 x.txt
422
476
423 The newly cloned subrepos contain no working copy:
477 The newly cloned subrepos contain no working copy:
424
478
425 $ hg -R foo summary
479 $ hg -R foo summary
426 parent: -1:000000000000 (no revision checked out)
480 parent: -1:000000000000 (no revision checked out)
427 branch: default
481 branch: default
428 commit: (clean)
482 commit: (clean)
429 update: 4 new changesets (update)
483 update: 4 new changesets (update)
430
484
431 Disable progress extension and cleanup:
485 Disable progress extension and cleanup:
432
486
433 $ mv $HGRCPATH.no-progress $HGRCPATH
487 $ mv $HGRCPATH.no-progress $HGRCPATH
434
488
435 Test archiving when there is a directory in the way for a subrepo
489 Test archiving when there is a directory in the way for a subrepo
436 created by archive:
490 created by archive:
437
491
438 $ hg clone -U . ../almost-empty
492 $ hg clone -U . ../almost-empty
439 $ cd ../almost-empty
493 $ cd ../almost-empty
440 $ mkdir foo
494 $ mkdir foo
441 $ echo f > foo/f
495 $ echo f > foo/f
442 $ hg archive --subrepos -r tip archive
496 $ hg archive --subrepos -r tip archive
443 cloning subrepo foo from $TESTTMP/empty/foo
497 cloning subrepo foo from $TESTTMP/empty/foo
444 abort: destination '$TESTTMP/almost-empty/foo' is not empty (in subrepo foo) (glob)
498 abort: destination '$TESTTMP/almost-empty/foo' is not empty (in subrepo foo) (glob)
445 [255]
499 [255]
446
500
447 Clone and test outgoing:
501 Clone and test outgoing:
448
502
449 $ cd ..
503 $ cd ..
450 $ hg clone repo repo2
504 $ hg clone repo repo2
451 updating to branch default
505 updating to branch default
452 cloning subrepo foo from $TESTTMP/repo/foo
506 cloning subrepo foo from $TESTTMP/repo/foo
453 cloning subrepo foo/bar from $TESTTMP/repo/foo/bar (glob)
507 cloning subrepo foo/bar from $TESTTMP/repo/foo/bar (glob)
454 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
508 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
455 $ cd repo2
509 $ cd repo2
456 $ hg outgoing -S
510 $ hg outgoing -S
457 comparing with $TESTTMP/repo (glob)
511 comparing with $TESTTMP/repo (glob)
458 searching for changes
512 searching for changes
459 no changes found
513 no changes found
460 comparing with $TESTTMP/repo/foo
514 comparing with $TESTTMP/repo/foo
461 searching for changes
515 searching for changes
462 no changes found
516 no changes found
463 comparing with $TESTTMP/repo/foo/bar
517 comparing with $TESTTMP/repo/foo/bar
464 searching for changes
518 searching for changes
465 no changes found
519 no changes found
466 [1]
520 [1]
467
521
468 Make nested change:
522 Make nested change:
469
523
470 $ echo y4 >> foo/y.txt
524 $ echo y4 >> foo/y.txt
471 $ hg diff --nodates -S
525 $ hg diff --nodates -S
472 diff -r 65903cebad86 foo/y.txt
526 diff -r 65903cebad86 foo/y.txt
473 --- a/foo/y.txt
527 --- a/foo/y.txt
474 +++ b/foo/y.txt
528 +++ b/foo/y.txt
475 @@ -1,3 +1,4 @@
529 @@ -1,3 +1,4 @@
476 y1
530 y1
477 y2
531 y2
478 y3
532 y3
479 +y4
533 +y4
480 $ hg commit --subrepos -m 3-4-2
534 $ hg commit --subrepos -m 3-4-2
481 committing subrepository foo
535 committing subrepository foo
482 $ hg outgoing -S
536 $ hg outgoing -S
483 comparing with $TESTTMP/repo (glob)
537 comparing with $TESTTMP/repo (glob)
484 searching for changes
538 searching for changes
485 changeset: 3:2655b8ecc4ee
539 changeset: 3:2655b8ecc4ee
486 tag: tip
540 tag: tip
487 user: test
541 user: test
488 date: Thu Jan 01 00:00:00 1970 +0000
542 date: Thu Jan 01 00:00:00 1970 +0000
489 summary: 3-4-2
543 summary: 3-4-2
490
544
491 comparing with $TESTTMP/repo/foo
545 comparing with $TESTTMP/repo/foo
492 searching for changes
546 searching for changes
493 changeset: 4:e96193d6cb36
547 changeset: 4:e96193d6cb36
494 tag: tip
548 tag: tip
495 user: test
549 user: test
496 date: Thu Jan 01 00:00:00 1970 +0000
550 date: Thu Jan 01 00:00:00 1970 +0000
497 summary: 3-4-2
551 summary: 3-4-2
498
552
499 comparing with $TESTTMP/repo/foo/bar
553 comparing with $TESTTMP/repo/foo/bar
500 searching for changes
554 searching for changes
501 no changes found
555 no changes found
502
556
503
557
504 Switch to original repo and setup default path:
558 Switch to original repo and setup default path:
505
559
506 $ cd ../repo
560 $ cd ../repo
507 $ echo '[paths]' >> .hg/hgrc
561 $ echo '[paths]' >> .hg/hgrc
508 $ echo 'default = ../repo2' >> .hg/hgrc
562 $ echo 'default = ../repo2' >> .hg/hgrc
509
563
510 Test incoming:
564 Test incoming:
511
565
512 $ hg incoming -S
566 $ hg incoming -S
513 comparing with $TESTTMP/repo2 (glob)
567 comparing with $TESTTMP/repo2 (glob)
514 searching for changes
568 searching for changes
515 changeset: 3:2655b8ecc4ee
569 changeset: 3:2655b8ecc4ee
516 tag: tip
570 tag: tip
517 user: test
571 user: test
518 date: Thu Jan 01 00:00:00 1970 +0000
572 date: Thu Jan 01 00:00:00 1970 +0000
519 summary: 3-4-2
573 summary: 3-4-2
520
574
521 comparing with $TESTTMP/repo2/foo
575 comparing with $TESTTMP/repo2/foo
522 searching for changes
576 searching for changes
523 changeset: 4:e96193d6cb36
577 changeset: 4:e96193d6cb36
524 tag: tip
578 tag: tip
525 user: test
579 user: test
526 date: Thu Jan 01 00:00:00 1970 +0000
580 date: Thu Jan 01 00:00:00 1970 +0000
527 summary: 3-4-2
581 summary: 3-4-2
528
582
529 comparing with $TESTTMP/repo2/foo/bar
583 comparing with $TESTTMP/repo2/foo/bar
530 searching for changes
584 searching for changes
531 no changes found
585 no changes found
532
586
533 $ hg incoming -S --bundle incoming.hg
587 $ hg incoming -S --bundle incoming.hg
534 abort: cannot combine --bundle and --subrepos
588 abort: cannot combine --bundle and --subrepos
535 [255]
589 [255]
536
590
537 Test missing subrepo:
591 Test missing subrepo:
538
592
539 $ rm -r foo
593 $ rm -r foo
540 $ hg status -S
594 $ hg status -S
541 warning: error "unknown revision '65903cebad86f1a84bd4f1134f62fa7dcb7a1c98'" in subrepository "foo"
595 warning: error "unknown revision '65903cebad86f1a84bd4f1134f62fa7dcb7a1c98'" in subrepository "foo"
542
596
543 Issue2619: IndexError: list index out of range on hg add with subrepos
597 Issue2619: IndexError: list index out of range on hg add with subrepos
544 The subrepo must sorts after the explicit filename.
598 The subrepo must sorts after the explicit filename.
545
599
546 $ cd ..
600 $ cd ..
547 $ hg init test
601 $ hg init test
548 $ cd test
602 $ cd test
549 $ hg init x
603 $ hg init x
550 $ echo abc > abc.txt
604 $ echo abc > abc.txt
551 $ hg ci -Am "abc"
605 $ hg ci -Am "abc"
552 adding abc.txt
606 adding abc.txt
553 $ echo "x = x" >> .hgsub
607 $ echo "x = x" >> .hgsub
554 $ hg add .hgsub
608 $ hg add .hgsub
555 $ touch a x/a
609 $ touch a x/a
556 $ hg add a x/a
610 $ hg add a x/a
557
611
558 $ hg ci -Sm "added x"
612 $ hg ci -Sm "added x"
559 committing subrepository x
613 committing subrepository x
560 $ echo abc > x/a
614 $ echo abc > x/a
561 $ hg revert --rev '.^' "set:subrepo('glob:x*')"
615 $ hg revert --rev '.^' "set:subrepo('glob:x*')"
562 abort: subrepository 'x' does not exist in 25ac2c9b3180!
616 abort: subrepository 'x' does not exist in 25ac2c9b3180!
563 [255]
617 [255]
564
618
565 $ cd ..
619 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now