##// END OF EJS Templates
remove: add progress support
timeless -
r28608:62e73d42 default
parent child Browse files
Show More
@@ -1,3500 +1,3533
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 cStringIO
10 import cStringIO
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14 import sys
14 import sys
15 import tempfile
15 import tempfile
16
16
17 from .i18n import _
17 from .i18n import _
18 from .node import (
18 from .node import (
19 bin,
19 bin,
20 hex,
20 hex,
21 nullid,
21 nullid,
22 nullrev,
22 nullrev,
23 short,
23 short,
24 )
24 )
25
25
26 from . import (
26 from . import (
27 bookmarks,
27 bookmarks,
28 changelog,
28 changelog,
29 copies,
29 copies,
30 crecord as crecordmod,
30 crecord as crecordmod,
31 encoding,
31 encoding,
32 error,
32 error,
33 formatter,
33 formatter,
34 graphmod,
34 graphmod,
35 lock as lockmod,
35 lock as lockmod,
36 match as matchmod,
36 match as matchmod,
37 obsolete,
37 obsolete,
38 patch,
38 patch,
39 pathutil,
39 pathutil,
40 phases,
40 phases,
41 repair,
41 repair,
42 revlog,
42 revlog,
43 revset,
43 revset,
44 scmutil,
44 scmutil,
45 templatekw,
45 templatekw,
46 templater,
46 templater,
47 util,
47 util,
48 )
48 )
49
49
50 def ishunk(x):
50 def ishunk(x):
51 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
51 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
52 return isinstance(x, hunkclasses)
52 return isinstance(x, hunkclasses)
53
53
54 def newandmodified(chunks, originalchunks):
54 def newandmodified(chunks, originalchunks):
55 newlyaddedandmodifiedfiles = set()
55 newlyaddedandmodifiedfiles = set()
56 for chunk in chunks:
56 for chunk in chunks:
57 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
57 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
58 originalchunks:
58 originalchunks:
59 newlyaddedandmodifiedfiles.add(chunk.header.filename())
59 newlyaddedandmodifiedfiles.add(chunk.header.filename())
60 return newlyaddedandmodifiedfiles
60 return newlyaddedandmodifiedfiles
61
61
62 def parsealiases(cmd):
62 def parsealiases(cmd):
63 return cmd.lstrip("^").split("|")
63 return cmd.lstrip("^").split("|")
64
64
65 def setupwrapcolorwrite(ui):
65 def setupwrapcolorwrite(ui):
66 # wrap ui.write so diff output can be labeled/colorized
66 # wrap ui.write so diff output can be labeled/colorized
67 def wrapwrite(orig, *args, **kw):
67 def wrapwrite(orig, *args, **kw):
68 label = kw.pop('label', '')
68 label = kw.pop('label', '')
69 for chunk, l in patch.difflabel(lambda: args):
69 for chunk, l in patch.difflabel(lambda: args):
70 orig(chunk, label=label + l)
70 orig(chunk, label=label + l)
71
71
72 oldwrite = ui.write
72 oldwrite = ui.write
73 def wrap(*args, **kwargs):
73 def wrap(*args, **kwargs):
74 return wrapwrite(oldwrite, *args, **kwargs)
74 return wrapwrite(oldwrite, *args, **kwargs)
75 setattr(ui, 'write', wrap)
75 setattr(ui, 'write', wrap)
76 return oldwrite
76 return oldwrite
77
77
78 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
78 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
79 if usecurses:
79 if usecurses:
80 if testfile:
80 if testfile:
81 recordfn = crecordmod.testdecorator(testfile,
81 recordfn = crecordmod.testdecorator(testfile,
82 crecordmod.testchunkselector)
82 crecordmod.testchunkselector)
83 else:
83 else:
84 recordfn = crecordmod.chunkselector
84 recordfn = crecordmod.chunkselector
85
85
86 return crecordmod.filterpatch(ui, originalhunks, recordfn, operation)
86 return crecordmod.filterpatch(ui, originalhunks, recordfn, operation)
87
87
88 else:
88 else:
89 return patch.filterpatch(ui, originalhunks, operation)
89 return patch.filterpatch(ui, originalhunks, operation)
90
90
91 def recordfilter(ui, originalhunks, operation=None):
91 def recordfilter(ui, originalhunks, operation=None):
92 """ Prompts the user to filter the originalhunks and return a list of
92 """ Prompts the user to filter the originalhunks and return a list of
93 selected hunks.
93 selected hunks.
94 *operation* is used for ui purposes to indicate the user
94 *operation* is used for ui purposes to indicate the user
95 what kind of filtering they are doing: reverting, committing, shelving, etc.
95 what kind of filtering they are doing: reverting, committing, shelving, etc.
96 *operation* has to be a translated string.
96 *operation* has to be a translated string.
97 """
97 """
98 usecurses = crecordmod.checkcurses(ui)
98 usecurses = crecordmod.checkcurses(ui)
99 testfile = ui.config('experimental', 'crecordtest', None)
99 testfile = ui.config('experimental', 'crecordtest', None)
100 oldwrite = setupwrapcolorwrite(ui)
100 oldwrite = setupwrapcolorwrite(ui)
101 try:
101 try:
102 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
102 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
103 testfile, operation)
103 testfile, operation)
104 finally:
104 finally:
105 ui.write = oldwrite
105 ui.write = oldwrite
106 return newchunks, newopts
106 return newchunks, newopts
107
107
108 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
108 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
109 filterfn, *pats, **opts):
109 filterfn, *pats, **opts):
110 from . import merge as mergemod
110 from . import merge as mergemod
111 if not ui.interactive():
111 if not ui.interactive():
112 if cmdsuggest:
112 if cmdsuggest:
113 msg = _('running non-interactively, use %s instead') % cmdsuggest
113 msg = _('running non-interactively, use %s instead') % cmdsuggest
114 else:
114 else:
115 msg = _('running non-interactively')
115 msg = _('running non-interactively')
116 raise error.Abort(msg)
116 raise error.Abort(msg)
117
117
118 # make sure username is set before going interactive
118 # make sure username is set before going interactive
119 if not opts.get('user'):
119 if not opts.get('user'):
120 ui.username() # raise exception, username not provided
120 ui.username() # raise exception, username not provided
121
121
122 def recordfunc(ui, repo, message, match, opts):
122 def recordfunc(ui, repo, message, match, opts):
123 """This is generic record driver.
123 """This is generic record driver.
124
124
125 Its job is to interactively filter local changes, and
125 Its job is to interactively filter local changes, and
126 accordingly prepare working directory into a state in which the
126 accordingly prepare working directory into a state in which the
127 job can be delegated to a non-interactive commit command such as
127 job can be delegated to a non-interactive commit command such as
128 'commit' or 'qrefresh'.
128 'commit' or 'qrefresh'.
129
129
130 After the actual job is done by non-interactive command, the
130 After the actual job is done by non-interactive command, the
131 working directory is restored to its original state.
131 working directory is restored to its original state.
132
132
133 In the end we'll record interesting changes, and everything else
133 In the end we'll record interesting changes, and everything else
134 will be left in place, so the user can continue working.
134 will be left in place, so the user can continue working.
135 """
135 """
136
136
137 checkunfinished(repo, commit=True)
137 checkunfinished(repo, commit=True)
138 merge = len(repo[None].parents()) > 1
138 merge = len(repo[None].parents()) > 1
139 if merge:
139 if merge:
140 raise error.Abort(_('cannot partially commit a merge '
140 raise error.Abort(_('cannot partially commit a merge '
141 '(use "hg commit" instead)'))
141 '(use "hg commit" instead)'))
142
142
143 status = repo.status(match=match)
143 status = repo.status(match=match)
144 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
144 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
145 diffopts.nodates = True
145 diffopts.nodates = True
146 diffopts.git = True
146 diffopts.git = True
147 diffopts.showfunc = True
147 diffopts.showfunc = True
148 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
148 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
149 originalchunks = patch.parsepatch(originaldiff)
149 originalchunks = patch.parsepatch(originaldiff)
150
150
151 # 1. filter patch, since we are intending to apply subset of it
151 # 1. filter patch, since we are intending to apply subset of it
152 try:
152 try:
153 chunks, newopts = filterfn(ui, originalchunks)
153 chunks, newopts = filterfn(ui, originalchunks)
154 except patch.PatchError as err:
154 except patch.PatchError as err:
155 raise error.Abort(_('error parsing patch: %s') % err)
155 raise error.Abort(_('error parsing patch: %s') % err)
156 opts.update(newopts)
156 opts.update(newopts)
157
157
158 # We need to keep a backup of files that have been newly added and
158 # We need to keep a backup of files that have been newly added and
159 # modified during the recording process because there is a previous
159 # modified during the recording process because there is a previous
160 # version without the edit in the workdir
160 # version without the edit in the workdir
161 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
161 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
162 contenders = set()
162 contenders = set()
163 for h in chunks:
163 for h in chunks:
164 try:
164 try:
165 contenders.update(set(h.files()))
165 contenders.update(set(h.files()))
166 except AttributeError:
166 except AttributeError:
167 pass
167 pass
168
168
169 changed = status.modified + status.added + status.removed
169 changed = status.modified + status.added + status.removed
170 newfiles = [f for f in changed if f in contenders]
170 newfiles = [f for f in changed if f in contenders]
171 if not newfiles:
171 if not newfiles:
172 ui.status(_('no changes to record\n'))
172 ui.status(_('no changes to record\n'))
173 return 0
173 return 0
174
174
175 modified = set(status.modified)
175 modified = set(status.modified)
176
176
177 # 2. backup changed files, so we can restore them in the end
177 # 2. backup changed files, so we can restore them in the end
178
178
179 if backupall:
179 if backupall:
180 tobackup = changed
180 tobackup = changed
181 else:
181 else:
182 tobackup = [f for f in newfiles if f in modified or f in \
182 tobackup = [f for f in newfiles if f in modified or f in \
183 newlyaddedandmodifiedfiles]
183 newlyaddedandmodifiedfiles]
184 backups = {}
184 backups = {}
185 if tobackup:
185 if tobackup:
186 backupdir = repo.join('record-backups')
186 backupdir = repo.join('record-backups')
187 try:
187 try:
188 os.mkdir(backupdir)
188 os.mkdir(backupdir)
189 except OSError as err:
189 except OSError as err:
190 if err.errno != errno.EEXIST:
190 if err.errno != errno.EEXIST:
191 raise
191 raise
192 try:
192 try:
193 # backup continues
193 # backup continues
194 for f in tobackup:
194 for f in tobackup:
195 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
195 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
196 dir=backupdir)
196 dir=backupdir)
197 os.close(fd)
197 os.close(fd)
198 ui.debug('backup %r as %r\n' % (f, tmpname))
198 ui.debug('backup %r as %r\n' % (f, tmpname))
199 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
199 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
200 backups[f] = tmpname
200 backups[f] = tmpname
201
201
202 fp = cStringIO.StringIO()
202 fp = cStringIO.StringIO()
203 for c in chunks:
203 for c in chunks:
204 fname = c.filename()
204 fname = c.filename()
205 if fname in backups:
205 if fname in backups:
206 c.write(fp)
206 c.write(fp)
207 dopatch = fp.tell()
207 dopatch = fp.tell()
208 fp.seek(0)
208 fp.seek(0)
209
209
210 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
210 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
211 # 3a. apply filtered patch to clean repo (clean)
211 # 3a. apply filtered patch to clean repo (clean)
212 if backups:
212 if backups:
213 # Equivalent to hg.revert
213 # Equivalent to hg.revert
214 m = scmutil.matchfiles(repo, backups.keys())
214 m = scmutil.matchfiles(repo, backups.keys())
215 mergemod.update(repo, repo.dirstate.p1(),
215 mergemod.update(repo, repo.dirstate.p1(),
216 False, True, matcher=m)
216 False, True, matcher=m)
217
217
218 # 3b. (apply)
218 # 3b. (apply)
219 if dopatch:
219 if dopatch:
220 try:
220 try:
221 ui.debug('applying patch\n')
221 ui.debug('applying patch\n')
222 ui.debug(fp.getvalue())
222 ui.debug(fp.getvalue())
223 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
223 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
224 except patch.PatchError as err:
224 except patch.PatchError as err:
225 raise error.Abort(str(err))
225 raise error.Abort(str(err))
226 del fp
226 del fp
227
227
228 # 4. We prepared working directory according to filtered
228 # 4. We prepared working directory according to filtered
229 # patch. Now is the time to delegate the job to
229 # patch. Now is the time to delegate the job to
230 # commit/qrefresh or the like!
230 # commit/qrefresh or the like!
231
231
232 # Make all of the pathnames absolute.
232 # Make all of the pathnames absolute.
233 newfiles = [repo.wjoin(nf) for nf in newfiles]
233 newfiles = [repo.wjoin(nf) for nf in newfiles]
234 return commitfunc(ui, repo, *newfiles, **opts)
234 return commitfunc(ui, repo, *newfiles, **opts)
235 finally:
235 finally:
236 # 5. finally restore backed-up files
236 # 5. finally restore backed-up files
237 try:
237 try:
238 dirstate = repo.dirstate
238 dirstate = repo.dirstate
239 for realname, tmpname in backups.iteritems():
239 for realname, tmpname in backups.iteritems():
240 ui.debug('restoring %r to %r\n' % (tmpname, realname))
240 ui.debug('restoring %r to %r\n' % (tmpname, realname))
241
241
242 if dirstate[realname] == 'n':
242 if dirstate[realname] == 'n':
243 # without normallookup, restoring timestamp
243 # without normallookup, restoring timestamp
244 # may cause partially committed files
244 # may cause partially committed files
245 # to be treated as unmodified
245 # to be treated as unmodified
246 dirstate.normallookup(realname)
246 dirstate.normallookup(realname)
247
247
248 # copystat=True here and above are a hack to trick any
248 # copystat=True here and above are a hack to trick any
249 # editors that have f open that we haven't modified them.
249 # editors that have f open that we haven't modified them.
250 #
250 #
251 # Also note that this racy as an editor could notice the
251 # Also note that this racy as an editor could notice the
252 # file's mtime before we've finished writing it.
252 # file's mtime before we've finished writing it.
253 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
253 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
254 os.unlink(tmpname)
254 os.unlink(tmpname)
255 if tobackup:
255 if tobackup:
256 os.rmdir(backupdir)
256 os.rmdir(backupdir)
257 except OSError:
257 except OSError:
258 pass
258 pass
259
259
260 def recordinwlock(ui, repo, message, match, opts):
260 def recordinwlock(ui, repo, message, match, opts):
261 with repo.wlock():
261 with repo.wlock():
262 return recordfunc(ui, repo, message, match, opts)
262 return recordfunc(ui, repo, message, match, opts)
263
263
264 return commit(ui, repo, recordinwlock, pats, opts)
264 return commit(ui, repo, recordinwlock, pats, opts)
265
265
266 def findpossible(cmd, table, strict=False):
266 def findpossible(cmd, table, strict=False):
267 """
267 """
268 Return cmd -> (aliases, command table entry)
268 Return cmd -> (aliases, command table entry)
269 for each matching command.
269 for each matching command.
270 Return debug commands (or their aliases) only if no normal command matches.
270 Return debug commands (or their aliases) only if no normal command matches.
271 """
271 """
272 choice = {}
272 choice = {}
273 debugchoice = {}
273 debugchoice = {}
274
274
275 if cmd in table:
275 if cmd in table:
276 # short-circuit exact matches, "log" alias beats "^log|history"
276 # short-circuit exact matches, "log" alias beats "^log|history"
277 keys = [cmd]
277 keys = [cmd]
278 else:
278 else:
279 keys = table.keys()
279 keys = table.keys()
280
280
281 allcmds = []
281 allcmds = []
282 for e in keys:
282 for e in keys:
283 aliases = parsealiases(e)
283 aliases = parsealiases(e)
284 allcmds.extend(aliases)
284 allcmds.extend(aliases)
285 found = None
285 found = None
286 if cmd in aliases:
286 if cmd in aliases:
287 found = cmd
287 found = cmd
288 elif not strict:
288 elif not strict:
289 for a in aliases:
289 for a in aliases:
290 if a.startswith(cmd):
290 if a.startswith(cmd):
291 found = a
291 found = a
292 break
292 break
293 if found is not None:
293 if found is not None:
294 if aliases[0].startswith("debug") or found.startswith("debug"):
294 if aliases[0].startswith("debug") or found.startswith("debug"):
295 debugchoice[found] = (aliases, table[e])
295 debugchoice[found] = (aliases, table[e])
296 else:
296 else:
297 choice[found] = (aliases, table[e])
297 choice[found] = (aliases, table[e])
298
298
299 if not choice and debugchoice:
299 if not choice and debugchoice:
300 choice = debugchoice
300 choice = debugchoice
301
301
302 return choice, allcmds
302 return choice, allcmds
303
303
304 def findcmd(cmd, table, strict=True):
304 def findcmd(cmd, table, strict=True):
305 """Return (aliases, command table entry) for command string."""
305 """Return (aliases, command table entry) for command string."""
306 choice, allcmds = findpossible(cmd, table, strict)
306 choice, allcmds = findpossible(cmd, table, strict)
307
307
308 if cmd in choice:
308 if cmd in choice:
309 return choice[cmd]
309 return choice[cmd]
310
310
311 if len(choice) > 1:
311 if len(choice) > 1:
312 clist = choice.keys()
312 clist = choice.keys()
313 clist.sort()
313 clist.sort()
314 raise error.AmbiguousCommand(cmd, clist)
314 raise error.AmbiguousCommand(cmd, clist)
315
315
316 if choice:
316 if choice:
317 return choice.values()[0]
317 return choice.values()[0]
318
318
319 raise error.UnknownCommand(cmd, allcmds)
319 raise error.UnknownCommand(cmd, allcmds)
320
320
321 def findrepo(p):
321 def findrepo(p):
322 while not os.path.isdir(os.path.join(p, ".hg")):
322 while not os.path.isdir(os.path.join(p, ".hg")):
323 oldp, p = p, os.path.dirname(p)
323 oldp, p = p, os.path.dirname(p)
324 if p == oldp:
324 if p == oldp:
325 return None
325 return None
326
326
327 return p
327 return p
328
328
329 def bailifchanged(repo, merge=True):
329 def bailifchanged(repo, merge=True):
330 if merge and repo.dirstate.p2() != nullid:
330 if merge and repo.dirstate.p2() != nullid:
331 raise error.Abort(_('outstanding uncommitted merge'))
331 raise error.Abort(_('outstanding uncommitted merge'))
332 modified, added, removed, deleted = repo.status()[:4]
332 modified, added, removed, deleted = repo.status()[:4]
333 if modified or added or removed or deleted:
333 if modified or added or removed or deleted:
334 raise error.Abort(_('uncommitted changes'))
334 raise error.Abort(_('uncommitted changes'))
335 ctx = repo[None]
335 ctx = repo[None]
336 for s in sorted(ctx.substate):
336 for s in sorted(ctx.substate):
337 ctx.sub(s).bailifchanged()
337 ctx.sub(s).bailifchanged()
338
338
339 def logmessage(ui, opts):
339 def logmessage(ui, opts):
340 """ get the log message according to -m and -l option """
340 """ get the log message according to -m and -l option """
341 message = opts.get('message')
341 message = opts.get('message')
342 logfile = opts.get('logfile')
342 logfile = opts.get('logfile')
343
343
344 if message and logfile:
344 if message and logfile:
345 raise error.Abort(_('options --message and --logfile are mutually '
345 raise error.Abort(_('options --message and --logfile are mutually '
346 'exclusive'))
346 'exclusive'))
347 if not message and logfile:
347 if not message and logfile:
348 try:
348 try:
349 if logfile == '-':
349 if logfile == '-':
350 message = ui.fin.read()
350 message = ui.fin.read()
351 else:
351 else:
352 message = '\n'.join(util.readfile(logfile).splitlines())
352 message = '\n'.join(util.readfile(logfile).splitlines())
353 except IOError as inst:
353 except IOError as inst:
354 raise error.Abort(_("can't read commit message '%s': %s") %
354 raise error.Abort(_("can't read commit message '%s': %s") %
355 (logfile, inst.strerror))
355 (logfile, inst.strerror))
356 return message
356 return message
357
357
358 def mergeeditform(ctxorbool, baseformname):
358 def mergeeditform(ctxorbool, baseformname):
359 """return appropriate editform name (referencing a committemplate)
359 """return appropriate editform name (referencing a committemplate)
360
360
361 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
361 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
362 merging is committed.
362 merging is committed.
363
363
364 This returns baseformname with '.merge' appended if it is a merge,
364 This returns baseformname with '.merge' appended if it is a merge,
365 otherwise '.normal' is appended.
365 otherwise '.normal' is appended.
366 """
366 """
367 if isinstance(ctxorbool, bool):
367 if isinstance(ctxorbool, bool):
368 if ctxorbool:
368 if ctxorbool:
369 return baseformname + ".merge"
369 return baseformname + ".merge"
370 elif 1 < len(ctxorbool.parents()):
370 elif 1 < len(ctxorbool.parents()):
371 return baseformname + ".merge"
371 return baseformname + ".merge"
372
372
373 return baseformname + ".normal"
373 return baseformname + ".normal"
374
374
375 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
375 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
376 editform='', **opts):
376 editform='', **opts):
377 """get appropriate commit message editor according to '--edit' option
377 """get appropriate commit message editor according to '--edit' option
378
378
379 'finishdesc' is a function to be called with edited commit message
379 'finishdesc' is a function to be called with edited commit message
380 (= 'description' of the new changeset) just after editing, but
380 (= 'description' of the new changeset) just after editing, but
381 before checking empty-ness. It should return actual text to be
381 before checking empty-ness. It should return actual text to be
382 stored into history. This allows to change description before
382 stored into history. This allows to change description before
383 storing.
383 storing.
384
384
385 'extramsg' is a extra message to be shown in the editor instead of
385 'extramsg' is a extra message to be shown in the editor instead of
386 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
386 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
387 is automatically added.
387 is automatically added.
388
388
389 'editform' is a dot-separated list of names, to distinguish
389 'editform' is a dot-separated list of names, to distinguish
390 the purpose of commit text editing.
390 the purpose of commit text editing.
391
391
392 'getcommiteditor' returns 'commitforceeditor' regardless of
392 'getcommiteditor' returns 'commitforceeditor' regardless of
393 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
393 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
394 they are specific for usage in MQ.
394 they are specific for usage in MQ.
395 """
395 """
396 if edit or finishdesc or extramsg:
396 if edit or finishdesc or extramsg:
397 return lambda r, c, s: commitforceeditor(r, c, s,
397 return lambda r, c, s: commitforceeditor(r, c, s,
398 finishdesc=finishdesc,
398 finishdesc=finishdesc,
399 extramsg=extramsg,
399 extramsg=extramsg,
400 editform=editform)
400 editform=editform)
401 elif editform:
401 elif editform:
402 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
402 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
403 else:
403 else:
404 return commiteditor
404 return commiteditor
405
405
406 def loglimit(opts):
406 def loglimit(opts):
407 """get the log limit according to option -l/--limit"""
407 """get the log limit according to option -l/--limit"""
408 limit = opts.get('limit')
408 limit = opts.get('limit')
409 if limit:
409 if limit:
410 try:
410 try:
411 limit = int(limit)
411 limit = int(limit)
412 except ValueError:
412 except ValueError:
413 raise error.Abort(_('limit must be a positive integer'))
413 raise error.Abort(_('limit must be a positive integer'))
414 if limit <= 0:
414 if limit <= 0:
415 raise error.Abort(_('limit must be positive'))
415 raise error.Abort(_('limit must be positive'))
416 else:
416 else:
417 limit = None
417 limit = None
418 return limit
418 return limit
419
419
420 def makefilename(repo, pat, node, desc=None,
420 def makefilename(repo, pat, node, desc=None,
421 total=None, seqno=None, revwidth=None, pathname=None):
421 total=None, seqno=None, revwidth=None, pathname=None):
422 node_expander = {
422 node_expander = {
423 'H': lambda: hex(node),
423 'H': lambda: hex(node),
424 'R': lambda: str(repo.changelog.rev(node)),
424 'R': lambda: str(repo.changelog.rev(node)),
425 'h': lambda: short(node),
425 'h': lambda: short(node),
426 'm': lambda: re.sub('[^\w]', '_', str(desc))
426 'm': lambda: re.sub('[^\w]', '_', str(desc))
427 }
427 }
428 expander = {
428 expander = {
429 '%': lambda: '%',
429 '%': lambda: '%',
430 'b': lambda: os.path.basename(repo.root),
430 'b': lambda: os.path.basename(repo.root),
431 }
431 }
432
432
433 try:
433 try:
434 if node:
434 if node:
435 expander.update(node_expander)
435 expander.update(node_expander)
436 if node:
436 if node:
437 expander['r'] = (lambda:
437 expander['r'] = (lambda:
438 str(repo.changelog.rev(node)).zfill(revwidth or 0))
438 str(repo.changelog.rev(node)).zfill(revwidth or 0))
439 if total is not None:
439 if total is not None:
440 expander['N'] = lambda: str(total)
440 expander['N'] = lambda: str(total)
441 if seqno is not None:
441 if seqno is not None:
442 expander['n'] = lambda: str(seqno)
442 expander['n'] = lambda: str(seqno)
443 if total is not None and seqno is not None:
443 if total is not None and seqno is not None:
444 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
444 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
445 if pathname is not None:
445 if pathname is not None:
446 expander['s'] = lambda: os.path.basename(pathname)
446 expander['s'] = lambda: os.path.basename(pathname)
447 expander['d'] = lambda: os.path.dirname(pathname) or '.'
447 expander['d'] = lambda: os.path.dirname(pathname) or '.'
448 expander['p'] = lambda: pathname
448 expander['p'] = lambda: pathname
449
449
450 newname = []
450 newname = []
451 patlen = len(pat)
451 patlen = len(pat)
452 i = 0
452 i = 0
453 while i < patlen:
453 while i < patlen:
454 c = pat[i]
454 c = pat[i]
455 if c == '%':
455 if c == '%':
456 i += 1
456 i += 1
457 c = pat[i]
457 c = pat[i]
458 c = expander[c]()
458 c = expander[c]()
459 newname.append(c)
459 newname.append(c)
460 i += 1
460 i += 1
461 return ''.join(newname)
461 return ''.join(newname)
462 except KeyError as inst:
462 except KeyError as inst:
463 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
463 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
464 inst.args[0])
464 inst.args[0])
465
465
466 class _unclosablefile(object):
466 class _unclosablefile(object):
467 def __init__(self, fp):
467 def __init__(self, fp):
468 self._fp = fp
468 self._fp = fp
469
469
470 def close(self):
470 def close(self):
471 pass
471 pass
472
472
473 def __iter__(self):
473 def __iter__(self):
474 return iter(self._fp)
474 return iter(self._fp)
475
475
476 def __getattr__(self, attr):
476 def __getattr__(self, attr):
477 return getattr(self._fp, attr)
477 return getattr(self._fp, attr)
478
478
479 def makefileobj(repo, pat, node=None, desc=None, total=None,
479 def makefileobj(repo, pat, node=None, desc=None, total=None,
480 seqno=None, revwidth=None, mode='wb', modemap=None,
480 seqno=None, revwidth=None, mode='wb', modemap=None,
481 pathname=None):
481 pathname=None):
482
482
483 writable = mode not in ('r', 'rb')
483 writable = mode not in ('r', 'rb')
484
484
485 if not pat or pat == '-':
485 if not pat or pat == '-':
486 if writable:
486 if writable:
487 fp = repo.ui.fout
487 fp = repo.ui.fout
488 else:
488 else:
489 fp = repo.ui.fin
489 fp = repo.ui.fin
490 return _unclosablefile(fp)
490 return _unclosablefile(fp)
491 if util.safehasattr(pat, 'write') and writable:
491 if util.safehasattr(pat, 'write') and writable:
492 return pat
492 return pat
493 if util.safehasattr(pat, 'read') and 'r' in mode:
493 if util.safehasattr(pat, 'read') and 'r' in mode:
494 return pat
494 return pat
495 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
495 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
496 if modemap is not None:
496 if modemap is not None:
497 mode = modemap.get(fn, mode)
497 mode = modemap.get(fn, mode)
498 if mode == 'wb':
498 if mode == 'wb':
499 modemap[fn] = 'ab'
499 modemap[fn] = 'ab'
500 return open(fn, mode)
500 return open(fn, mode)
501
501
502 def openrevlog(repo, cmd, file_, opts):
502 def openrevlog(repo, cmd, file_, opts):
503 """opens the changelog, manifest, a filelog or a given revlog"""
503 """opens the changelog, manifest, a filelog or a given revlog"""
504 cl = opts['changelog']
504 cl = opts['changelog']
505 mf = opts['manifest']
505 mf = opts['manifest']
506 dir = opts['dir']
506 dir = opts['dir']
507 msg = None
507 msg = None
508 if cl and mf:
508 if cl and mf:
509 msg = _('cannot specify --changelog and --manifest at the same time')
509 msg = _('cannot specify --changelog and --manifest at the same time')
510 elif cl and dir:
510 elif cl and dir:
511 msg = _('cannot specify --changelog and --dir at the same time')
511 msg = _('cannot specify --changelog and --dir at the same time')
512 elif cl or mf:
512 elif cl or mf:
513 if file_:
513 if file_:
514 msg = _('cannot specify filename with --changelog or --manifest')
514 msg = _('cannot specify filename with --changelog or --manifest')
515 elif not repo:
515 elif not repo:
516 msg = _('cannot specify --changelog or --manifest or --dir '
516 msg = _('cannot specify --changelog or --manifest or --dir '
517 'without a repository')
517 'without a repository')
518 if msg:
518 if msg:
519 raise error.Abort(msg)
519 raise error.Abort(msg)
520
520
521 r = None
521 r = None
522 if repo:
522 if repo:
523 if cl:
523 if cl:
524 r = repo.unfiltered().changelog
524 r = repo.unfiltered().changelog
525 elif dir:
525 elif dir:
526 if 'treemanifest' not in repo.requirements:
526 if 'treemanifest' not in repo.requirements:
527 raise error.Abort(_("--dir can only be used on repos with "
527 raise error.Abort(_("--dir can only be used on repos with "
528 "treemanifest enabled"))
528 "treemanifest enabled"))
529 dirlog = repo.dirlog(file_)
529 dirlog = repo.dirlog(file_)
530 if len(dirlog):
530 if len(dirlog):
531 r = dirlog
531 r = dirlog
532 elif mf:
532 elif mf:
533 r = repo.manifest
533 r = repo.manifest
534 elif file_:
534 elif file_:
535 filelog = repo.file(file_)
535 filelog = repo.file(file_)
536 if len(filelog):
536 if len(filelog):
537 r = filelog
537 r = filelog
538 if not r:
538 if not r:
539 if not file_:
539 if not file_:
540 raise error.CommandError(cmd, _('invalid arguments'))
540 raise error.CommandError(cmd, _('invalid arguments'))
541 if not os.path.isfile(file_):
541 if not os.path.isfile(file_):
542 raise error.Abort(_("revlog '%s' not found") % file_)
542 raise error.Abort(_("revlog '%s' not found") % file_)
543 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
543 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
544 file_[:-2] + ".i")
544 file_[:-2] + ".i")
545 return r
545 return r
546
546
547 def copy(ui, repo, pats, opts, rename=False):
547 def copy(ui, repo, pats, opts, rename=False):
548 # called with the repo lock held
548 # called with the repo lock held
549 #
549 #
550 # hgsep => pathname that uses "/" to separate directories
550 # hgsep => pathname that uses "/" to separate directories
551 # ossep => pathname that uses os.sep to separate directories
551 # ossep => pathname that uses os.sep to separate directories
552 cwd = repo.getcwd()
552 cwd = repo.getcwd()
553 targets = {}
553 targets = {}
554 after = opts.get("after")
554 after = opts.get("after")
555 dryrun = opts.get("dry_run")
555 dryrun = opts.get("dry_run")
556 wctx = repo[None]
556 wctx = repo[None]
557
557
558 def walkpat(pat):
558 def walkpat(pat):
559 srcs = []
559 srcs = []
560 if after:
560 if after:
561 badstates = '?'
561 badstates = '?'
562 else:
562 else:
563 badstates = '?r'
563 badstates = '?r'
564 m = scmutil.match(repo[None], [pat], opts, globbed=True)
564 m = scmutil.match(repo[None], [pat], opts, globbed=True)
565 for abs in repo.walk(m):
565 for abs in repo.walk(m):
566 state = repo.dirstate[abs]
566 state = repo.dirstate[abs]
567 rel = m.rel(abs)
567 rel = m.rel(abs)
568 exact = m.exact(abs)
568 exact = m.exact(abs)
569 if state in badstates:
569 if state in badstates:
570 if exact and state == '?':
570 if exact and state == '?':
571 ui.warn(_('%s: not copying - file is not managed\n') % rel)
571 ui.warn(_('%s: not copying - file is not managed\n') % rel)
572 if exact and state == 'r':
572 if exact and state == 'r':
573 ui.warn(_('%s: not copying - file has been marked for'
573 ui.warn(_('%s: not copying - file has been marked for'
574 ' remove\n') % rel)
574 ' remove\n') % rel)
575 continue
575 continue
576 # abs: hgsep
576 # abs: hgsep
577 # rel: ossep
577 # rel: ossep
578 srcs.append((abs, rel, exact))
578 srcs.append((abs, rel, exact))
579 return srcs
579 return srcs
580
580
581 # abssrc: hgsep
581 # abssrc: hgsep
582 # relsrc: ossep
582 # relsrc: ossep
583 # otarget: ossep
583 # otarget: ossep
584 def copyfile(abssrc, relsrc, otarget, exact):
584 def copyfile(abssrc, relsrc, otarget, exact):
585 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
585 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
586 if '/' in abstarget:
586 if '/' in abstarget:
587 # We cannot normalize abstarget itself, this would prevent
587 # We cannot normalize abstarget itself, this would prevent
588 # case only renames, like a => A.
588 # case only renames, like a => A.
589 abspath, absname = abstarget.rsplit('/', 1)
589 abspath, absname = abstarget.rsplit('/', 1)
590 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
590 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
591 reltarget = repo.pathto(abstarget, cwd)
591 reltarget = repo.pathto(abstarget, cwd)
592 target = repo.wjoin(abstarget)
592 target = repo.wjoin(abstarget)
593 src = repo.wjoin(abssrc)
593 src = repo.wjoin(abssrc)
594 state = repo.dirstate[abstarget]
594 state = repo.dirstate[abstarget]
595
595
596 scmutil.checkportable(ui, abstarget)
596 scmutil.checkportable(ui, abstarget)
597
597
598 # check for collisions
598 # check for collisions
599 prevsrc = targets.get(abstarget)
599 prevsrc = targets.get(abstarget)
600 if prevsrc is not None:
600 if prevsrc is not None:
601 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
601 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
602 (reltarget, repo.pathto(abssrc, cwd),
602 (reltarget, repo.pathto(abssrc, cwd),
603 repo.pathto(prevsrc, cwd)))
603 repo.pathto(prevsrc, cwd)))
604 return
604 return
605
605
606 # check for overwrites
606 # check for overwrites
607 exists = os.path.lexists(target)
607 exists = os.path.lexists(target)
608 samefile = False
608 samefile = False
609 if exists and abssrc != abstarget:
609 if exists and abssrc != abstarget:
610 if (repo.dirstate.normalize(abssrc) ==
610 if (repo.dirstate.normalize(abssrc) ==
611 repo.dirstate.normalize(abstarget)):
611 repo.dirstate.normalize(abstarget)):
612 if not rename:
612 if not rename:
613 ui.warn(_("%s: can't copy - same file\n") % reltarget)
613 ui.warn(_("%s: can't copy - same file\n") % reltarget)
614 return
614 return
615 exists = False
615 exists = False
616 samefile = True
616 samefile = True
617
617
618 if not after and exists or after and state in 'mn':
618 if not after and exists or after and state in 'mn':
619 if not opts['force']:
619 if not opts['force']:
620 ui.warn(_('%s: not overwriting - file exists\n') %
620 ui.warn(_('%s: not overwriting - file exists\n') %
621 reltarget)
621 reltarget)
622 return
622 return
623
623
624 if after:
624 if after:
625 if not exists:
625 if not exists:
626 if rename:
626 if rename:
627 ui.warn(_('%s: not recording move - %s does not exist\n') %
627 ui.warn(_('%s: not recording move - %s does not exist\n') %
628 (relsrc, reltarget))
628 (relsrc, reltarget))
629 else:
629 else:
630 ui.warn(_('%s: not recording copy - %s does not exist\n') %
630 ui.warn(_('%s: not recording copy - %s does not exist\n') %
631 (relsrc, reltarget))
631 (relsrc, reltarget))
632 return
632 return
633 elif not dryrun:
633 elif not dryrun:
634 try:
634 try:
635 if exists:
635 if exists:
636 os.unlink(target)
636 os.unlink(target)
637 targetdir = os.path.dirname(target) or '.'
637 targetdir = os.path.dirname(target) or '.'
638 if not os.path.isdir(targetdir):
638 if not os.path.isdir(targetdir):
639 os.makedirs(targetdir)
639 os.makedirs(targetdir)
640 if samefile:
640 if samefile:
641 tmp = target + "~hgrename"
641 tmp = target + "~hgrename"
642 os.rename(src, tmp)
642 os.rename(src, tmp)
643 os.rename(tmp, target)
643 os.rename(tmp, target)
644 else:
644 else:
645 util.copyfile(src, target)
645 util.copyfile(src, target)
646 srcexists = True
646 srcexists = True
647 except IOError as inst:
647 except IOError as inst:
648 if inst.errno == errno.ENOENT:
648 if inst.errno == errno.ENOENT:
649 ui.warn(_('%s: deleted in working directory\n') % relsrc)
649 ui.warn(_('%s: deleted in working directory\n') % relsrc)
650 srcexists = False
650 srcexists = False
651 else:
651 else:
652 ui.warn(_('%s: cannot copy - %s\n') %
652 ui.warn(_('%s: cannot copy - %s\n') %
653 (relsrc, inst.strerror))
653 (relsrc, inst.strerror))
654 return True # report a failure
654 return True # report a failure
655
655
656 if ui.verbose or not exact:
656 if ui.verbose or not exact:
657 if rename:
657 if rename:
658 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
658 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
659 else:
659 else:
660 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
660 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
661
661
662 targets[abstarget] = abssrc
662 targets[abstarget] = abssrc
663
663
664 # fix up dirstate
664 # fix up dirstate
665 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
665 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
666 dryrun=dryrun, cwd=cwd)
666 dryrun=dryrun, cwd=cwd)
667 if rename and not dryrun:
667 if rename and not dryrun:
668 if not after and srcexists and not samefile:
668 if not after and srcexists and not samefile:
669 util.unlinkpath(repo.wjoin(abssrc))
669 util.unlinkpath(repo.wjoin(abssrc))
670 wctx.forget([abssrc])
670 wctx.forget([abssrc])
671
671
672 # pat: ossep
672 # pat: ossep
673 # dest ossep
673 # dest ossep
674 # srcs: list of (hgsep, hgsep, ossep, bool)
674 # srcs: list of (hgsep, hgsep, ossep, bool)
675 # return: function that takes hgsep and returns ossep
675 # return: function that takes hgsep and returns ossep
676 def targetpathfn(pat, dest, srcs):
676 def targetpathfn(pat, dest, srcs):
677 if os.path.isdir(pat):
677 if os.path.isdir(pat):
678 abspfx = pathutil.canonpath(repo.root, cwd, pat)
678 abspfx = pathutil.canonpath(repo.root, cwd, pat)
679 abspfx = util.localpath(abspfx)
679 abspfx = util.localpath(abspfx)
680 if destdirexists:
680 if destdirexists:
681 striplen = len(os.path.split(abspfx)[0])
681 striplen = len(os.path.split(abspfx)[0])
682 else:
682 else:
683 striplen = len(abspfx)
683 striplen = len(abspfx)
684 if striplen:
684 if striplen:
685 striplen += len(os.sep)
685 striplen += len(os.sep)
686 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
686 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
687 elif destdirexists:
687 elif destdirexists:
688 res = lambda p: os.path.join(dest,
688 res = lambda p: os.path.join(dest,
689 os.path.basename(util.localpath(p)))
689 os.path.basename(util.localpath(p)))
690 else:
690 else:
691 res = lambda p: dest
691 res = lambda p: dest
692 return res
692 return res
693
693
694 # pat: ossep
694 # pat: ossep
695 # dest ossep
695 # dest ossep
696 # srcs: list of (hgsep, hgsep, ossep, bool)
696 # srcs: list of (hgsep, hgsep, ossep, bool)
697 # return: function that takes hgsep and returns ossep
697 # return: function that takes hgsep and returns ossep
698 def targetpathafterfn(pat, dest, srcs):
698 def targetpathafterfn(pat, dest, srcs):
699 if matchmod.patkind(pat):
699 if matchmod.patkind(pat):
700 # a mercurial pattern
700 # a mercurial pattern
701 res = lambda p: os.path.join(dest,
701 res = lambda p: os.path.join(dest,
702 os.path.basename(util.localpath(p)))
702 os.path.basename(util.localpath(p)))
703 else:
703 else:
704 abspfx = pathutil.canonpath(repo.root, cwd, pat)
704 abspfx = pathutil.canonpath(repo.root, cwd, pat)
705 if len(abspfx) < len(srcs[0][0]):
705 if len(abspfx) < len(srcs[0][0]):
706 # A directory. Either the target path contains the last
706 # A directory. Either the target path contains the last
707 # component of the source path or it does not.
707 # component of the source path or it does not.
708 def evalpath(striplen):
708 def evalpath(striplen):
709 score = 0
709 score = 0
710 for s in srcs:
710 for s in srcs:
711 t = os.path.join(dest, util.localpath(s[0])[striplen:])
711 t = os.path.join(dest, util.localpath(s[0])[striplen:])
712 if os.path.lexists(t):
712 if os.path.lexists(t):
713 score += 1
713 score += 1
714 return score
714 return score
715
715
716 abspfx = util.localpath(abspfx)
716 abspfx = util.localpath(abspfx)
717 striplen = len(abspfx)
717 striplen = len(abspfx)
718 if striplen:
718 if striplen:
719 striplen += len(os.sep)
719 striplen += len(os.sep)
720 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
720 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
721 score = evalpath(striplen)
721 score = evalpath(striplen)
722 striplen1 = len(os.path.split(abspfx)[0])
722 striplen1 = len(os.path.split(abspfx)[0])
723 if striplen1:
723 if striplen1:
724 striplen1 += len(os.sep)
724 striplen1 += len(os.sep)
725 if evalpath(striplen1) > score:
725 if evalpath(striplen1) > score:
726 striplen = striplen1
726 striplen = striplen1
727 res = lambda p: os.path.join(dest,
727 res = lambda p: os.path.join(dest,
728 util.localpath(p)[striplen:])
728 util.localpath(p)[striplen:])
729 else:
729 else:
730 # a file
730 # a file
731 if destdirexists:
731 if destdirexists:
732 res = lambda p: os.path.join(dest,
732 res = lambda p: os.path.join(dest,
733 os.path.basename(util.localpath(p)))
733 os.path.basename(util.localpath(p)))
734 else:
734 else:
735 res = lambda p: dest
735 res = lambda p: dest
736 return res
736 return res
737
737
738 pats = scmutil.expandpats(pats)
738 pats = scmutil.expandpats(pats)
739 if not pats:
739 if not pats:
740 raise error.Abort(_('no source or destination specified'))
740 raise error.Abort(_('no source or destination specified'))
741 if len(pats) == 1:
741 if len(pats) == 1:
742 raise error.Abort(_('no destination specified'))
742 raise error.Abort(_('no destination specified'))
743 dest = pats.pop()
743 dest = pats.pop()
744 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
744 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
745 if not destdirexists:
745 if not destdirexists:
746 if len(pats) > 1 or matchmod.patkind(pats[0]):
746 if len(pats) > 1 or matchmod.patkind(pats[0]):
747 raise error.Abort(_('with multiple sources, destination must be an '
747 raise error.Abort(_('with multiple sources, destination must be an '
748 'existing directory'))
748 'existing directory'))
749 if util.endswithsep(dest):
749 if util.endswithsep(dest):
750 raise error.Abort(_('destination %s is not a directory') % dest)
750 raise error.Abort(_('destination %s is not a directory') % dest)
751
751
752 tfn = targetpathfn
752 tfn = targetpathfn
753 if after:
753 if after:
754 tfn = targetpathafterfn
754 tfn = targetpathafterfn
755 copylist = []
755 copylist = []
756 for pat in pats:
756 for pat in pats:
757 srcs = walkpat(pat)
757 srcs = walkpat(pat)
758 if not srcs:
758 if not srcs:
759 continue
759 continue
760 copylist.append((tfn(pat, dest, srcs), srcs))
760 copylist.append((tfn(pat, dest, srcs), srcs))
761 if not copylist:
761 if not copylist:
762 raise error.Abort(_('no files to copy'))
762 raise error.Abort(_('no files to copy'))
763
763
764 errors = 0
764 errors = 0
765 for targetpath, srcs in copylist:
765 for targetpath, srcs in copylist:
766 for abssrc, relsrc, exact in srcs:
766 for abssrc, relsrc, exact in srcs:
767 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
767 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
768 errors += 1
768 errors += 1
769
769
770 if errors:
770 if errors:
771 ui.warn(_('(consider using --after)\n'))
771 ui.warn(_('(consider using --after)\n'))
772
772
773 return errors != 0
773 return errors != 0
774
774
775 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
775 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
776 runargs=None, appendpid=False):
776 runargs=None, appendpid=False):
777 '''Run a command as a service.'''
777 '''Run a command as a service.'''
778
778
779 def writepid(pid):
779 def writepid(pid):
780 if opts['pid_file']:
780 if opts['pid_file']:
781 if appendpid:
781 if appendpid:
782 mode = 'a'
782 mode = 'a'
783 else:
783 else:
784 mode = 'w'
784 mode = 'w'
785 fp = open(opts['pid_file'], mode)
785 fp = open(opts['pid_file'], mode)
786 fp.write(str(pid) + '\n')
786 fp.write(str(pid) + '\n')
787 fp.close()
787 fp.close()
788
788
789 if opts['daemon'] and not opts['daemon_postexec']:
789 if opts['daemon'] and not opts['daemon_postexec']:
790 # Signal child process startup with file removal
790 # Signal child process startup with file removal
791 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
791 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
792 os.close(lockfd)
792 os.close(lockfd)
793 try:
793 try:
794 if not runargs:
794 if not runargs:
795 runargs = util.hgcmd() + sys.argv[1:]
795 runargs = util.hgcmd() + sys.argv[1:]
796 runargs.append('--daemon-postexec=unlink:%s' % lockpath)
796 runargs.append('--daemon-postexec=unlink:%s' % lockpath)
797 # Don't pass --cwd to the child process, because we've already
797 # Don't pass --cwd to the child process, because we've already
798 # changed directory.
798 # changed directory.
799 for i in xrange(1, len(runargs)):
799 for i in xrange(1, len(runargs)):
800 if runargs[i].startswith('--cwd='):
800 if runargs[i].startswith('--cwd='):
801 del runargs[i]
801 del runargs[i]
802 break
802 break
803 elif runargs[i].startswith('--cwd'):
803 elif runargs[i].startswith('--cwd'):
804 del runargs[i:i + 2]
804 del runargs[i:i + 2]
805 break
805 break
806 def condfn():
806 def condfn():
807 return not os.path.exists(lockpath)
807 return not os.path.exists(lockpath)
808 pid = util.rundetached(runargs, condfn)
808 pid = util.rundetached(runargs, condfn)
809 if pid < 0:
809 if pid < 0:
810 raise error.Abort(_('child process failed to start'))
810 raise error.Abort(_('child process failed to start'))
811 writepid(pid)
811 writepid(pid)
812 finally:
812 finally:
813 try:
813 try:
814 os.unlink(lockpath)
814 os.unlink(lockpath)
815 except OSError as e:
815 except OSError as e:
816 if e.errno != errno.ENOENT:
816 if e.errno != errno.ENOENT:
817 raise
817 raise
818 if parentfn:
818 if parentfn:
819 return parentfn(pid)
819 return parentfn(pid)
820 else:
820 else:
821 return
821 return
822
822
823 if initfn:
823 if initfn:
824 initfn()
824 initfn()
825
825
826 if not opts['daemon']:
826 if not opts['daemon']:
827 writepid(util.getpid())
827 writepid(util.getpid())
828
828
829 if opts['daemon_postexec']:
829 if opts['daemon_postexec']:
830 try:
830 try:
831 os.setsid()
831 os.setsid()
832 except AttributeError:
832 except AttributeError:
833 pass
833 pass
834 for inst in opts['daemon_postexec']:
834 for inst in opts['daemon_postexec']:
835 if inst.startswith('unlink:'):
835 if inst.startswith('unlink:'):
836 lockpath = inst[7:]
836 lockpath = inst[7:]
837 os.unlink(lockpath)
837 os.unlink(lockpath)
838 elif inst.startswith('chdir:'):
838 elif inst.startswith('chdir:'):
839 os.chdir(inst[6:])
839 os.chdir(inst[6:])
840 elif inst != 'none':
840 elif inst != 'none':
841 raise error.Abort(_('invalid value for --daemon-postexec: %s')
841 raise error.Abort(_('invalid value for --daemon-postexec: %s')
842 % inst)
842 % inst)
843 util.hidewindow()
843 util.hidewindow()
844 sys.stdout.flush()
844 sys.stdout.flush()
845 sys.stderr.flush()
845 sys.stderr.flush()
846
846
847 nullfd = os.open(os.devnull, os.O_RDWR)
847 nullfd = os.open(os.devnull, os.O_RDWR)
848 logfilefd = nullfd
848 logfilefd = nullfd
849 if logfile:
849 if logfile:
850 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
850 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
851 os.dup2(nullfd, 0)
851 os.dup2(nullfd, 0)
852 os.dup2(logfilefd, 1)
852 os.dup2(logfilefd, 1)
853 os.dup2(logfilefd, 2)
853 os.dup2(logfilefd, 2)
854 if nullfd not in (0, 1, 2):
854 if nullfd not in (0, 1, 2):
855 os.close(nullfd)
855 os.close(nullfd)
856 if logfile and logfilefd not in (0, 1, 2):
856 if logfile and logfilefd not in (0, 1, 2):
857 os.close(logfilefd)
857 os.close(logfilefd)
858
858
859 if runfn:
859 if runfn:
860 return runfn()
860 return runfn()
861
861
862 ## facility to let extension process additional data into an import patch
862 ## facility to let extension process additional data into an import patch
863 # list of identifier to be executed in order
863 # list of identifier to be executed in order
864 extrapreimport = [] # run before commit
864 extrapreimport = [] # run before commit
865 extrapostimport = [] # run after commit
865 extrapostimport = [] # run after commit
866 # mapping from identifier to actual import function
866 # mapping from identifier to actual import function
867 #
867 #
868 # 'preimport' are run before the commit is made and are provided the following
868 # 'preimport' are run before the commit is made and are provided the following
869 # arguments:
869 # arguments:
870 # - repo: the localrepository instance,
870 # - repo: the localrepository instance,
871 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
871 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
872 # - extra: the future extra dictionary of the changeset, please mutate it,
872 # - extra: the future extra dictionary of the changeset, please mutate it,
873 # - opts: the import options.
873 # - opts: the import options.
874 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
874 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
875 # mutation of in memory commit and more. Feel free to rework the code to get
875 # mutation of in memory commit and more. Feel free to rework the code to get
876 # there.
876 # there.
877 extrapreimportmap = {}
877 extrapreimportmap = {}
878 # 'postimport' are run after the commit is made and are provided the following
878 # 'postimport' are run after the commit is made and are provided the following
879 # argument:
879 # argument:
880 # - ctx: the changectx created by import.
880 # - ctx: the changectx created by import.
881 extrapostimportmap = {}
881 extrapostimportmap = {}
882
882
883 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
883 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
884 """Utility function used by commands.import to import a single patch
884 """Utility function used by commands.import to import a single patch
885
885
886 This function is explicitly defined here to help the evolve extension to
886 This function is explicitly defined here to help the evolve extension to
887 wrap this part of the import logic.
887 wrap this part of the import logic.
888
888
889 The API is currently a bit ugly because it a simple code translation from
889 The API is currently a bit ugly because it a simple code translation from
890 the import command. Feel free to make it better.
890 the import command. Feel free to make it better.
891
891
892 :hunk: a patch (as a binary string)
892 :hunk: a patch (as a binary string)
893 :parents: nodes that will be parent of the created commit
893 :parents: nodes that will be parent of the created commit
894 :opts: the full dict of option passed to the import command
894 :opts: the full dict of option passed to the import command
895 :msgs: list to save commit message to.
895 :msgs: list to save commit message to.
896 (used in case we need to save it when failing)
896 (used in case we need to save it when failing)
897 :updatefunc: a function that update a repo to a given node
897 :updatefunc: a function that update a repo to a given node
898 updatefunc(<repo>, <node>)
898 updatefunc(<repo>, <node>)
899 """
899 """
900 # avoid cycle context -> subrepo -> cmdutil
900 # avoid cycle context -> subrepo -> cmdutil
901 from . import context
901 from . import context
902 extractdata = patch.extract(ui, hunk)
902 extractdata = patch.extract(ui, hunk)
903 tmpname = extractdata.get('filename')
903 tmpname = extractdata.get('filename')
904 message = extractdata.get('message')
904 message = extractdata.get('message')
905 user = opts.get('user') or extractdata.get('user')
905 user = opts.get('user') or extractdata.get('user')
906 date = opts.get('date') or extractdata.get('date')
906 date = opts.get('date') or extractdata.get('date')
907 branch = extractdata.get('branch')
907 branch = extractdata.get('branch')
908 nodeid = extractdata.get('nodeid')
908 nodeid = extractdata.get('nodeid')
909 p1 = extractdata.get('p1')
909 p1 = extractdata.get('p1')
910 p2 = extractdata.get('p2')
910 p2 = extractdata.get('p2')
911
911
912 nocommit = opts.get('no_commit')
912 nocommit = opts.get('no_commit')
913 importbranch = opts.get('import_branch')
913 importbranch = opts.get('import_branch')
914 update = not opts.get('bypass')
914 update = not opts.get('bypass')
915 strip = opts["strip"]
915 strip = opts["strip"]
916 prefix = opts["prefix"]
916 prefix = opts["prefix"]
917 sim = float(opts.get('similarity') or 0)
917 sim = float(opts.get('similarity') or 0)
918 if not tmpname:
918 if not tmpname:
919 return (None, None, False)
919 return (None, None, False)
920
920
921 rejects = False
921 rejects = False
922
922
923 try:
923 try:
924 cmdline_message = logmessage(ui, opts)
924 cmdline_message = logmessage(ui, opts)
925 if cmdline_message:
925 if cmdline_message:
926 # pickup the cmdline msg
926 # pickup the cmdline msg
927 message = cmdline_message
927 message = cmdline_message
928 elif message:
928 elif message:
929 # pickup the patch msg
929 # pickup the patch msg
930 message = message.strip()
930 message = message.strip()
931 else:
931 else:
932 # launch the editor
932 # launch the editor
933 message = None
933 message = None
934 ui.debug('message:\n%s\n' % message)
934 ui.debug('message:\n%s\n' % message)
935
935
936 if len(parents) == 1:
936 if len(parents) == 1:
937 parents.append(repo[nullid])
937 parents.append(repo[nullid])
938 if opts.get('exact'):
938 if opts.get('exact'):
939 if not nodeid or not p1:
939 if not nodeid or not p1:
940 raise error.Abort(_('not a Mercurial patch'))
940 raise error.Abort(_('not a Mercurial patch'))
941 p1 = repo[p1]
941 p1 = repo[p1]
942 p2 = repo[p2 or nullid]
942 p2 = repo[p2 or nullid]
943 elif p2:
943 elif p2:
944 try:
944 try:
945 p1 = repo[p1]
945 p1 = repo[p1]
946 p2 = repo[p2]
946 p2 = repo[p2]
947 # Without any options, consider p2 only if the
947 # Without any options, consider p2 only if the
948 # patch is being applied on top of the recorded
948 # patch is being applied on top of the recorded
949 # first parent.
949 # first parent.
950 if p1 != parents[0]:
950 if p1 != parents[0]:
951 p1 = parents[0]
951 p1 = parents[0]
952 p2 = repo[nullid]
952 p2 = repo[nullid]
953 except error.RepoError:
953 except error.RepoError:
954 p1, p2 = parents
954 p1, p2 = parents
955 if p2.node() == nullid:
955 if p2.node() == nullid:
956 ui.warn(_("warning: import the patch as a normal revision\n"
956 ui.warn(_("warning: import the patch as a normal revision\n"
957 "(use --exact to import the patch as a merge)\n"))
957 "(use --exact to import the patch as a merge)\n"))
958 else:
958 else:
959 p1, p2 = parents
959 p1, p2 = parents
960
960
961 n = None
961 n = None
962 if update:
962 if update:
963 if p1 != parents[0]:
963 if p1 != parents[0]:
964 updatefunc(repo, p1.node())
964 updatefunc(repo, p1.node())
965 if p2 != parents[1]:
965 if p2 != parents[1]:
966 repo.setparents(p1.node(), p2.node())
966 repo.setparents(p1.node(), p2.node())
967
967
968 if opts.get('exact') or importbranch:
968 if opts.get('exact') or importbranch:
969 repo.dirstate.setbranch(branch or 'default')
969 repo.dirstate.setbranch(branch or 'default')
970
970
971 partial = opts.get('partial', False)
971 partial = opts.get('partial', False)
972 files = set()
972 files = set()
973 try:
973 try:
974 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
974 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
975 files=files, eolmode=None, similarity=sim / 100.0)
975 files=files, eolmode=None, similarity=sim / 100.0)
976 except patch.PatchError as e:
976 except patch.PatchError as e:
977 if not partial:
977 if not partial:
978 raise error.Abort(str(e))
978 raise error.Abort(str(e))
979 if partial:
979 if partial:
980 rejects = True
980 rejects = True
981
981
982 files = list(files)
982 files = list(files)
983 if nocommit:
983 if nocommit:
984 if message:
984 if message:
985 msgs.append(message)
985 msgs.append(message)
986 else:
986 else:
987 if opts.get('exact') or p2:
987 if opts.get('exact') or p2:
988 # If you got here, you either use --force and know what
988 # If you got here, you either use --force and know what
989 # you are doing or used --exact or a merge patch while
989 # you are doing or used --exact or a merge patch while
990 # being updated to its first parent.
990 # being updated to its first parent.
991 m = None
991 m = None
992 else:
992 else:
993 m = scmutil.matchfiles(repo, files or [])
993 m = scmutil.matchfiles(repo, files or [])
994 editform = mergeeditform(repo[None], 'import.normal')
994 editform = mergeeditform(repo[None], 'import.normal')
995 if opts.get('exact'):
995 if opts.get('exact'):
996 editor = None
996 editor = None
997 else:
997 else:
998 editor = getcommiteditor(editform=editform, **opts)
998 editor = getcommiteditor(editform=editform, **opts)
999 allowemptyback = repo.ui.backupconfig('ui', 'allowemptycommit')
999 allowemptyback = repo.ui.backupconfig('ui', 'allowemptycommit')
1000 extra = {}
1000 extra = {}
1001 for idfunc in extrapreimport:
1001 for idfunc in extrapreimport:
1002 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
1002 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
1003 try:
1003 try:
1004 if partial:
1004 if partial:
1005 repo.ui.setconfig('ui', 'allowemptycommit', True)
1005 repo.ui.setconfig('ui', 'allowemptycommit', True)
1006 n = repo.commit(message, user,
1006 n = repo.commit(message, user,
1007 date, match=m,
1007 date, match=m,
1008 editor=editor, extra=extra)
1008 editor=editor, extra=extra)
1009 for idfunc in extrapostimport:
1009 for idfunc in extrapostimport:
1010 extrapostimportmap[idfunc](repo[n])
1010 extrapostimportmap[idfunc](repo[n])
1011 finally:
1011 finally:
1012 repo.ui.restoreconfig(allowemptyback)
1012 repo.ui.restoreconfig(allowemptyback)
1013 else:
1013 else:
1014 if opts.get('exact') or importbranch:
1014 if opts.get('exact') or importbranch:
1015 branch = branch or 'default'
1015 branch = branch or 'default'
1016 else:
1016 else:
1017 branch = p1.branch()
1017 branch = p1.branch()
1018 store = patch.filestore()
1018 store = patch.filestore()
1019 try:
1019 try:
1020 files = set()
1020 files = set()
1021 try:
1021 try:
1022 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1022 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1023 files, eolmode=None)
1023 files, eolmode=None)
1024 except patch.PatchError as e:
1024 except patch.PatchError as e:
1025 raise error.Abort(str(e))
1025 raise error.Abort(str(e))
1026 if opts.get('exact'):
1026 if opts.get('exact'):
1027 editor = None
1027 editor = None
1028 else:
1028 else:
1029 editor = getcommiteditor(editform='import.bypass')
1029 editor = getcommiteditor(editform='import.bypass')
1030 memctx = context.makememctx(repo, (p1.node(), p2.node()),
1030 memctx = context.makememctx(repo, (p1.node(), p2.node()),
1031 message,
1031 message,
1032 user,
1032 user,
1033 date,
1033 date,
1034 branch, files, store,
1034 branch, files, store,
1035 editor=editor)
1035 editor=editor)
1036 n = memctx.commit()
1036 n = memctx.commit()
1037 finally:
1037 finally:
1038 store.close()
1038 store.close()
1039 if opts.get('exact') and nocommit:
1039 if opts.get('exact') and nocommit:
1040 # --exact with --no-commit is still useful in that it does merge
1040 # --exact with --no-commit is still useful in that it does merge
1041 # and branch bits
1041 # and branch bits
1042 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1042 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1043 elif opts.get('exact') and hex(n) != nodeid:
1043 elif opts.get('exact') and hex(n) != nodeid:
1044 raise error.Abort(_('patch is damaged or loses information'))
1044 raise error.Abort(_('patch is damaged or loses information'))
1045 msg = _('applied to working directory')
1045 msg = _('applied to working directory')
1046 if n:
1046 if n:
1047 # i18n: refers to a short changeset id
1047 # i18n: refers to a short changeset id
1048 msg = _('created %s') % short(n)
1048 msg = _('created %s') % short(n)
1049 return (msg, n, rejects)
1049 return (msg, n, rejects)
1050 finally:
1050 finally:
1051 os.unlink(tmpname)
1051 os.unlink(tmpname)
1052
1052
1053 # facility to let extensions include additional data in an exported patch
1053 # facility to let extensions include additional data in an exported patch
1054 # list of identifiers to be executed in order
1054 # list of identifiers to be executed in order
1055 extraexport = []
1055 extraexport = []
1056 # mapping from identifier to actual export function
1056 # mapping from identifier to actual export function
1057 # function as to return a string to be added to the header or None
1057 # function as to return a string to be added to the header or None
1058 # it is given two arguments (sequencenumber, changectx)
1058 # it is given two arguments (sequencenumber, changectx)
1059 extraexportmap = {}
1059 extraexportmap = {}
1060
1060
1061 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1061 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1062 opts=None, match=None):
1062 opts=None, match=None):
1063 '''export changesets as hg patches.'''
1063 '''export changesets as hg patches.'''
1064
1064
1065 total = len(revs)
1065 total = len(revs)
1066 revwidth = max([len(str(rev)) for rev in revs])
1066 revwidth = max([len(str(rev)) for rev in revs])
1067 filemode = {}
1067 filemode = {}
1068
1068
1069 def single(rev, seqno, fp):
1069 def single(rev, seqno, fp):
1070 ctx = repo[rev]
1070 ctx = repo[rev]
1071 node = ctx.node()
1071 node = ctx.node()
1072 parents = [p.node() for p in ctx.parents() if p]
1072 parents = [p.node() for p in ctx.parents() if p]
1073 branch = ctx.branch()
1073 branch = ctx.branch()
1074 if switch_parent:
1074 if switch_parent:
1075 parents.reverse()
1075 parents.reverse()
1076
1076
1077 if parents:
1077 if parents:
1078 prev = parents[0]
1078 prev = parents[0]
1079 else:
1079 else:
1080 prev = nullid
1080 prev = nullid
1081
1081
1082 shouldclose = False
1082 shouldclose = False
1083 if not fp and len(template) > 0:
1083 if not fp and len(template) > 0:
1084 desc_lines = ctx.description().rstrip().split('\n')
1084 desc_lines = ctx.description().rstrip().split('\n')
1085 desc = desc_lines[0] #Commit always has a first line.
1085 desc = desc_lines[0] #Commit always has a first line.
1086 fp = makefileobj(repo, template, node, desc=desc, total=total,
1086 fp = makefileobj(repo, template, node, desc=desc, total=total,
1087 seqno=seqno, revwidth=revwidth, mode='wb',
1087 seqno=seqno, revwidth=revwidth, mode='wb',
1088 modemap=filemode)
1088 modemap=filemode)
1089 shouldclose = True
1089 shouldclose = True
1090 if fp and not getattr(fp, 'name', '<unnamed>').startswith('<'):
1090 if fp and not getattr(fp, 'name', '<unnamed>').startswith('<'):
1091 repo.ui.note("%s\n" % fp.name)
1091 repo.ui.note("%s\n" % fp.name)
1092
1092
1093 if not fp:
1093 if not fp:
1094 write = repo.ui.write
1094 write = repo.ui.write
1095 else:
1095 else:
1096 def write(s, **kw):
1096 def write(s, **kw):
1097 fp.write(s)
1097 fp.write(s)
1098
1098
1099 write("# HG changeset patch\n")
1099 write("# HG changeset patch\n")
1100 write("# User %s\n" % ctx.user())
1100 write("# User %s\n" % ctx.user())
1101 write("# Date %d %d\n" % ctx.date())
1101 write("# Date %d %d\n" % ctx.date())
1102 write("# %s\n" % util.datestr(ctx.date()))
1102 write("# %s\n" % util.datestr(ctx.date()))
1103 if branch and branch != 'default':
1103 if branch and branch != 'default':
1104 write("# Branch %s\n" % branch)
1104 write("# Branch %s\n" % branch)
1105 write("# Node ID %s\n" % hex(node))
1105 write("# Node ID %s\n" % hex(node))
1106 write("# Parent %s\n" % hex(prev))
1106 write("# Parent %s\n" % hex(prev))
1107 if len(parents) > 1:
1107 if len(parents) > 1:
1108 write("# Parent %s\n" % hex(parents[1]))
1108 write("# Parent %s\n" % hex(parents[1]))
1109
1109
1110 for headerid in extraexport:
1110 for headerid in extraexport:
1111 header = extraexportmap[headerid](seqno, ctx)
1111 header = extraexportmap[headerid](seqno, ctx)
1112 if header is not None:
1112 if header is not None:
1113 write('# %s\n' % header)
1113 write('# %s\n' % header)
1114 write(ctx.description().rstrip())
1114 write(ctx.description().rstrip())
1115 write("\n\n")
1115 write("\n\n")
1116
1116
1117 for chunk, label in patch.diffui(repo, prev, node, match, opts=opts):
1117 for chunk, label in patch.diffui(repo, prev, node, match, opts=opts):
1118 write(chunk, label=label)
1118 write(chunk, label=label)
1119
1119
1120 if shouldclose:
1120 if shouldclose:
1121 fp.close()
1121 fp.close()
1122
1122
1123 for seqno, rev in enumerate(revs):
1123 for seqno, rev in enumerate(revs):
1124 single(rev, seqno + 1, fp)
1124 single(rev, seqno + 1, fp)
1125
1125
1126 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1126 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1127 changes=None, stat=False, fp=None, prefix='',
1127 changes=None, stat=False, fp=None, prefix='',
1128 root='', listsubrepos=False):
1128 root='', listsubrepos=False):
1129 '''show diff or diffstat.'''
1129 '''show diff or diffstat.'''
1130 if fp is None:
1130 if fp is None:
1131 write = ui.write
1131 write = ui.write
1132 else:
1132 else:
1133 def write(s, **kw):
1133 def write(s, **kw):
1134 fp.write(s)
1134 fp.write(s)
1135
1135
1136 if root:
1136 if root:
1137 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1137 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1138 else:
1138 else:
1139 relroot = ''
1139 relroot = ''
1140 if relroot != '':
1140 if relroot != '':
1141 # XXX relative roots currently don't work if the root is within a
1141 # XXX relative roots currently don't work if the root is within a
1142 # subrepo
1142 # subrepo
1143 uirelroot = match.uipath(relroot)
1143 uirelroot = match.uipath(relroot)
1144 relroot += '/'
1144 relroot += '/'
1145 for matchroot in match.files():
1145 for matchroot in match.files():
1146 if not matchroot.startswith(relroot):
1146 if not matchroot.startswith(relroot):
1147 ui.warn(_('warning: %s not inside relative root %s\n') % (
1147 ui.warn(_('warning: %s not inside relative root %s\n') % (
1148 match.uipath(matchroot), uirelroot))
1148 match.uipath(matchroot), uirelroot))
1149
1149
1150 if stat:
1150 if stat:
1151 diffopts = diffopts.copy(context=0)
1151 diffopts = diffopts.copy(context=0)
1152 width = 80
1152 width = 80
1153 if not ui.plain():
1153 if not ui.plain():
1154 width = ui.termwidth()
1154 width = ui.termwidth()
1155 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1155 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1156 prefix=prefix, relroot=relroot)
1156 prefix=prefix, relroot=relroot)
1157 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1157 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1158 width=width,
1158 width=width,
1159 git=diffopts.git):
1159 git=diffopts.git):
1160 write(chunk, label=label)
1160 write(chunk, label=label)
1161 else:
1161 else:
1162 for chunk, label in patch.diffui(repo, node1, node2, match,
1162 for chunk, label in patch.diffui(repo, node1, node2, match,
1163 changes, diffopts, prefix=prefix,
1163 changes, diffopts, prefix=prefix,
1164 relroot=relroot):
1164 relroot=relroot):
1165 write(chunk, label=label)
1165 write(chunk, label=label)
1166
1166
1167 if listsubrepos:
1167 if listsubrepos:
1168 ctx1 = repo[node1]
1168 ctx1 = repo[node1]
1169 ctx2 = repo[node2]
1169 ctx2 = repo[node2]
1170 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1170 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1171 tempnode2 = node2
1171 tempnode2 = node2
1172 try:
1172 try:
1173 if node2 is not None:
1173 if node2 is not None:
1174 tempnode2 = ctx2.substate[subpath][1]
1174 tempnode2 = ctx2.substate[subpath][1]
1175 except KeyError:
1175 except KeyError:
1176 # A subrepo that existed in node1 was deleted between node1 and
1176 # A subrepo that existed in node1 was deleted between node1 and
1177 # node2 (inclusive). Thus, ctx2's substate won't contain that
1177 # node2 (inclusive). Thus, ctx2's substate won't contain that
1178 # subpath. The best we can do is to ignore it.
1178 # subpath. The best we can do is to ignore it.
1179 tempnode2 = None
1179 tempnode2 = None
1180 submatch = matchmod.subdirmatcher(subpath, match)
1180 submatch = matchmod.subdirmatcher(subpath, match)
1181 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1181 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1182 stat=stat, fp=fp, prefix=prefix)
1182 stat=stat, fp=fp, prefix=prefix)
1183
1183
1184 class changeset_printer(object):
1184 class changeset_printer(object):
1185 '''show changeset information when templating not requested.'''
1185 '''show changeset information when templating not requested.'''
1186
1186
1187 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1187 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1188 self.ui = ui
1188 self.ui = ui
1189 self.repo = repo
1189 self.repo = repo
1190 self.buffered = buffered
1190 self.buffered = buffered
1191 self.matchfn = matchfn
1191 self.matchfn = matchfn
1192 self.diffopts = diffopts
1192 self.diffopts = diffopts
1193 self.header = {}
1193 self.header = {}
1194 self.hunk = {}
1194 self.hunk = {}
1195 self.lastheader = None
1195 self.lastheader = None
1196 self.footer = None
1196 self.footer = None
1197
1197
1198 def flush(self, ctx):
1198 def flush(self, ctx):
1199 rev = ctx.rev()
1199 rev = ctx.rev()
1200 if rev in self.header:
1200 if rev in self.header:
1201 h = self.header[rev]
1201 h = self.header[rev]
1202 if h != self.lastheader:
1202 if h != self.lastheader:
1203 self.lastheader = h
1203 self.lastheader = h
1204 self.ui.write(h)
1204 self.ui.write(h)
1205 del self.header[rev]
1205 del self.header[rev]
1206 if rev in self.hunk:
1206 if rev in self.hunk:
1207 self.ui.write(self.hunk[rev])
1207 self.ui.write(self.hunk[rev])
1208 del self.hunk[rev]
1208 del self.hunk[rev]
1209 return 1
1209 return 1
1210 return 0
1210 return 0
1211
1211
1212 def close(self):
1212 def close(self):
1213 if self.footer:
1213 if self.footer:
1214 self.ui.write(self.footer)
1214 self.ui.write(self.footer)
1215
1215
1216 def show(self, ctx, copies=None, matchfn=None, **props):
1216 def show(self, ctx, copies=None, matchfn=None, **props):
1217 if self.buffered:
1217 if self.buffered:
1218 self.ui.pushbuffer(labeled=True)
1218 self.ui.pushbuffer(labeled=True)
1219 self._show(ctx, copies, matchfn, props)
1219 self._show(ctx, copies, matchfn, props)
1220 self.hunk[ctx.rev()] = self.ui.popbuffer()
1220 self.hunk[ctx.rev()] = self.ui.popbuffer()
1221 else:
1221 else:
1222 self._show(ctx, copies, matchfn, props)
1222 self._show(ctx, copies, matchfn, props)
1223
1223
1224 def _show(self, ctx, copies, matchfn, props):
1224 def _show(self, ctx, copies, matchfn, props):
1225 '''show a single changeset or file revision'''
1225 '''show a single changeset or file revision'''
1226 changenode = ctx.node()
1226 changenode = ctx.node()
1227 rev = ctx.rev()
1227 rev = ctx.rev()
1228 if self.ui.debugflag:
1228 if self.ui.debugflag:
1229 hexfunc = hex
1229 hexfunc = hex
1230 else:
1230 else:
1231 hexfunc = short
1231 hexfunc = short
1232 # as of now, wctx.node() and wctx.rev() return None, but we want to
1232 # as of now, wctx.node() and wctx.rev() return None, but we want to
1233 # show the same values as {node} and {rev} templatekw
1233 # show the same values as {node} and {rev} templatekw
1234 revnode = (scmutil.intrev(rev), hexfunc(bin(ctx.hex())))
1234 revnode = (scmutil.intrev(rev), hexfunc(bin(ctx.hex())))
1235
1235
1236 if self.ui.quiet:
1236 if self.ui.quiet:
1237 self.ui.write("%d:%s\n" % revnode, label='log.node')
1237 self.ui.write("%d:%s\n" % revnode, label='log.node')
1238 return
1238 return
1239
1239
1240 date = util.datestr(ctx.date())
1240 date = util.datestr(ctx.date())
1241
1241
1242 # i18n: column positioning for "hg log"
1242 # i18n: column positioning for "hg log"
1243 self.ui.write(_("changeset: %d:%s\n") % revnode,
1243 self.ui.write(_("changeset: %d:%s\n") % revnode,
1244 label='log.changeset changeset.%s' % ctx.phasestr())
1244 label='log.changeset changeset.%s' % ctx.phasestr())
1245
1245
1246 # branches are shown first before any other names due to backwards
1246 # branches are shown first before any other names due to backwards
1247 # compatibility
1247 # compatibility
1248 branch = ctx.branch()
1248 branch = ctx.branch()
1249 # don't show the default branch name
1249 # don't show the default branch name
1250 if branch != 'default':
1250 if branch != 'default':
1251 # i18n: column positioning for "hg log"
1251 # i18n: column positioning for "hg log"
1252 self.ui.write(_("branch: %s\n") % branch,
1252 self.ui.write(_("branch: %s\n") % branch,
1253 label='log.branch')
1253 label='log.branch')
1254
1254
1255 for name, ns in self.repo.names.iteritems():
1255 for name, ns in self.repo.names.iteritems():
1256 # branches has special logic already handled above, so here we just
1256 # branches has special logic already handled above, so here we just
1257 # skip it
1257 # skip it
1258 if name == 'branches':
1258 if name == 'branches':
1259 continue
1259 continue
1260 # we will use the templatename as the color name since those two
1260 # we will use the templatename as the color name since those two
1261 # should be the same
1261 # should be the same
1262 for name in ns.names(self.repo, changenode):
1262 for name in ns.names(self.repo, changenode):
1263 self.ui.write(ns.logfmt % name,
1263 self.ui.write(ns.logfmt % name,
1264 label='log.%s' % ns.colorname)
1264 label='log.%s' % ns.colorname)
1265 if self.ui.debugflag:
1265 if self.ui.debugflag:
1266 # i18n: column positioning for "hg log"
1266 # i18n: column positioning for "hg log"
1267 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1267 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1268 label='log.phase')
1268 label='log.phase')
1269 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1269 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1270 label = 'log.parent changeset.%s' % pctx.phasestr()
1270 label = 'log.parent changeset.%s' % pctx.phasestr()
1271 # i18n: column positioning for "hg log"
1271 # i18n: column positioning for "hg log"
1272 self.ui.write(_("parent: %d:%s\n")
1272 self.ui.write(_("parent: %d:%s\n")
1273 % (pctx.rev(), hexfunc(pctx.node())),
1273 % (pctx.rev(), hexfunc(pctx.node())),
1274 label=label)
1274 label=label)
1275
1275
1276 if self.ui.debugflag and rev is not None:
1276 if self.ui.debugflag and rev is not None:
1277 mnode = ctx.manifestnode()
1277 mnode = ctx.manifestnode()
1278 # i18n: column positioning for "hg log"
1278 # i18n: column positioning for "hg log"
1279 self.ui.write(_("manifest: %d:%s\n") %
1279 self.ui.write(_("manifest: %d:%s\n") %
1280 (self.repo.manifest.rev(mnode), hex(mnode)),
1280 (self.repo.manifest.rev(mnode), hex(mnode)),
1281 label='ui.debug log.manifest')
1281 label='ui.debug log.manifest')
1282 # i18n: column positioning for "hg log"
1282 # i18n: column positioning for "hg log"
1283 self.ui.write(_("user: %s\n") % ctx.user(),
1283 self.ui.write(_("user: %s\n") % ctx.user(),
1284 label='log.user')
1284 label='log.user')
1285 # i18n: column positioning for "hg log"
1285 # i18n: column positioning for "hg log"
1286 self.ui.write(_("date: %s\n") % date,
1286 self.ui.write(_("date: %s\n") % date,
1287 label='log.date')
1287 label='log.date')
1288
1288
1289 if self.ui.debugflag:
1289 if self.ui.debugflag:
1290 files = ctx.p1().status(ctx)[:3]
1290 files = ctx.p1().status(ctx)[:3]
1291 for key, value in zip([# i18n: column positioning for "hg log"
1291 for key, value in zip([# i18n: column positioning for "hg log"
1292 _("files:"),
1292 _("files:"),
1293 # i18n: column positioning for "hg log"
1293 # i18n: column positioning for "hg log"
1294 _("files+:"),
1294 _("files+:"),
1295 # i18n: column positioning for "hg log"
1295 # i18n: column positioning for "hg log"
1296 _("files-:")], files):
1296 _("files-:")], files):
1297 if value:
1297 if value:
1298 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1298 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1299 label='ui.debug log.files')
1299 label='ui.debug log.files')
1300 elif ctx.files() and self.ui.verbose:
1300 elif ctx.files() and self.ui.verbose:
1301 # i18n: column positioning for "hg log"
1301 # i18n: column positioning for "hg log"
1302 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1302 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1303 label='ui.note log.files')
1303 label='ui.note log.files')
1304 if copies and self.ui.verbose:
1304 if copies and self.ui.verbose:
1305 copies = ['%s (%s)' % c for c in copies]
1305 copies = ['%s (%s)' % c for c in copies]
1306 # i18n: column positioning for "hg log"
1306 # i18n: column positioning for "hg log"
1307 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1307 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1308 label='ui.note log.copies')
1308 label='ui.note log.copies')
1309
1309
1310 extra = ctx.extra()
1310 extra = ctx.extra()
1311 if extra and self.ui.debugflag:
1311 if extra and self.ui.debugflag:
1312 for key, value in sorted(extra.items()):
1312 for key, value in sorted(extra.items()):
1313 # i18n: column positioning for "hg log"
1313 # i18n: column positioning for "hg log"
1314 self.ui.write(_("extra: %s=%s\n")
1314 self.ui.write(_("extra: %s=%s\n")
1315 % (key, value.encode('string_escape')),
1315 % (key, value.encode('string_escape')),
1316 label='ui.debug log.extra')
1316 label='ui.debug log.extra')
1317
1317
1318 description = ctx.description().strip()
1318 description = ctx.description().strip()
1319 if description:
1319 if description:
1320 if self.ui.verbose:
1320 if self.ui.verbose:
1321 self.ui.write(_("description:\n"),
1321 self.ui.write(_("description:\n"),
1322 label='ui.note log.description')
1322 label='ui.note log.description')
1323 self.ui.write(description,
1323 self.ui.write(description,
1324 label='ui.note log.description')
1324 label='ui.note log.description')
1325 self.ui.write("\n\n")
1325 self.ui.write("\n\n")
1326 else:
1326 else:
1327 # i18n: column positioning for "hg log"
1327 # i18n: column positioning for "hg log"
1328 self.ui.write(_("summary: %s\n") %
1328 self.ui.write(_("summary: %s\n") %
1329 description.splitlines()[0],
1329 description.splitlines()[0],
1330 label='log.summary')
1330 label='log.summary')
1331 self.ui.write("\n")
1331 self.ui.write("\n")
1332
1332
1333 self.showpatch(ctx, matchfn)
1333 self.showpatch(ctx, matchfn)
1334
1334
1335 def showpatch(self, ctx, matchfn):
1335 def showpatch(self, ctx, matchfn):
1336 if not matchfn:
1336 if not matchfn:
1337 matchfn = self.matchfn
1337 matchfn = self.matchfn
1338 if matchfn:
1338 if matchfn:
1339 stat = self.diffopts.get('stat')
1339 stat = self.diffopts.get('stat')
1340 diff = self.diffopts.get('patch')
1340 diff = self.diffopts.get('patch')
1341 diffopts = patch.diffallopts(self.ui, self.diffopts)
1341 diffopts = patch.diffallopts(self.ui, self.diffopts)
1342 node = ctx.node()
1342 node = ctx.node()
1343 prev = ctx.p1().node()
1343 prev = ctx.p1().node()
1344 if stat:
1344 if stat:
1345 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1345 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1346 match=matchfn, stat=True)
1346 match=matchfn, stat=True)
1347 if diff:
1347 if diff:
1348 if stat:
1348 if stat:
1349 self.ui.write("\n")
1349 self.ui.write("\n")
1350 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1350 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1351 match=matchfn, stat=False)
1351 match=matchfn, stat=False)
1352 self.ui.write("\n")
1352 self.ui.write("\n")
1353
1353
1354 class jsonchangeset(changeset_printer):
1354 class jsonchangeset(changeset_printer):
1355 '''format changeset information.'''
1355 '''format changeset information.'''
1356
1356
1357 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1357 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1358 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1358 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1359 self.cache = {}
1359 self.cache = {}
1360 self._first = True
1360 self._first = True
1361
1361
1362 def close(self):
1362 def close(self):
1363 if not self._first:
1363 if not self._first:
1364 self.ui.write("\n]\n")
1364 self.ui.write("\n]\n")
1365 else:
1365 else:
1366 self.ui.write("[]\n")
1366 self.ui.write("[]\n")
1367
1367
1368 def _show(self, ctx, copies, matchfn, props):
1368 def _show(self, ctx, copies, matchfn, props):
1369 '''show a single changeset or file revision'''
1369 '''show a single changeset or file revision'''
1370 rev = ctx.rev()
1370 rev = ctx.rev()
1371 if rev is None:
1371 if rev is None:
1372 jrev = jnode = 'null'
1372 jrev = jnode = 'null'
1373 else:
1373 else:
1374 jrev = str(rev)
1374 jrev = str(rev)
1375 jnode = '"%s"' % hex(ctx.node())
1375 jnode = '"%s"' % hex(ctx.node())
1376 j = encoding.jsonescape
1376 j = encoding.jsonescape
1377
1377
1378 if self._first:
1378 if self._first:
1379 self.ui.write("[\n {")
1379 self.ui.write("[\n {")
1380 self._first = False
1380 self._first = False
1381 else:
1381 else:
1382 self.ui.write(",\n {")
1382 self.ui.write(",\n {")
1383
1383
1384 if self.ui.quiet:
1384 if self.ui.quiet:
1385 self.ui.write('\n "rev": %s' % jrev)
1385 self.ui.write('\n "rev": %s' % jrev)
1386 self.ui.write(',\n "node": %s' % jnode)
1386 self.ui.write(',\n "node": %s' % jnode)
1387 self.ui.write('\n }')
1387 self.ui.write('\n }')
1388 return
1388 return
1389
1389
1390 self.ui.write('\n "rev": %s' % jrev)
1390 self.ui.write('\n "rev": %s' % jrev)
1391 self.ui.write(',\n "node": %s' % jnode)
1391 self.ui.write(',\n "node": %s' % jnode)
1392 self.ui.write(',\n "branch": "%s"' % j(ctx.branch()))
1392 self.ui.write(',\n "branch": "%s"' % j(ctx.branch()))
1393 self.ui.write(',\n "phase": "%s"' % ctx.phasestr())
1393 self.ui.write(',\n "phase": "%s"' % ctx.phasestr())
1394 self.ui.write(',\n "user": "%s"' % j(ctx.user()))
1394 self.ui.write(',\n "user": "%s"' % j(ctx.user()))
1395 self.ui.write(',\n "date": [%d, %d]' % ctx.date())
1395 self.ui.write(',\n "date": [%d, %d]' % ctx.date())
1396 self.ui.write(',\n "desc": "%s"' % j(ctx.description()))
1396 self.ui.write(',\n "desc": "%s"' % j(ctx.description()))
1397
1397
1398 self.ui.write(',\n "bookmarks": [%s]' %
1398 self.ui.write(',\n "bookmarks": [%s]' %
1399 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1399 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1400 self.ui.write(',\n "tags": [%s]' %
1400 self.ui.write(',\n "tags": [%s]' %
1401 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1401 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1402 self.ui.write(',\n "parents": [%s]' %
1402 self.ui.write(',\n "parents": [%s]' %
1403 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1403 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1404
1404
1405 if self.ui.debugflag:
1405 if self.ui.debugflag:
1406 if rev is None:
1406 if rev is None:
1407 jmanifestnode = 'null'
1407 jmanifestnode = 'null'
1408 else:
1408 else:
1409 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1409 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1410 self.ui.write(',\n "manifest": %s' % jmanifestnode)
1410 self.ui.write(',\n "manifest": %s' % jmanifestnode)
1411
1411
1412 self.ui.write(',\n "extra": {%s}' %
1412 self.ui.write(',\n "extra": {%s}' %
1413 ", ".join('"%s": "%s"' % (j(k), j(v))
1413 ", ".join('"%s": "%s"' % (j(k), j(v))
1414 for k, v in ctx.extra().items()))
1414 for k, v in ctx.extra().items()))
1415
1415
1416 files = ctx.p1().status(ctx)
1416 files = ctx.p1().status(ctx)
1417 self.ui.write(',\n "modified": [%s]' %
1417 self.ui.write(',\n "modified": [%s]' %
1418 ", ".join('"%s"' % j(f) for f in files[0]))
1418 ", ".join('"%s"' % j(f) for f in files[0]))
1419 self.ui.write(',\n "added": [%s]' %
1419 self.ui.write(',\n "added": [%s]' %
1420 ", ".join('"%s"' % j(f) for f in files[1]))
1420 ", ".join('"%s"' % j(f) for f in files[1]))
1421 self.ui.write(',\n "removed": [%s]' %
1421 self.ui.write(',\n "removed": [%s]' %
1422 ", ".join('"%s"' % j(f) for f in files[2]))
1422 ", ".join('"%s"' % j(f) for f in files[2]))
1423
1423
1424 elif self.ui.verbose:
1424 elif self.ui.verbose:
1425 self.ui.write(',\n "files": [%s]' %
1425 self.ui.write(',\n "files": [%s]' %
1426 ", ".join('"%s"' % j(f) for f in ctx.files()))
1426 ", ".join('"%s"' % j(f) for f in ctx.files()))
1427
1427
1428 if copies:
1428 if copies:
1429 self.ui.write(',\n "copies": {%s}' %
1429 self.ui.write(',\n "copies": {%s}' %
1430 ", ".join('"%s": "%s"' % (j(k), j(v))
1430 ", ".join('"%s": "%s"' % (j(k), j(v))
1431 for k, v in copies))
1431 for k, v in copies))
1432
1432
1433 matchfn = self.matchfn
1433 matchfn = self.matchfn
1434 if matchfn:
1434 if matchfn:
1435 stat = self.diffopts.get('stat')
1435 stat = self.diffopts.get('stat')
1436 diff = self.diffopts.get('patch')
1436 diff = self.diffopts.get('patch')
1437 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1437 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1438 node, prev = ctx.node(), ctx.p1().node()
1438 node, prev = ctx.node(), ctx.p1().node()
1439 if stat:
1439 if stat:
1440 self.ui.pushbuffer()
1440 self.ui.pushbuffer()
1441 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1441 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1442 match=matchfn, stat=True)
1442 match=matchfn, stat=True)
1443 self.ui.write(',\n "diffstat": "%s"' % j(self.ui.popbuffer()))
1443 self.ui.write(',\n "diffstat": "%s"' % j(self.ui.popbuffer()))
1444 if diff:
1444 if diff:
1445 self.ui.pushbuffer()
1445 self.ui.pushbuffer()
1446 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1446 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1447 match=matchfn, stat=False)
1447 match=matchfn, stat=False)
1448 self.ui.write(',\n "diff": "%s"' % j(self.ui.popbuffer()))
1448 self.ui.write(',\n "diff": "%s"' % j(self.ui.popbuffer()))
1449
1449
1450 self.ui.write("\n }")
1450 self.ui.write("\n }")
1451
1451
1452 class changeset_templater(changeset_printer):
1452 class changeset_templater(changeset_printer):
1453 '''format changeset information.'''
1453 '''format changeset information.'''
1454
1454
1455 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1455 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1456 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1456 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1457 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1457 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1458 defaulttempl = {
1458 defaulttempl = {
1459 'parent': '{rev}:{node|formatnode} ',
1459 'parent': '{rev}:{node|formatnode} ',
1460 'manifest': '{rev}:{node|formatnode}',
1460 'manifest': '{rev}:{node|formatnode}',
1461 'file_copy': '{name} ({source})',
1461 'file_copy': '{name} ({source})',
1462 'extra': '{key}={value|stringescape}'
1462 'extra': '{key}={value|stringescape}'
1463 }
1463 }
1464 # filecopy is preserved for compatibility reasons
1464 # filecopy is preserved for compatibility reasons
1465 defaulttempl['filecopy'] = defaulttempl['file_copy']
1465 defaulttempl['filecopy'] = defaulttempl['file_copy']
1466 self.t = templater.templater(mapfile, {'formatnode': formatnode},
1466 self.t = templater.templater(mapfile, {'formatnode': formatnode},
1467 cache=defaulttempl)
1467 cache=defaulttempl)
1468 if tmpl:
1468 if tmpl:
1469 self.t.cache['changeset'] = tmpl
1469 self.t.cache['changeset'] = tmpl
1470
1470
1471 self.cache = {}
1471 self.cache = {}
1472
1472
1473 # find correct templates for current mode
1473 # find correct templates for current mode
1474 tmplmodes = [
1474 tmplmodes = [
1475 (True, None),
1475 (True, None),
1476 (self.ui.verbose, 'verbose'),
1476 (self.ui.verbose, 'verbose'),
1477 (self.ui.quiet, 'quiet'),
1477 (self.ui.quiet, 'quiet'),
1478 (self.ui.debugflag, 'debug'),
1478 (self.ui.debugflag, 'debug'),
1479 ]
1479 ]
1480
1480
1481 self._parts = {'header': '', 'footer': '', 'changeset': 'changeset',
1481 self._parts = {'header': '', 'footer': '', 'changeset': 'changeset',
1482 'docheader': '', 'docfooter': ''}
1482 'docheader': '', 'docfooter': ''}
1483 for mode, postfix in tmplmodes:
1483 for mode, postfix in tmplmodes:
1484 for t in self._parts:
1484 for t in self._parts:
1485 cur = t
1485 cur = t
1486 if postfix:
1486 if postfix:
1487 cur += "_" + postfix
1487 cur += "_" + postfix
1488 if mode and cur in self.t:
1488 if mode and cur in self.t:
1489 self._parts[t] = cur
1489 self._parts[t] = cur
1490
1490
1491 if self._parts['docheader']:
1491 if self._parts['docheader']:
1492 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1492 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1493
1493
1494 def close(self):
1494 def close(self):
1495 if self._parts['docfooter']:
1495 if self._parts['docfooter']:
1496 if not self.footer:
1496 if not self.footer:
1497 self.footer = ""
1497 self.footer = ""
1498 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1498 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1499 return super(changeset_templater, self).close()
1499 return super(changeset_templater, self).close()
1500
1500
1501 def _show(self, ctx, copies, matchfn, props):
1501 def _show(self, ctx, copies, matchfn, props):
1502 '''show a single changeset or file revision'''
1502 '''show a single changeset or file revision'''
1503 props = props.copy()
1503 props = props.copy()
1504 props.update(templatekw.keywords)
1504 props.update(templatekw.keywords)
1505 props['templ'] = self.t
1505 props['templ'] = self.t
1506 props['ctx'] = ctx
1506 props['ctx'] = ctx
1507 props['repo'] = self.repo
1507 props['repo'] = self.repo
1508 props['ui'] = self.repo.ui
1508 props['ui'] = self.repo.ui
1509 props['revcache'] = {'copies': copies}
1509 props['revcache'] = {'copies': copies}
1510 props['cache'] = self.cache
1510 props['cache'] = self.cache
1511
1511
1512 try:
1512 try:
1513 # write header
1513 # write header
1514 if self._parts['header']:
1514 if self._parts['header']:
1515 h = templater.stringify(self.t(self._parts['header'], **props))
1515 h = templater.stringify(self.t(self._parts['header'], **props))
1516 if self.buffered:
1516 if self.buffered:
1517 self.header[ctx.rev()] = h
1517 self.header[ctx.rev()] = h
1518 else:
1518 else:
1519 if self.lastheader != h:
1519 if self.lastheader != h:
1520 self.lastheader = h
1520 self.lastheader = h
1521 self.ui.write(h)
1521 self.ui.write(h)
1522
1522
1523 # write changeset metadata, then patch if requested
1523 # write changeset metadata, then patch if requested
1524 key = self._parts['changeset']
1524 key = self._parts['changeset']
1525 self.ui.write(templater.stringify(self.t(key, **props)))
1525 self.ui.write(templater.stringify(self.t(key, **props)))
1526 self.showpatch(ctx, matchfn)
1526 self.showpatch(ctx, matchfn)
1527
1527
1528 if self._parts['footer']:
1528 if self._parts['footer']:
1529 if not self.footer:
1529 if not self.footer:
1530 self.footer = templater.stringify(
1530 self.footer = templater.stringify(
1531 self.t(self._parts['footer'], **props))
1531 self.t(self._parts['footer'], **props))
1532 except KeyError as inst:
1532 except KeyError as inst:
1533 msg = _("%s: no key named '%s'")
1533 msg = _("%s: no key named '%s'")
1534 raise error.Abort(msg % (self.t.mapfile, inst.args[0]))
1534 raise error.Abort(msg % (self.t.mapfile, inst.args[0]))
1535 except SyntaxError as inst:
1535 except SyntaxError as inst:
1536 raise error.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
1536 raise error.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
1537
1537
1538 def gettemplate(ui, tmpl, style):
1538 def gettemplate(ui, tmpl, style):
1539 """
1539 """
1540 Find the template matching the given template spec or style.
1540 Find the template matching the given template spec or style.
1541 """
1541 """
1542
1542
1543 # ui settings
1543 # ui settings
1544 if not tmpl and not style: # template are stronger than style
1544 if not tmpl and not style: # template are stronger than style
1545 tmpl = ui.config('ui', 'logtemplate')
1545 tmpl = ui.config('ui', 'logtemplate')
1546 if tmpl:
1546 if tmpl:
1547 try:
1547 try:
1548 tmpl = templater.unquotestring(tmpl)
1548 tmpl = templater.unquotestring(tmpl)
1549 except SyntaxError:
1549 except SyntaxError:
1550 pass
1550 pass
1551 return tmpl, None
1551 return tmpl, None
1552 else:
1552 else:
1553 style = util.expandpath(ui.config('ui', 'style', ''))
1553 style = util.expandpath(ui.config('ui', 'style', ''))
1554
1554
1555 if not tmpl and style:
1555 if not tmpl and style:
1556 mapfile = style
1556 mapfile = style
1557 if not os.path.split(mapfile)[0]:
1557 if not os.path.split(mapfile)[0]:
1558 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1558 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1559 or templater.templatepath(mapfile))
1559 or templater.templatepath(mapfile))
1560 if mapname:
1560 if mapname:
1561 mapfile = mapname
1561 mapfile = mapname
1562 return None, mapfile
1562 return None, mapfile
1563
1563
1564 if not tmpl:
1564 if not tmpl:
1565 return None, None
1565 return None, None
1566
1566
1567 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1567 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1568
1568
1569 def show_changeset(ui, repo, opts, buffered=False):
1569 def show_changeset(ui, repo, opts, buffered=False):
1570 """show one changeset using template or regular display.
1570 """show one changeset using template or regular display.
1571
1571
1572 Display format will be the first non-empty hit of:
1572 Display format will be the first non-empty hit of:
1573 1. option 'template'
1573 1. option 'template'
1574 2. option 'style'
1574 2. option 'style'
1575 3. [ui] setting 'logtemplate'
1575 3. [ui] setting 'logtemplate'
1576 4. [ui] setting 'style'
1576 4. [ui] setting 'style'
1577 If all of these values are either the unset or the empty string,
1577 If all of these values are either the unset or the empty string,
1578 regular display via changeset_printer() is done.
1578 regular display via changeset_printer() is done.
1579 """
1579 """
1580 # options
1580 # options
1581 matchfn = None
1581 matchfn = None
1582 if opts.get('patch') or opts.get('stat'):
1582 if opts.get('patch') or opts.get('stat'):
1583 matchfn = scmutil.matchall(repo)
1583 matchfn = scmutil.matchall(repo)
1584
1584
1585 if opts.get('template') == 'json':
1585 if opts.get('template') == 'json':
1586 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1586 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1587
1587
1588 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1588 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1589
1589
1590 if not tmpl and not mapfile:
1590 if not tmpl and not mapfile:
1591 return changeset_printer(ui, repo, matchfn, opts, buffered)
1591 return changeset_printer(ui, repo, matchfn, opts, buffered)
1592
1592
1593 try:
1593 try:
1594 t = changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile,
1594 t = changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile,
1595 buffered)
1595 buffered)
1596 except SyntaxError as inst:
1596 except SyntaxError as inst:
1597 raise error.Abort(inst.args[0])
1597 raise error.Abort(inst.args[0])
1598 return t
1598 return t
1599
1599
1600 def showmarker(ui, marker):
1600 def showmarker(ui, marker):
1601 """utility function to display obsolescence marker in a readable way
1601 """utility function to display obsolescence marker in a readable way
1602
1602
1603 To be used by debug function."""
1603 To be used by debug function."""
1604 ui.write(hex(marker.precnode()))
1604 ui.write(hex(marker.precnode()))
1605 for repl in marker.succnodes():
1605 for repl in marker.succnodes():
1606 ui.write(' ')
1606 ui.write(' ')
1607 ui.write(hex(repl))
1607 ui.write(hex(repl))
1608 ui.write(' %X ' % marker.flags())
1608 ui.write(' %X ' % marker.flags())
1609 parents = marker.parentnodes()
1609 parents = marker.parentnodes()
1610 if parents is not None:
1610 if parents is not None:
1611 ui.write('{%s} ' % ', '.join(hex(p) for p in parents))
1611 ui.write('{%s} ' % ', '.join(hex(p) for p in parents))
1612 ui.write('(%s) ' % util.datestr(marker.date()))
1612 ui.write('(%s) ' % util.datestr(marker.date()))
1613 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1613 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1614 sorted(marker.metadata().items())
1614 sorted(marker.metadata().items())
1615 if t[0] != 'date')))
1615 if t[0] != 'date')))
1616 ui.write('\n')
1616 ui.write('\n')
1617
1617
1618 def finddate(ui, repo, date):
1618 def finddate(ui, repo, date):
1619 """Find the tipmost changeset that matches the given date spec"""
1619 """Find the tipmost changeset that matches the given date spec"""
1620
1620
1621 df = util.matchdate(date)
1621 df = util.matchdate(date)
1622 m = scmutil.matchall(repo)
1622 m = scmutil.matchall(repo)
1623 results = {}
1623 results = {}
1624
1624
1625 def prep(ctx, fns):
1625 def prep(ctx, fns):
1626 d = ctx.date()
1626 d = ctx.date()
1627 if df(d[0]):
1627 if df(d[0]):
1628 results[ctx.rev()] = d
1628 results[ctx.rev()] = d
1629
1629
1630 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1630 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1631 rev = ctx.rev()
1631 rev = ctx.rev()
1632 if rev in results:
1632 if rev in results:
1633 ui.status(_("found revision %s from %s\n") %
1633 ui.status(_("found revision %s from %s\n") %
1634 (rev, util.datestr(results[rev])))
1634 (rev, util.datestr(results[rev])))
1635 return str(rev)
1635 return str(rev)
1636
1636
1637 raise error.Abort(_("revision matching date not found"))
1637 raise error.Abort(_("revision matching date not found"))
1638
1638
1639 def increasingwindows(windowsize=8, sizelimit=512):
1639 def increasingwindows(windowsize=8, sizelimit=512):
1640 while True:
1640 while True:
1641 yield windowsize
1641 yield windowsize
1642 if windowsize < sizelimit:
1642 if windowsize < sizelimit:
1643 windowsize *= 2
1643 windowsize *= 2
1644
1644
1645 class FileWalkError(Exception):
1645 class FileWalkError(Exception):
1646 pass
1646 pass
1647
1647
1648 def walkfilerevs(repo, match, follow, revs, fncache):
1648 def walkfilerevs(repo, match, follow, revs, fncache):
1649 '''Walks the file history for the matched files.
1649 '''Walks the file history for the matched files.
1650
1650
1651 Returns the changeset revs that are involved in the file history.
1651 Returns the changeset revs that are involved in the file history.
1652
1652
1653 Throws FileWalkError if the file history can't be walked using
1653 Throws FileWalkError if the file history can't be walked using
1654 filelogs alone.
1654 filelogs alone.
1655 '''
1655 '''
1656 wanted = set()
1656 wanted = set()
1657 copies = []
1657 copies = []
1658 minrev, maxrev = min(revs), max(revs)
1658 minrev, maxrev = min(revs), max(revs)
1659 def filerevgen(filelog, last):
1659 def filerevgen(filelog, last):
1660 """
1660 """
1661 Only files, no patterns. Check the history of each file.
1661 Only files, no patterns. Check the history of each file.
1662
1662
1663 Examines filelog entries within minrev, maxrev linkrev range
1663 Examines filelog entries within minrev, maxrev linkrev range
1664 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1664 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1665 tuples in backwards order
1665 tuples in backwards order
1666 """
1666 """
1667 cl_count = len(repo)
1667 cl_count = len(repo)
1668 revs = []
1668 revs = []
1669 for j in xrange(0, last + 1):
1669 for j in xrange(0, last + 1):
1670 linkrev = filelog.linkrev(j)
1670 linkrev = filelog.linkrev(j)
1671 if linkrev < minrev:
1671 if linkrev < minrev:
1672 continue
1672 continue
1673 # only yield rev for which we have the changelog, it can
1673 # only yield rev for which we have the changelog, it can
1674 # happen while doing "hg log" during a pull or commit
1674 # happen while doing "hg log" during a pull or commit
1675 if linkrev >= cl_count:
1675 if linkrev >= cl_count:
1676 break
1676 break
1677
1677
1678 parentlinkrevs = []
1678 parentlinkrevs = []
1679 for p in filelog.parentrevs(j):
1679 for p in filelog.parentrevs(j):
1680 if p != nullrev:
1680 if p != nullrev:
1681 parentlinkrevs.append(filelog.linkrev(p))
1681 parentlinkrevs.append(filelog.linkrev(p))
1682 n = filelog.node(j)
1682 n = filelog.node(j)
1683 revs.append((linkrev, parentlinkrevs,
1683 revs.append((linkrev, parentlinkrevs,
1684 follow and filelog.renamed(n)))
1684 follow and filelog.renamed(n)))
1685
1685
1686 return reversed(revs)
1686 return reversed(revs)
1687 def iterfiles():
1687 def iterfiles():
1688 pctx = repo['.']
1688 pctx = repo['.']
1689 for filename in match.files():
1689 for filename in match.files():
1690 if follow:
1690 if follow:
1691 if filename not in pctx:
1691 if filename not in pctx:
1692 raise error.Abort(_('cannot follow file not in parent '
1692 raise error.Abort(_('cannot follow file not in parent '
1693 'revision: "%s"') % filename)
1693 'revision: "%s"') % filename)
1694 yield filename, pctx[filename].filenode()
1694 yield filename, pctx[filename].filenode()
1695 else:
1695 else:
1696 yield filename, None
1696 yield filename, None
1697 for filename_node in copies:
1697 for filename_node in copies:
1698 yield filename_node
1698 yield filename_node
1699
1699
1700 for file_, node in iterfiles():
1700 for file_, node in iterfiles():
1701 filelog = repo.file(file_)
1701 filelog = repo.file(file_)
1702 if not len(filelog):
1702 if not len(filelog):
1703 if node is None:
1703 if node is None:
1704 # A zero count may be a directory or deleted file, so
1704 # A zero count may be a directory or deleted file, so
1705 # try to find matching entries on the slow path.
1705 # try to find matching entries on the slow path.
1706 if follow:
1706 if follow:
1707 raise error.Abort(
1707 raise error.Abort(
1708 _('cannot follow nonexistent file: "%s"') % file_)
1708 _('cannot follow nonexistent file: "%s"') % file_)
1709 raise FileWalkError("Cannot walk via filelog")
1709 raise FileWalkError("Cannot walk via filelog")
1710 else:
1710 else:
1711 continue
1711 continue
1712
1712
1713 if node is None:
1713 if node is None:
1714 last = len(filelog) - 1
1714 last = len(filelog) - 1
1715 else:
1715 else:
1716 last = filelog.rev(node)
1716 last = filelog.rev(node)
1717
1717
1718 # keep track of all ancestors of the file
1718 # keep track of all ancestors of the file
1719 ancestors = set([filelog.linkrev(last)])
1719 ancestors = set([filelog.linkrev(last)])
1720
1720
1721 # iterate from latest to oldest revision
1721 # iterate from latest to oldest revision
1722 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1722 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1723 if not follow:
1723 if not follow:
1724 if rev > maxrev:
1724 if rev > maxrev:
1725 continue
1725 continue
1726 else:
1726 else:
1727 # Note that last might not be the first interesting
1727 # Note that last might not be the first interesting
1728 # rev to us:
1728 # rev to us:
1729 # if the file has been changed after maxrev, we'll
1729 # if the file has been changed after maxrev, we'll
1730 # have linkrev(last) > maxrev, and we still need
1730 # have linkrev(last) > maxrev, and we still need
1731 # to explore the file graph
1731 # to explore the file graph
1732 if rev not in ancestors:
1732 if rev not in ancestors:
1733 continue
1733 continue
1734 # XXX insert 1327 fix here
1734 # XXX insert 1327 fix here
1735 if flparentlinkrevs:
1735 if flparentlinkrevs:
1736 ancestors.update(flparentlinkrevs)
1736 ancestors.update(flparentlinkrevs)
1737
1737
1738 fncache.setdefault(rev, []).append(file_)
1738 fncache.setdefault(rev, []).append(file_)
1739 wanted.add(rev)
1739 wanted.add(rev)
1740 if copied:
1740 if copied:
1741 copies.append(copied)
1741 copies.append(copied)
1742
1742
1743 return wanted
1743 return wanted
1744
1744
1745 class _followfilter(object):
1745 class _followfilter(object):
1746 def __init__(self, repo, onlyfirst=False):
1746 def __init__(self, repo, onlyfirst=False):
1747 self.repo = repo
1747 self.repo = repo
1748 self.startrev = nullrev
1748 self.startrev = nullrev
1749 self.roots = set()
1749 self.roots = set()
1750 self.onlyfirst = onlyfirst
1750 self.onlyfirst = onlyfirst
1751
1751
1752 def match(self, rev):
1752 def match(self, rev):
1753 def realparents(rev):
1753 def realparents(rev):
1754 if self.onlyfirst:
1754 if self.onlyfirst:
1755 return self.repo.changelog.parentrevs(rev)[0:1]
1755 return self.repo.changelog.parentrevs(rev)[0:1]
1756 else:
1756 else:
1757 return filter(lambda x: x != nullrev,
1757 return filter(lambda x: x != nullrev,
1758 self.repo.changelog.parentrevs(rev))
1758 self.repo.changelog.parentrevs(rev))
1759
1759
1760 if self.startrev == nullrev:
1760 if self.startrev == nullrev:
1761 self.startrev = rev
1761 self.startrev = rev
1762 return True
1762 return True
1763
1763
1764 if rev > self.startrev:
1764 if rev > self.startrev:
1765 # forward: all descendants
1765 # forward: all descendants
1766 if not self.roots:
1766 if not self.roots:
1767 self.roots.add(self.startrev)
1767 self.roots.add(self.startrev)
1768 for parent in realparents(rev):
1768 for parent in realparents(rev):
1769 if parent in self.roots:
1769 if parent in self.roots:
1770 self.roots.add(rev)
1770 self.roots.add(rev)
1771 return True
1771 return True
1772 else:
1772 else:
1773 # backwards: all parents
1773 # backwards: all parents
1774 if not self.roots:
1774 if not self.roots:
1775 self.roots.update(realparents(self.startrev))
1775 self.roots.update(realparents(self.startrev))
1776 if rev in self.roots:
1776 if rev in self.roots:
1777 self.roots.remove(rev)
1777 self.roots.remove(rev)
1778 self.roots.update(realparents(rev))
1778 self.roots.update(realparents(rev))
1779 return True
1779 return True
1780
1780
1781 return False
1781 return False
1782
1782
1783 def walkchangerevs(repo, match, opts, prepare):
1783 def walkchangerevs(repo, match, opts, prepare):
1784 '''Iterate over files and the revs in which they changed.
1784 '''Iterate over files and the revs in which they changed.
1785
1785
1786 Callers most commonly need to iterate backwards over the history
1786 Callers most commonly need to iterate backwards over the history
1787 in which they are interested. Doing so has awful (quadratic-looking)
1787 in which they are interested. Doing so has awful (quadratic-looking)
1788 performance, so we use iterators in a "windowed" way.
1788 performance, so we use iterators in a "windowed" way.
1789
1789
1790 We walk a window of revisions in the desired order. Within the
1790 We walk a window of revisions in the desired order. Within the
1791 window, we first walk forwards to gather data, then in the desired
1791 window, we first walk forwards to gather data, then in the desired
1792 order (usually backwards) to display it.
1792 order (usually backwards) to display it.
1793
1793
1794 This function returns an iterator yielding contexts. Before
1794 This function returns an iterator yielding contexts. Before
1795 yielding each context, the iterator will first call the prepare
1795 yielding each context, the iterator will first call the prepare
1796 function on each context in the window in forward order.'''
1796 function on each context in the window in forward order.'''
1797
1797
1798 follow = opts.get('follow') or opts.get('follow_first')
1798 follow = opts.get('follow') or opts.get('follow_first')
1799 revs = _logrevs(repo, opts)
1799 revs = _logrevs(repo, opts)
1800 if not revs:
1800 if not revs:
1801 return []
1801 return []
1802 wanted = set()
1802 wanted = set()
1803 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1803 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1804 opts.get('removed'))
1804 opts.get('removed'))
1805 fncache = {}
1805 fncache = {}
1806 change = repo.changectx
1806 change = repo.changectx
1807
1807
1808 # First step is to fill wanted, the set of revisions that we want to yield.
1808 # First step is to fill wanted, the set of revisions that we want to yield.
1809 # When it does not induce extra cost, we also fill fncache for revisions in
1809 # When it does not induce extra cost, we also fill fncache for revisions in
1810 # wanted: a cache of filenames that were changed (ctx.files()) and that
1810 # wanted: a cache of filenames that were changed (ctx.files()) and that
1811 # match the file filtering conditions.
1811 # match the file filtering conditions.
1812
1812
1813 if match.always():
1813 if match.always():
1814 # No files, no patterns. Display all revs.
1814 # No files, no patterns. Display all revs.
1815 wanted = revs
1815 wanted = revs
1816 elif not slowpath:
1816 elif not slowpath:
1817 # We only have to read through the filelog to find wanted revisions
1817 # We only have to read through the filelog to find wanted revisions
1818
1818
1819 try:
1819 try:
1820 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1820 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1821 except FileWalkError:
1821 except FileWalkError:
1822 slowpath = True
1822 slowpath = True
1823
1823
1824 # We decided to fall back to the slowpath because at least one
1824 # We decided to fall back to the slowpath because at least one
1825 # of the paths was not a file. Check to see if at least one of them
1825 # of the paths was not a file. Check to see if at least one of them
1826 # existed in history, otherwise simply return
1826 # existed in history, otherwise simply return
1827 for path in match.files():
1827 for path in match.files():
1828 if path == '.' or path in repo.store:
1828 if path == '.' or path in repo.store:
1829 break
1829 break
1830 else:
1830 else:
1831 return []
1831 return []
1832
1832
1833 if slowpath:
1833 if slowpath:
1834 # We have to read the changelog to match filenames against
1834 # We have to read the changelog to match filenames against
1835 # changed files
1835 # changed files
1836
1836
1837 if follow:
1837 if follow:
1838 raise error.Abort(_('can only follow copies/renames for explicit '
1838 raise error.Abort(_('can only follow copies/renames for explicit '
1839 'filenames'))
1839 'filenames'))
1840
1840
1841 # The slow path checks files modified in every changeset.
1841 # The slow path checks files modified in every changeset.
1842 # This is really slow on large repos, so compute the set lazily.
1842 # This is really slow on large repos, so compute the set lazily.
1843 class lazywantedset(object):
1843 class lazywantedset(object):
1844 def __init__(self):
1844 def __init__(self):
1845 self.set = set()
1845 self.set = set()
1846 self.revs = set(revs)
1846 self.revs = set(revs)
1847
1847
1848 # No need to worry about locality here because it will be accessed
1848 # No need to worry about locality here because it will be accessed
1849 # in the same order as the increasing window below.
1849 # in the same order as the increasing window below.
1850 def __contains__(self, value):
1850 def __contains__(self, value):
1851 if value in self.set:
1851 if value in self.set:
1852 return True
1852 return True
1853 elif not value in self.revs:
1853 elif not value in self.revs:
1854 return False
1854 return False
1855 else:
1855 else:
1856 self.revs.discard(value)
1856 self.revs.discard(value)
1857 ctx = change(value)
1857 ctx = change(value)
1858 matches = filter(match, ctx.files())
1858 matches = filter(match, ctx.files())
1859 if matches:
1859 if matches:
1860 fncache[value] = matches
1860 fncache[value] = matches
1861 self.set.add(value)
1861 self.set.add(value)
1862 return True
1862 return True
1863 return False
1863 return False
1864
1864
1865 def discard(self, value):
1865 def discard(self, value):
1866 self.revs.discard(value)
1866 self.revs.discard(value)
1867 self.set.discard(value)
1867 self.set.discard(value)
1868
1868
1869 wanted = lazywantedset()
1869 wanted = lazywantedset()
1870
1870
1871 # it might be worthwhile to do this in the iterator if the rev range
1871 # it might be worthwhile to do this in the iterator if the rev range
1872 # is descending and the prune args are all within that range
1872 # is descending and the prune args are all within that range
1873 for rev in opts.get('prune', ()):
1873 for rev in opts.get('prune', ()):
1874 rev = repo[rev].rev()
1874 rev = repo[rev].rev()
1875 ff = _followfilter(repo)
1875 ff = _followfilter(repo)
1876 stop = min(revs[0], revs[-1])
1876 stop = min(revs[0], revs[-1])
1877 for x in xrange(rev, stop - 1, -1):
1877 for x in xrange(rev, stop - 1, -1):
1878 if ff.match(x):
1878 if ff.match(x):
1879 wanted = wanted - [x]
1879 wanted = wanted - [x]
1880
1880
1881 # Now that wanted is correctly initialized, we can iterate over the
1881 # Now that wanted is correctly initialized, we can iterate over the
1882 # revision range, yielding only revisions in wanted.
1882 # revision range, yielding only revisions in wanted.
1883 def iterate():
1883 def iterate():
1884 if follow and match.always():
1884 if follow and match.always():
1885 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1885 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1886 def want(rev):
1886 def want(rev):
1887 return ff.match(rev) and rev in wanted
1887 return ff.match(rev) and rev in wanted
1888 else:
1888 else:
1889 def want(rev):
1889 def want(rev):
1890 return rev in wanted
1890 return rev in wanted
1891
1891
1892 it = iter(revs)
1892 it = iter(revs)
1893 stopiteration = False
1893 stopiteration = False
1894 for windowsize in increasingwindows():
1894 for windowsize in increasingwindows():
1895 nrevs = []
1895 nrevs = []
1896 for i in xrange(windowsize):
1896 for i in xrange(windowsize):
1897 rev = next(it, None)
1897 rev = next(it, None)
1898 if rev is None:
1898 if rev is None:
1899 stopiteration = True
1899 stopiteration = True
1900 break
1900 break
1901 elif want(rev):
1901 elif want(rev):
1902 nrevs.append(rev)
1902 nrevs.append(rev)
1903 for rev in sorted(nrevs):
1903 for rev in sorted(nrevs):
1904 fns = fncache.get(rev)
1904 fns = fncache.get(rev)
1905 ctx = change(rev)
1905 ctx = change(rev)
1906 if not fns:
1906 if not fns:
1907 def fns_generator():
1907 def fns_generator():
1908 for f in ctx.files():
1908 for f in ctx.files():
1909 if match(f):
1909 if match(f):
1910 yield f
1910 yield f
1911 fns = fns_generator()
1911 fns = fns_generator()
1912 prepare(ctx, fns)
1912 prepare(ctx, fns)
1913 for rev in nrevs:
1913 for rev in nrevs:
1914 yield change(rev)
1914 yield change(rev)
1915
1915
1916 if stopiteration:
1916 if stopiteration:
1917 break
1917 break
1918
1918
1919 return iterate()
1919 return iterate()
1920
1920
1921 def _makefollowlogfilematcher(repo, files, followfirst):
1921 def _makefollowlogfilematcher(repo, files, followfirst):
1922 # When displaying a revision with --patch --follow FILE, we have
1922 # When displaying a revision with --patch --follow FILE, we have
1923 # to know which file of the revision must be diffed. With
1923 # to know which file of the revision must be diffed. With
1924 # --follow, we want the names of the ancestors of FILE in the
1924 # --follow, we want the names of the ancestors of FILE in the
1925 # revision, stored in "fcache". "fcache" is populated by
1925 # revision, stored in "fcache". "fcache" is populated by
1926 # reproducing the graph traversal already done by --follow revset
1926 # reproducing the graph traversal already done by --follow revset
1927 # and relating linkrevs to file names (which is not "correct" but
1927 # and relating linkrevs to file names (which is not "correct" but
1928 # good enough).
1928 # good enough).
1929 fcache = {}
1929 fcache = {}
1930 fcacheready = [False]
1930 fcacheready = [False]
1931 pctx = repo['.']
1931 pctx = repo['.']
1932
1932
1933 def populate():
1933 def populate():
1934 for fn in files:
1934 for fn in files:
1935 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1935 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1936 for c in i:
1936 for c in i:
1937 fcache.setdefault(c.linkrev(), set()).add(c.path())
1937 fcache.setdefault(c.linkrev(), set()).add(c.path())
1938
1938
1939 def filematcher(rev):
1939 def filematcher(rev):
1940 if not fcacheready[0]:
1940 if not fcacheready[0]:
1941 # Lazy initialization
1941 # Lazy initialization
1942 fcacheready[0] = True
1942 fcacheready[0] = True
1943 populate()
1943 populate()
1944 return scmutil.matchfiles(repo, fcache.get(rev, []))
1944 return scmutil.matchfiles(repo, fcache.get(rev, []))
1945
1945
1946 return filematcher
1946 return filematcher
1947
1947
1948 def _makenofollowlogfilematcher(repo, pats, opts):
1948 def _makenofollowlogfilematcher(repo, pats, opts):
1949 '''hook for extensions to override the filematcher for non-follow cases'''
1949 '''hook for extensions to override the filematcher for non-follow cases'''
1950 return None
1950 return None
1951
1951
1952 def _makelogrevset(repo, pats, opts, revs):
1952 def _makelogrevset(repo, pats, opts, revs):
1953 """Return (expr, filematcher) where expr is a revset string built
1953 """Return (expr, filematcher) where expr is a revset string built
1954 from log options and file patterns or None. If --stat or --patch
1954 from log options and file patterns or None. If --stat or --patch
1955 are not passed filematcher is None. Otherwise it is a callable
1955 are not passed filematcher is None. Otherwise it is a callable
1956 taking a revision number and returning a match objects filtering
1956 taking a revision number and returning a match objects filtering
1957 the files to be detailed when displaying the revision.
1957 the files to be detailed when displaying the revision.
1958 """
1958 """
1959 opt2revset = {
1959 opt2revset = {
1960 'no_merges': ('not merge()', None),
1960 'no_merges': ('not merge()', None),
1961 'only_merges': ('merge()', None),
1961 'only_merges': ('merge()', None),
1962 '_ancestors': ('ancestors(%(val)s)', None),
1962 '_ancestors': ('ancestors(%(val)s)', None),
1963 '_fancestors': ('_firstancestors(%(val)s)', None),
1963 '_fancestors': ('_firstancestors(%(val)s)', None),
1964 '_descendants': ('descendants(%(val)s)', None),
1964 '_descendants': ('descendants(%(val)s)', None),
1965 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1965 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1966 '_matchfiles': ('_matchfiles(%(val)s)', None),
1966 '_matchfiles': ('_matchfiles(%(val)s)', None),
1967 'date': ('date(%(val)r)', None),
1967 'date': ('date(%(val)r)', None),
1968 'branch': ('branch(%(val)r)', ' or '),
1968 'branch': ('branch(%(val)r)', ' or '),
1969 '_patslog': ('filelog(%(val)r)', ' or '),
1969 '_patslog': ('filelog(%(val)r)', ' or '),
1970 '_patsfollow': ('follow(%(val)r)', ' or '),
1970 '_patsfollow': ('follow(%(val)r)', ' or '),
1971 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1971 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1972 'keyword': ('keyword(%(val)r)', ' or '),
1972 'keyword': ('keyword(%(val)r)', ' or '),
1973 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1973 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1974 'user': ('user(%(val)r)', ' or '),
1974 'user': ('user(%(val)r)', ' or '),
1975 }
1975 }
1976
1976
1977 opts = dict(opts)
1977 opts = dict(opts)
1978 # follow or not follow?
1978 # follow or not follow?
1979 follow = opts.get('follow') or opts.get('follow_first')
1979 follow = opts.get('follow') or opts.get('follow_first')
1980 if opts.get('follow_first'):
1980 if opts.get('follow_first'):
1981 followfirst = 1
1981 followfirst = 1
1982 else:
1982 else:
1983 followfirst = 0
1983 followfirst = 0
1984 # --follow with FILE behavior depends on revs...
1984 # --follow with FILE behavior depends on revs...
1985 it = iter(revs)
1985 it = iter(revs)
1986 startrev = it.next()
1986 startrev = it.next()
1987 followdescendants = startrev < next(it, startrev)
1987 followdescendants = startrev < next(it, startrev)
1988
1988
1989 # branch and only_branch are really aliases and must be handled at
1989 # branch and only_branch are really aliases and must be handled at
1990 # the same time
1990 # the same time
1991 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1991 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1992 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1992 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1993 # pats/include/exclude are passed to match.match() directly in
1993 # pats/include/exclude are passed to match.match() directly in
1994 # _matchfiles() revset but walkchangerevs() builds its matcher with
1994 # _matchfiles() revset but walkchangerevs() builds its matcher with
1995 # scmutil.match(). The difference is input pats are globbed on
1995 # scmutil.match(). The difference is input pats are globbed on
1996 # platforms without shell expansion (windows).
1996 # platforms without shell expansion (windows).
1997 wctx = repo[None]
1997 wctx = repo[None]
1998 match, pats = scmutil.matchandpats(wctx, pats, opts)
1998 match, pats = scmutil.matchandpats(wctx, pats, opts)
1999 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1999 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
2000 opts.get('removed'))
2000 opts.get('removed'))
2001 if not slowpath:
2001 if not slowpath:
2002 for f in match.files():
2002 for f in match.files():
2003 if follow and f not in wctx:
2003 if follow and f not in wctx:
2004 # If the file exists, it may be a directory, so let it
2004 # If the file exists, it may be a directory, so let it
2005 # take the slow path.
2005 # take the slow path.
2006 if os.path.exists(repo.wjoin(f)):
2006 if os.path.exists(repo.wjoin(f)):
2007 slowpath = True
2007 slowpath = True
2008 continue
2008 continue
2009 else:
2009 else:
2010 raise error.Abort(_('cannot follow file not in parent '
2010 raise error.Abort(_('cannot follow file not in parent '
2011 'revision: "%s"') % f)
2011 'revision: "%s"') % f)
2012 filelog = repo.file(f)
2012 filelog = repo.file(f)
2013 if not filelog:
2013 if not filelog:
2014 # A zero count may be a directory or deleted file, so
2014 # A zero count may be a directory or deleted file, so
2015 # try to find matching entries on the slow path.
2015 # try to find matching entries on the slow path.
2016 if follow:
2016 if follow:
2017 raise error.Abort(
2017 raise error.Abort(
2018 _('cannot follow nonexistent file: "%s"') % f)
2018 _('cannot follow nonexistent file: "%s"') % f)
2019 slowpath = True
2019 slowpath = True
2020
2020
2021 # We decided to fall back to the slowpath because at least one
2021 # We decided to fall back to the slowpath because at least one
2022 # of the paths was not a file. Check to see if at least one of them
2022 # of the paths was not a file. Check to see if at least one of them
2023 # existed in history - in that case, we'll continue down the
2023 # existed in history - in that case, we'll continue down the
2024 # slowpath; otherwise, we can turn off the slowpath
2024 # slowpath; otherwise, we can turn off the slowpath
2025 if slowpath:
2025 if slowpath:
2026 for path in match.files():
2026 for path in match.files():
2027 if path == '.' or path in repo.store:
2027 if path == '.' or path in repo.store:
2028 break
2028 break
2029 else:
2029 else:
2030 slowpath = False
2030 slowpath = False
2031
2031
2032 fpats = ('_patsfollow', '_patsfollowfirst')
2032 fpats = ('_patsfollow', '_patsfollowfirst')
2033 fnopats = (('_ancestors', '_fancestors'),
2033 fnopats = (('_ancestors', '_fancestors'),
2034 ('_descendants', '_fdescendants'))
2034 ('_descendants', '_fdescendants'))
2035 if slowpath:
2035 if slowpath:
2036 # See walkchangerevs() slow path.
2036 # See walkchangerevs() slow path.
2037 #
2037 #
2038 # pats/include/exclude cannot be represented as separate
2038 # pats/include/exclude cannot be represented as separate
2039 # revset expressions as their filtering logic applies at file
2039 # revset expressions as their filtering logic applies at file
2040 # level. For instance "-I a -X a" matches a revision touching
2040 # level. For instance "-I a -X a" matches a revision touching
2041 # "a" and "b" while "file(a) and not file(b)" does
2041 # "a" and "b" while "file(a) and not file(b)" does
2042 # not. Besides, filesets are evaluated against the working
2042 # not. Besides, filesets are evaluated against the working
2043 # directory.
2043 # directory.
2044 matchargs = ['r:', 'd:relpath']
2044 matchargs = ['r:', 'd:relpath']
2045 for p in pats:
2045 for p in pats:
2046 matchargs.append('p:' + p)
2046 matchargs.append('p:' + p)
2047 for p in opts.get('include', []):
2047 for p in opts.get('include', []):
2048 matchargs.append('i:' + p)
2048 matchargs.append('i:' + p)
2049 for p in opts.get('exclude', []):
2049 for p in opts.get('exclude', []):
2050 matchargs.append('x:' + p)
2050 matchargs.append('x:' + p)
2051 matchargs = ','.join(('%r' % p) for p in matchargs)
2051 matchargs = ','.join(('%r' % p) for p in matchargs)
2052 opts['_matchfiles'] = matchargs
2052 opts['_matchfiles'] = matchargs
2053 if follow:
2053 if follow:
2054 opts[fnopats[0][followfirst]] = '.'
2054 opts[fnopats[0][followfirst]] = '.'
2055 else:
2055 else:
2056 if follow:
2056 if follow:
2057 if pats:
2057 if pats:
2058 # follow() revset interprets its file argument as a
2058 # follow() revset interprets its file argument as a
2059 # manifest entry, so use match.files(), not pats.
2059 # manifest entry, so use match.files(), not pats.
2060 opts[fpats[followfirst]] = list(match.files())
2060 opts[fpats[followfirst]] = list(match.files())
2061 else:
2061 else:
2062 op = fnopats[followdescendants][followfirst]
2062 op = fnopats[followdescendants][followfirst]
2063 opts[op] = 'rev(%d)' % startrev
2063 opts[op] = 'rev(%d)' % startrev
2064 else:
2064 else:
2065 opts['_patslog'] = list(pats)
2065 opts['_patslog'] = list(pats)
2066
2066
2067 filematcher = None
2067 filematcher = None
2068 if opts.get('patch') or opts.get('stat'):
2068 if opts.get('patch') or opts.get('stat'):
2069 # When following files, track renames via a special matcher.
2069 # When following files, track renames via a special matcher.
2070 # If we're forced to take the slowpath it means we're following
2070 # If we're forced to take the slowpath it means we're following
2071 # at least one pattern/directory, so don't bother with rename tracking.
2071 # at least one pattern/directory, so don't bother with rename tracking.
2072 if follow and not match.always() and not slowpath:
2072 if follow and not match.always() and not slowpath:
2073 # _makefollowlogfilematcher expects its files argument to be
2073 # _makefollowlogfilematcher expects its files argument to be
2074 # relative to the repo root, so use match.files(), not pats.
2074 # relative to the repo root, so use match.files(), not pats.
2075 filematcher = _makefollowlogfilematcher(repo, match.files(),
2075 filematcher = _makefollowlogfilematcher(repo, match.files(),
2076 followfirst)
2076 followfirst)
2077 else:
2077 else:
2078 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2078 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2079 if filematcher is None:
2079 if filematcher is None:
2080 filematcher = lambda rev: match
2080 filematcher = lambda rev: match
2081
2081
2082 expr = []
2082 expr = []
2083 for op, val in sorted(opts.iteritems()):
2083 for op, val in sorted(opts.iteritems()):
2084 if not val:
2084 if not val:
2085 continue
2085 continue
2086 if op not in opt2revset:
2086 if op not in opt2revset:
2087 continue
2087 continue
2088 revop, andor = opt2revset[op]
2088 revop, andor = opt2revset[op]
2089 if '%(val)' not in revop:
2089 if '%(val)' not in revop:
2090 expr.append(revop)
2090 expr.append(revop)
2091 else:
2091 else:
2092 if not isinstance(val, list):
2092 if not isinstance(val, list):
2093 e = revop % {'val': val}
2093 e = revop % {'val': val}
2094 else:
2094 else:
2095 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2095 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2096 expr.append(e)
2096 expr.append(e)
2097
2097
2098 if expr:
2098 if expr:
2099 expr = '(' + ' and '.join(expr) + ')'
2099 expr = '(' + ' and '.join(expr) + ')'
2100 else:
2100 else:
2101 expr = None
2101 expr = None
2102 return expr, filematcher
2102 return expr, filematcher
2103
2103
2104 def _logrevs(repo, opts):
2104 def _logrevs(repo, opts):
2105 # Default --rev value depends on --follow but --follow behavior
2105 # Default --rev value depends on --follow but --follow behavior
2106 # depends on revisions resolved from --rev...
2106 # depends on revisions resolved from --rev...
2107 follow = opts.get('follow') or opts.get('follow_first')
2107 follow = opts.get('follow') or opts.get('follow_first')
2108 if opts.get('rev'):
2108 if opts.get('rev'):
2109 revs = scmutil.revrange(repo, opts['rev'])
2109 revs = scmutil.revrange(repo, opts['rev'])
2110 elif follow and repo.dirstate.p1() == nullid:
2110 elif follow and repo.dirstate.p1() == nullid:
2111 revs = revset.baseset()
2111 revs = revset.baseset()
2112 elif follow:
2112 elif follow:
2113 revs = repo.revs('reverse(:.)')
2113 revs = repo.revs('reverse(:.)')
2114 else:
2114 else:
2115 revs = revset.spanset(repo)
2115 revs = revset.spanset(repo)
2116 revs.reverse()
2116 revs.reverse()
2117 return revs
2117 return revs
2118
2118
2119 def getgraphlogrevs(repo, pats, opts):
2119 def getgraphlogrevs(repo, pats, opts):
2120 """Return (revs, expr, filematcher) where revs is an iterable of
2120 """Return (revs, expr, filematcher) where revs is an iterable of
2121 revision numbers, expr is a revset string built from log options
2121 revision numbers, expr is a revset string built from log options
2122 and file patterns or None, and used to filter 'revs'. If --stat or
2122 and file patterns or None, and used to filter 'revs'. If --stat or
2123 --patch are not passed filematcher is None. Otherwise it is a
2123 --patch are not passed filematcher is None. Otherwise it is a
2124 callable taking a revision number and returning a match objects
2124 callable taking a revision number and returning a match objects
2125 filtering the files to be detailed when displaying the revision.
2125 filtering the files to be detailed when displaying the revision.
2126 """
2126 """
2127 limit = loglimit(opts)
2127 limit = loglimit(opts)
2128 revs = _logrevs(repo, opts)
2128 revs = _logrevs(repo, opts)
2129 if not revs:
2129 if not revs:
2130 return revset.baseset(), None, None
2130 return revset.baseset(), None, None
2131 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2131 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2132 if opts.get('rev'):
2132 if opts.get('rev'):
2133 # User-specified revs might be unsorted, but don't sort before
2133 # User-specified revs might be unsorted, but don't sort before
2134 # _makelogrevset because it might depend on the order of revs
2134 # _makelogrevset because it might depend on the order of revs
2135 revs.sort(reverse=True)
2135 revs.sort(reverse=True)
2136 if expr:
2136 if expr:
2137 # Revset matchers often operate faster on revisions in changelog
2137 # Revset matchers often operate faster on revisions in changelog
2138 # order, because most filters deal with the changelog.
2138 # order, because most filters deal with the changelog.
2139 revs.reverse()
2139 revs.reverse()
2140 matcher = revset.match(repo.ui, expr)
2140 matcher = revset.match(repo.ui, expr)
2141 # Revset matches can reorder revisions. "A or B" typically returns
2141 # Revset matches can reorder revisions. "A or B" typically returns
2142 # returns the revision matching A then the revision matching B. Sort
2142 # returns the revision matching A then the revision matching B. Sort
2143 # again to fix that.
2143 # again to fix that.
2144 revs = matcher(repo, revs)
2144 revs = matcher(repo, revs)
2145 revs.sort(reverse=True)
2145 revs.sort(reverse=True)
2146 if limit is not None:
2146 if limit is not None:
2147 limitedrevs = []
2147 limitedrevs = []
2148 for idx, rev in enumerate(revs):
2148 for idx, rev in enumerate(revs):
2149 if idx >= limit:
2149 if idx >= limit:
2150 break
2150 break
2151 limitedrevs.append(rev)
2151 limitedrevs.append(rev)
2152 revs = revset.baseset(limitedrevs)
2152 revs = revset.baseset(limitedrevs)
2153
2153
2154 return revs, expr, filematcher
2154 return revs, expr, filematcher
2155
2155
2156 def getlogrevs(repo, pats, opts):
2156 def getlogrevs(repo, pats, opts):
2157 """Return (revs, expr, filematcher) where revs is an iterable of
2157 """Return (revs, expr, filematcher) where revs is an iterable of
2158 revision numbers, expr is a revset string built from log options
2158 revision numbers, expr is a revset string built from log options
2159 and file patterns or None, and used to filter 'revs'. If --stat or
2159 and file patterns or None, and used to filter 'revs'. If --stat or
2160 --patch are not passed filematcher is None. Otherwise it is a
2160 --patch are not passed filematcher is None. Otherwise it is a
2161 callable taking a revision number and returning a match objects
2161 callable taking a revision number and returning a match objects
2162 filtering the files to be detailed when displaying the revision.
2162 filtering the files to be detailed when displaying the revision.
2163 """
2163 """
2164 limit = loglimit(opts)
2164 limit = loglimit(opts)
2165 revs = _logrevs(repo, opts)
2165 revs = _logrevs(repo, opts)
2166 if not revs:
2166 if not revs:
2167 return revset.baseset([]), None, None
2167 return revset.baseset([]), None, None
2168 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2168 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2169 if expr:
2169 if expr:
2170 # Revset matchers often operate faster on revisions in changelog
2170 # Revset matchers often operate faster on revisions in changelog
2171 # order, because most filters deal with the changelog.
2171 # order, because most filters deal with the changelog.
2172 if not opts.get('rev'):
2172 if not opts.get('rev'):
2173 revs.reverse()
2173 revs.reverse()
2174 matcher = revset.match(repo.ui, expr)
2174 matcher = revset.match(repo.ui, expr)
2175 # Revset matches can reorder revisions. "A or B" typically returns
2175 # Revset matches can reorder revisions. "A or B" typically returns
2176 # returns the revision matching A then the revision matching B. Sort
2176 # returns the revision matching A then the revision matching B. Sort
2177 # again to fix that.
2177 # again to fix that.
2178 fixopts = ['branch', 'only_branch', 'keyword', 'user']
2178 fixopts = ['branch', 'only_branch', 'keyword', 'user']
2179 oldrevs = revs
2179 oldrevs = revs
2180 revs = matcher(repo, revs)
2180 revs = matcher(repo, revs)
2181 if not opts.get('rev'):
2181 if not opts.get('rev'):
2182 revs.sort(reverse=True)
2182 revs.sort(reverse=True)
2183 elif len(pats) > 1 or any(len(opts.get(op, [])) > 1 for op in fixopts):
2183 elif len(pats) > 1 or any(len(opts.get(op, [])) > 1 for op in fixopts):
2184 # XXX "A or B" is known to change the order; fix it by filtering
2184 # XXX "A or B" is known to change the order; fix it by filtering
2185 # matched set again (issue5100)
2185 # matched set again (issue5100)
2186 revs = oldrevs & revs
2186 revs = oldrevs & revs
2187 if limit is not None:
2187 if limit is not None:
2188 limitedrevs = []
2188 limitedrevs = []
2189 for idx, r in enumerate(revs):
2189 for idx, r in enumerate(revs):
2190 if limit <= idx:
2190 if limit <= idx:
2191 break
2191 break
2192 limitedrevs.append(r)
2192 limitedrevs.append(r)
2193 revs = revset.baseset(limitedrevs)
2193 revs = revset.baseset(limitedrevs)
2194
2194
2195 return revs, expr, filematcher
2195 return revs, expr, filematcher
2196
2196
2197 def _graphnodeformatter(ui, displayer):
2197 def _graphnodeformatter(ui, displayer):
2198 spec = ui.config('ui', 'graphnodetemplate')
2198 spec = ui.config('ui', 'graphnodetemplate')
2199 if not spec:
2199 if not spec:
2200 return templatekw.showgraphnode # fast path for "{graphnode}"
2200 return templatekw.showgraphnode # fast path for "{graphnode}"
2201
2201
2202 templ = formatter.gettemplater(ui, 'graphnode', spec)
2202 templ = formatter.gettemplater(ui, 'graphnode', spec)
2203 cache = {}
2203 cache = {}
2204 if isinstance(displayer, changeset_templater):
2204 if isinstance(displayer, changeset_templater):
2205 cache = displayer.cache # reuse cache of slow templates
2205 cache = displayer.cache # reuse cache of slow templates
2206 props = templatekw.keywords.copy()
2206 props = templatekw.keywords.copy()
2207 props['templ'] = templ
2207 props['templ'] = templ
2208 props['cache'] = cache
2208 props['cache'] = cache
2209 def formatnode(repo, ctx):
2209 def formatnode(repo, ctx):
2210 props['ctx'] = ctx
2210 props['ctx'] = ctx
2211 props['repo'] = repo
2211 props['repo'] = repo
2212 props['ui'] = repo.ui
2212 props['ui'] = repo.ui
2213 props['revcache'] = {}
2213 props['revcache'] = {}
2214 return templater.stringify(templ('graphnode', **props))
2214 return templater.stringify(templ('graphnode', **props))
2215 return formatnode
2215 return formatnode
2216
2216
2217 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2217 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2218 filematcher=None):
2218 filematcher=None):
2219 formatnode = _graphnodeformatter(ui, displayer)
2219 formatnode = _graphnodeformatter(ui, displayer)
2220 state = graphmod.asciistate()
2220 state = graphmod.asciistate()
2221 styles = state['styles']
2221 styles = state['styles']
2222 edgetypes = {
2222 edgetypes = {
2223 'parent': graphmod.PARENT,
2223 'parent': graphmod.PARENT,
2224 'grandparent': graphmod.GRANDPARENT,
2224 'grandparent': graphmod.GRANDPARENT,
2225 'missing': graphmod.MISSINGPARENT
2225 'missing': graphmod.MISSINGPARENT
2226 }
2226 }
2227 for name, key in edgetypes.items():
2227 for name, key in edgetypes.items():
2228 # experimental config: ui.graphstyle.*
2228 # experimental config: ui.graphstyle.*
2229 styles[key] = ui.config('ui', 'graphstyle.%s' % name, styles[key])
2229 styles[key] = ui.config('ui', 'graphstyle.%s' % name, styles[key])
2230 if not styles[key]:
2230 if not styles[key]:
2231 styles[key] = None
2231 styles[key] = None
2232 for rev, type, ctx, parents in dag:
2232 for rev, type, ctx, parents in dag:
2233 char = formatnode(repo, ctx)
2233 char = formatnode(repo, ctx)
2234 copies = None
2234 copies = None
2235 if getrenamed and ctx.rev():
2235 if getrenamed and ctx.rev():
2236 copies = []
2236 copies = []
2237 for fn in ctx.files():
2237 for fn in ctx.files():
2238 rename = getrenamed(fn, ctx.rev())
2238 rename = getrenamed(fn, ctx.rev())
2239 if rename:
2239 if rename:
2240 copies.append((fn, rename[0]))
2240 copies.append((fn, rename[0]))
2241 revmatchfn = None
2241 revmatchfn = None
2242 if filematcher is not None:
2242 if filematcher is not None:
2243 revmatchfn = filematcher(ctx.rev())
2243 revmatchfn = filematcher(ctx.rev())
2244 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2244 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2245 lines = displayer.hunk.pop(rev).split('\n')
2245 lines = displayer.hunk.pop(rev).split('\n')
2246 if not lines[-1]:
2246 if not lines[-1]:
2247 del lines[-1]
2247 del lines[-1]
2248 displayer.flush(ctx)
2248 displayer.flush(ctx)
2249 edges = edgefn(type, char, lines, state, rev, parents)
2249 edges = edgefn(type, char, lines, state, rev, parents)
2250 for type, char, lines, coldata in edges:
2250 for type, char, lines, coldata in edges:
2251 graphmod.ascii(ui, state, type, char, lines, coldata)
2251 graphmod.ascii(ui, state, type, char, lines, coldata)
2252 displayer.close()
2252 displayer.close()
2253
2253
2254 def graphlog(ui, repo, *pats, **opts):
2254 def graphlog(ui, repo, *pats, **opts):
2255 # Parameters are identical to log command ones
2255 # Parameters are identical to log command ones
2256 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2256 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2257 revdag = graphmod.dagwalker(repo, revs)
2257 revdag = graphmod.dagwalker(repo, revs)
2258
2258
2259 getrenamed = None
2259 getrenamed = None
2260 if opts.get('copies'):
2260 if opts.get('copies'):
2261 endrev = None
2261 endrev = None
2262 if opts.get('rev'):
2262 if opts.get('rev'):
2263 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2263 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2264 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2264 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2265 displayer = show_changeset(ui, repo, opts, buffered=True)
2265 displayer = show_changeset(ui, repo, opts, buffered=True)
2266 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2266 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2267 filematcher)
2267 filematcher)
2268
2268
2269 def checkunsupportedgraphflags(pats, opts):
2269 def checkunsupportedgraphflags(pats, opts):
2270 for op in ["newest_first"]:
2270 for op in ["newest_first"]:
2271 if op in opts and opts[op]:
2271 if op in opts and opts[op]:
2272 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2272 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2273 % op.replace("_", "-"))
2273 % op.replace("_", "-"))
2274
2274
2275 def graphrevs(repo, nodes, opts):
2275 def graphrevs(repo, nodes, opts):
2276 limit = loglimit(opts)
2276 limit = loglimit(opts)
2277 nodes.reverse()
2277 nodes.reverse()
2278 if limit is not None:
2278 if limit is not None:
2279 nodes = nodes[:limit]
2279 nodes = nodes[:limit]
2280 return graphmod.nodes(repo, nodes)
2280 return graphmod.nodes(repo, nodes)
2281
2281
2282 def add(ui, repo, match, prefix, explicitonly, **opts):
2282 def add(ui, repo, match, prefix, explicitonly, **opts):
2283 join = lambda f: os.path.join(prefix, f)
2283 join = lambda f: os.path.join(prefix, f)
2284 bad = []
2284 bad = []
2285
2285
2286 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2286 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2287 names = []
2287 names = []
2288 wctx = repo[None]
2288 wctx = repo[None]
2289 cca = None
2289 cca = None
2290 abort, warn = scmutil.checkportabilityalert(ui)
2290 abort, warn = scmutil.checkportabilityalert(ui)
2291 if abort or warn:
2291 if abort or warn:
2292 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2292 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2293
2293
2294 badmatch = matchmod.badmatch(match, badfn)
2294 badmatch = matchmod.badmatch(match, badfn)
2295 dirstate = repo.dirstate
2295 dirstate = repo.dirstate
2296 # We don't want to just call wctx.walk here, since it would return a lot of
2296 # We don't want to just call wctx.walk here, since it would return a lot of
2297 # clean files, which we aren't interested in and takes time.
2297 # clean files, which we aren't interested in and takes time.
2298 for f in sorted(dirstate.walk(badmatch, sorted(wctx.substate),
2298 for f in sorted(dirstate.walk(badmatch, sorted(wctx.substate),
2299 True, False, full=False)):
2299 True, False, full=False)):
2300 exact = match.exact(f)
2300 exact = match.exact(f)
2301 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2301 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2302 if cca:
2302 if cca:
2303 cca(f)
2303 cca(f)
2304 names.append(f)
2304 names.append(f)
2305 if ui.verbose or not exact:
2305 if ui.verbose or not exact:
2306 ui.status(_('adding %s\n') % match.rel(f))
2306 ui.status(_('adding %s\n') % match.rel(f))
2307
2307
2308 for subpath in sorted(wctx.substate):
2308 for subpath in sorted(wctx.substate):
2309 sub = wctx.sub(subpath)
2309 sub = wctx.sub(subpath)
2310 try:
2310 try:
2311 submatch = matchmod.subdirmatcher(subpath, match)
2311 submatch = matchmod.subdirmatcher(subpath, match)
2312 if opts.get('subrepos'):
2312 if opts.get('subrepos'):
2313 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2313 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2314 else:
2314 else:
2315 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2315 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2316 except error.LookupError:
2316 except error.LookupError:
2317 ui.status(_("skipping missing subrepository: %s\n")
2317 ui.status(_("skipping missing subrepository: %s\n")
2318 % join(subpath))
2318 % join(subpath))
2319
2319
2320 if not opts.get('dry_run'):
2320 if not opts.get('dry_run'):
2321 rejected = wctx.add(names, prefix)
2321 rejected = wctx.add(names, prefix)
2322 bad.extend(f for f in rejected if f in match.files())
2322 bad.extend(f for f in rejected if f in match.files())
2323 return bad
2323 return bad
2324
2324
2325 def forget(ui, repo, match, prefix, explicitonly):
2325 def forget(ui, repo, match, prefix, explicitonly):
2326 join = lambda f: os.path.join(prefix, f)
2326 join = lambda f: os.path.join(prefix, f)
2327 bad = []
2327 bad = []
2328 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2328 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2329 wctx = repo[None]
2329 wctx = repo[None]
2330 forgot = []
2330 forgot = []
2331
2331
2332 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2332 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2333 forget = sorted(s[0] + s[1] + s[3] + s[6])
2333 forget = sorted(s[0] + s[1] + s[3] + s[6])
2334 if explicitonly:
2334 if explicitonly:
2335 forget = [f for f in forget if match.exact(f)]
2335 forget = [f for f in forget if match.exact(f)]
2336
2336
2337 for subpath in sorted(wctx.substate):
2337 for subpath in sorted(wctx.substate):
2338 sub = wctx.sub(subpath)
2338 sub = wctx.sub(subpath)
2339 try:
2339 try:
2340 submatch = matchmod.subdirmatcher(subpath, match)
2340 submatch = matchmod.subdirmatcher(subpath, match)
2341 subbad, subforgot = sub.forget(submatch, prefix)
2341 subbad, subforgot = sub.forget(submatch, prefix)
2342 bad.extend([subpath + '/' + f for f in subbad])
2342 bad.extend([subpath + '/' + f for f in subbad])
2343 forgot.extend([subpath + '/' + f for f in subforgot])
2343 forgot.extend([subpath + '/' + f for f in subforgot])
2344 except error.LookupError:
2344 except error.LookupError:
2345 ui.status(_("skipping missing subrepository: %s\n")
2345 ui.status(_("skipping missing subrepository: %s\n")
2346 % join(subpath))
2346 % join(subpath))
2347
2347
2348 if not explicitonly:
2348 if not explicitonly:
2349 for f in match.files():
2349 for f in match.files():
2350 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2350 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2351 if f not in forgot:
2351 if f not in forgot:
2352 if repo.wvfs.exists(f):
2352 if repo.wvfs.exists(f):
2353 # Don't complain if the exact case match wasn't given.
2353 # Don't complain if the exact case match wasn't given.
2354 # But don't do this until after checking 'forgot', so
2354 # But don't do this until after checking 'forgot', so
2355 # that subrepo files aren't normalized, and this op is
2355 # that subrepo files aren't normalized, and this op is
2356 # purely from data cached by the status walk above.
2356 # purely from data cached by the status walk above.
2357 if repo.dirstate.normalize(f) in repo.dirstate:
2357 if repo.dirstate.normalize(f) in repo.dirstate:
2358 continue
2358 continue
2359 ui.warn(_('not removing %s: '
2359 ui.warn(_('not removing %s: '
2360 'file is already untracked\n')
2360 'file is already untracked\n')
2361 % match.rel(f))
2361 % match.rel(f))
2362 bad.append(f)
2362 bad.append(f)
2363
2363
2364 for f in forget:
2364 for f in forget:
2365 if ui.verbose or not match.exact(f):
2365 if ui.verbose or not match.exact(f):
2366 ui.status(_('removing %s\n') % match.rel(f))
2366 ui.status(_('removing %s\n') % match.rel(f))
2367
2367
2368 rejected = wctx.forget(forget, prefix)
2368 rejected = wctx.forget(forget, prefix)
2369 bad.extend(f for f in rejected if f in match.files())
2369 bad.extend(f for f in rejected if f in match.files())
2370 forgot.extend(f for f in forget if f not in rejected)
2370 forgot.extend(f for f in forget if f not in rejected)
2371 return bad, forgot
2371 return bad, forgot
2372
2372
2373 def files(ui, ctx, m, fm, fmt, subrepos):
2373 def files(ui, ctx, m, fm, fmt, subrepos):
2374 rev = ctx.rev()
2374 rev = ctx.rev()
2375 ret = 1
2375 ret = 1
2376 ds = ctx.repo().dirstate
2376 ds = ctx.repo().dirstate
2377
2377
2378 for f in ctx.matches(m):
2378 for f in ctx.matches(m):
2379 if rev is None and ds[f] == 'r':
2379 if rev is None and ds[f] == 'r':
2380 continue
2380 continue
2381 fm.startitem()
2381 fm.startitem()
2382 if ui.verbose:
2382 if ui.verbose:
2383 fc = ctx[f]
2383 fc = ctx[f]
2384 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2384 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2385 fm.data(abspath=f)
2385 fm.data(abspath=f)
2386 fm.write('path', fmt, m.rel(f))
2386 fm.write('path', fmt, m.rel(f))
2387 ret = 0
2387 ret = 0
2388
2388
2389 for subpath in sorted(ctx.substate):
2389 for subpath in sorted(ctx.substate):
2390 def matchessubrepo(subpath):
2390 def matchessubrepo(subpath):
2391 return (m.exact(subpath)
2391 return (m.exact(subpath)
2392 or any(f.startswith(subpath + '/') for f in m.files()))
2392 or any(f.startswith(subpath + '/') for f in m.files()))
2393
2393
2394 if subrepos or matchessubrepo(subpath):
2394 if subrepos or matchessubrepo(subpath):
2395 sub = ctx.sub(subpath)
2395 sub = ctx.sub(subpath)
2396 try:
2396 try:
2397 submatch = matchmod.subdirmatcher(subpath, m)
2397 submatch = matchmod.subdirmatcher(subpath, m)
2398 recurse = m.exact(subpath) or subrepos
2398 recurse = m.exact(subpath) or subrepos
2399 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2399 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2400 ret = 0
2400 ret = 0
2401 except error.LookupError:
2401 except error.LookupError:
2402 ui.status(_("skipping missing subrepository: %s\n")
2402 ui.status(_("skipping missing subrepository: %s\n")
2403 % m.abs(subpath))
2403 % m.abs(subpath))
2404
2404
2405 return ret
2405 return ret
2406
2406
2407 def remove(ui, repo, m, prefix, after, force, subrepos, warnings=None):
2407 def remove(ui, repo, m, prefix, after, force, subrepos, warnings=None):
2408 join = lambda f: os.path.join(prefix, f)
2408 join = lambda f: os.path.join(prefix, f)
2409 ret = 0
2409 ret = 0
2410 s = repo.status(match=m, clean=True)
2410 s = repo.status(match=m, clean=True)
2411 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2411 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2412
2412
2413 wctx = repo[None]
2413 wctx = repo[None]
2414
2414
2415 if warnings is None:
2415 if warnings is None:
2416 warnings = []
2416 warnings = []
2417 warn = True
2417 warn = True
2418 else:
2418 else:
2419 warn = False
2419 warn = False
2420
2420
2421 for subpath in sorted(wctx.substate):
2421 subs = sorted(wctx.substate)
2422 total = len(subs)
2423 count = 0
2424 for subpath in subs:
2422 def matchessubrepo(matcher, subpath):
2425 def matchessubrepo(matcher, subpath):
2423 if matcher.exact(subpath):
2426 if matcher.exact(subpath):
2424 return True
2427 return True
2425 for f in matcher.files():
2428 for f in matcher.files():
2426 if f.startswith(subpath):
2429 if f.startswith(subpath):
2427 return True
2430 return True
2428 return False
2431 return False
2429
2432
2433 count += 1
2430 if subrepos or matchessubrepo(m, subpath):
2434 if subrepos or matchessubrepo(m, subpath):
2435 ui.progress(_('searching'), count, total=total, unit=_('subrepos'))
2436
2431 sub = wctx.sub(subpath)
2437 sub = wctx.sub(subpath)
2432 try:
2438 try:
2433 submatch = matchmod.subdirmatcher(subpath, m)
2439 submatch = matchmod.subdirmatcher(subpath, m)
2434 if sub.removefiles(submatch, prefix, after, force, subrepos,
2440 if sub.removefiles(submatch, prefix, after, force, subrepos,
2435 warnings):
2441 warnings):
2436 ret = 1
2442 ret = 1
2437 except error.LookupError:
2443 except error.LookupError:
2438 warnings.append(_("skipping missing subrepository: %s\n")
2444 warnings.append(_("skipping missing subrepository: %s\n")
2439 % join(subpath))
2445 % join(subpath))
2446 ui.progress(_('searching'), None)
2440
2447
2441 # warn about failure to delete explicit files/dirs
2448 # warn about failure to delete explicit files/dirs
2442 deleteddirs = util.dirs(deleted)
2449 deleteddirs = util.dirs(deleted)
2443 for f in m.files():
2450 files = m.files()
2451 total = len(files)
2452 count = 0
2453 for f in files:
2444 def insubrepo():
2454 def insubrepo():
2445 for subpath in wctx.substate:
2455 for subpath in wctx.substate:
2446 if f.startswith(subpath):
2456 if f.startswith(subpath):
2447 return True
2457 return True
2448 return False
2458 return False
2449
2459
2460 count += 1
2461 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2450 isdir = f in deleteddirs or wctx.hasdir(f)
2462 isdir = f in deleteddirs or wctx.hasdir(f)
2451 if f in repo.dirstate or isdir or f == '.' or insubrepo():
2463 if f in repo.dirstate or isdir or f == '.' or insubrepo():
2452 continue
2464 continue
2453
2465
2454 if repo.wvfs.exists(f):
2466 if repo.wvfs.exists(f):
2455 if repo.wvfs.isdir(f):
2467 if repo.wvfs.isdir(f):
2456 warnings.append(_('not removing %s: no tracked files\n')
2468 warnings.append(_('not removing %s: no tracked files\n')
2457 % m.rel(f))
2469 % m.rel(f))
2458 else:
2470 else:
2459 warnings.append(_('not removing %s: file is untracked\n')
2471 warnings.append(_('not removing %s: file is untracked\n')
2460 % m.rel(f))
2472 % m.rel(f))
2461 # missing files will generate a warning elsewhere
2473 # missing files will generate a warning elsewhere
2462 ret = 1
2474 ret = 1
2475 ui.progress(_('deleting'), None)
2463
2476
2464 if force:
2477 if force:
2465 list = modified + deleted + clean + added
2478 list = modified + deleted + clean + added
2466 elif after:
2479 elif after:
2467 list = deleted
2480 list = deleted
2468 for f in modified + added + clean:
2481 remaining = modified + added + clean
2469 warnings.append(_('not removing %s: file still exists\n') % m.rel(f))
2482 total = len(remaining)
2483 count = 0
2484 for f in remaining:
2485 count += 1
2486 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2487 warnings.append(_('not removing %s: file still exists\n')
2488 % m.rel(f))
2470 ret = 1
2489 ret = 1
2490 ui.progress(_('skipping'), None)
2471 else:
2491 else:
2472 list = deleted + clean
2492 list = deleted + clean
2493 total = len(modified) + len(added)
2494 count = 0
2473 for f in modified:
2495 for f in modified:
2496 count += 1
2497 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2474 warnings.append(_('not removing %s: file is modified (use -f'
2498 warnings.append(_('not removing %s: file is modified (use -f'
2475 ' to force removal)\n') % m.rel(f))
2499 ' to force removal)\n') % m.rel(f))
2476 ret = 1
2500 ret = 1
2477 for f in added:
2501 for f in added:
2502 count += 1
2503 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2478 warnings.append(_('not removing %s: file has been marked for add'
2504 warnings.append(_('not removing %s: file has been marked for add'
2479 ' (use forget to undo)\n') % m.rel(f))
2505 ' (use forget to undo)\n') % m.rel(f))
2480 ret = 1
2506 ret = 1
2481
2507 ui.progress(_('skipping'), None)
2482 for f in sorted(list):
2508
2509 list = sorted(list)
2510 total = len(list)
2511 count = 0
2512 for f in list:
2513 count += 1
2483 if ui.verbose or not m.exact(f):
2514 if ui.verbose or not m.exact(f):
2515 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2484 ui.status(_('removing %s\n') % m.rel(f))
2516 ui.status(_('removing %s\n') % m.rel(f))
2517 ui.progress(_('deleting'), None)
2485
2518
2486 with repo.wlock():
2519 with repo.wlock():
2487 if not after:
2520 if not after:
2488 for f in list:
2521 for f in list:
2489 if f in added:
2522 if f in added:
2490 continue # we never unlink added files on remove
2523 continue # we never unlink added files on remove
2491 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
2524 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
2492 repo[None].forget(list)
2525 repo[None].forget(list)
2493
2526
2494 if warn:
2527 if warn:
2495 for warning in warnings:
2528 for warning in warnings:
2496 ui.warn(warning)
2529 ui.warn(warning)
2497
2530
2498 return ret
2531 return ret
2499
2532
2500 def cat(ui, repo, ctx, matcher, prefix, **opts):
2533 def cat(ui, repo, ctx, matcher, prefix, **opts):
2501 err = 1
2534 err = 1
2502
2535
2503 def write(path):
2536 def write(path):
2504 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2537 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2505 pathname=os.path.join(prefix, path))
2538 pathname=os.path.join(prefix, path))
2506 data = ctx[path].data()
2539 data = ctx[path].data()
2507 if opts.get('decode'):
2540 if opts.get('decode'):
2508 data = repo.wwritedata(path, data)
2541 data = repo.wwritedata(path, data)
2509 fp.write(data)
2542 fp.write(data)
2510 fp.close()
2543 fp.close()
2511
2544
2512 # Automation often uses hg cat on single files, so special case it
2545 # Automation often uses hg cat on single files, so special case it
2513 # for performance to avoid the cost of parsing the manifest.
2546 # for performance to avoid the cost of parsing the manifest.
2514 if len(matcher.files()) == 1 and not matcher.anypats():
2547 if len(matcher.files()) == 1 and not matcher.anypats():
2515 file = matcher.files()[0]
2548 file = matcher.files()[0]
2516 mf = repo.manifest
2549 mf = repo.manifest
2517 mfnode = ctx.manifestnode()
2550 mfnode = ctx.manifestnode()
2518 if mfnode and mf.find(mfnode, file)[0]:
2551 if mfnode and mf.find(mfnode, file)[0]:
2519 write(file)
2552 write(file)
2520 return 0
2553 return 0
2521
2554
2522 # Don't warn about "missing" files that are really in subrepos
2555 # Don't warn about "missing" files that are really in subrepos
2523 def badfn(path, msg):
2556 def badfn(path, msg):
2524 for subpath in ctx.substate:
2557 for subpath in ctx.substate:
2525 if path.startswith(subpath):
2558 if path.startswith(subpath):
2526 return
2559 return
2527 matcher.bad(path, msg)
2560 matcher.bad(path, msg)
2528
2561
2529 for abs in ctx.walk(matchmod.badmatch(matcher, badfn)):
2562 for abs in ctx.walk(matchmod.badmatch(matcher, badfn)):
2530 write(abs)
2563 write(abs)
2531 err = 0
2564 err = 0
2532
2565
2533 for subpath in sorted(ctx.substate):
2566 for subpath in sorted(ctx.substate):
2534 sub = ctx.sub(subpath)
2567 sub = ctx.sub(subpath)
2535 try:
2568 try:
2536 submatch = matchmod.subdirmatcher(subpath, matcher)
2569 submatch = matchmod.subdirmatcher(subpath, matcher)
2537
2570
2538 if not sub.cat(submatch, os.path.join(prefix, sub._path),
2571 if not sub.cat(submatch, os.path.join(prefix, sub._path),
2539 **opts):
2572 **opts):
2540 err = 0
2573 err = 0
2541 except error.RepoLookupError:
2574 except error.RepoLookupError:
2542 ui.status(_("skipping missing subrepository: %s\n")
2575 ui.status(_("skipping missing subrepository: %s\n")
2543 % os.path.join(prefix, subpath))
2576 % os.path.join(prefix, subpath))
2544
2577
2545 return err
2578 return err
2546
2579
2547 def commit(ui, repo, commitfunc, pats, opts):
2580 def commit(ui, repo, commitfunc, pats, opts):
2548 '''commit the specified files or all outstanding changes'''
2581 '''commit the specified files or all outstanding changes'''
2549 date = opts.get('date')
2582 date = opts.get('date')
2550 if date:
2583 if date:
2551 opts['date'] = util.parsedate(date)
2584 opts['date'] = util.parsedate(date)
2552 message = logmessage(ui, opts)
2585 message = logmessage(ui, opts)
2553 matcher = scmutil.match(repo[None], pats, opts)
2586 matcher = scmutil.match(repo[None], pats, opts)
2554
2587
2555 # extract addremove carefully -- this function can be called from a command
2588 # extract addremove carefully -- this function can be called from a command
2556 # that doesn't support addremove
2589 # that doesn't support addremove
2557 if opts.get('addremove'):
2590 if opts.get('addremove'):
2558 if scmutil.addremove(repo, matcher, "", opts) != 0:
2591 if scmutil.addremove(repo, matcher, "", opts) != 0:
2559 raise error.Abort(
2592 raise error.Abort(
2560 _("failed to mark all new/missing files as added/removed"))
2593 _("failed to mark all new/missing files as added/removed"))
2561
2594
2562 return commitfunc(ui, repo, message, matcher, opts)
2595 return commitfunc(ui, repo, message, matcher, opts)
2563
2596
2564 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2597 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2565 # avoid cycle context -> subrepo -> cmdutil
2598 # avoid cycle context -> subrepo -> cmdutil
2566 from . import context
2599 from . import context
2567
2600
2568 # amend will reuse the existing user if not specified, but the obsolete
2601 # amend will reuse the existing user if not specified, but the obsolete
2569 # marker creation requires that the current user's name is specified.
2602 # marker creation requires that the current user's name is specified.
2570 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2603 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2571 ui.username() # raise exception if username not set
2604 ui.username() # raise exception if username not set
2572
2605
2573 ui.note(_('amending changeset %s\n') % old)
2606 ui.note(_('amending changeset %s\n') % old)
2574 base = old.p1()
2607 base = old.p1()
2575 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2608 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2576
2609
2577 wlock = lock = newid = None
2610 wlock = lock = newid = None
2578 try:
2611 try:
2579 wlock = repo.wlock()
2612 wlock = repo.wlock()
2580 lock = repo.lock()
2613 lock = repo.lock()
2581 with repo.transaction('amend') as tr:
2614 with repo.transaction('amend') as tr:
2582 # See if we got a message from -m or -l, if not, open the editor
2615 # See if we got a message from -m or -l, if not, open the editor
2583 # with the message of the changeset to amend
2616 # with the message of the changeset to amend
2584 message = logmessage(ui, opts)
2617 message = logmessage(ui, opts)
2585 # ensure logfile does not conflict with later enforcement of the
2618 # ensure logfile does not conflict with later enforcement of the
2586 # message. potential logfile content has been processed by
2619 # message. potential logfile content has been processed by
2587 # `logmessage` anyway.
2620 # `logmessage` anyway.
2588 opts.pop('logfile')
2621 opts.pop('logfile')
2589 # First, do a regular commit to record all changes in the working
2622 # First, do a regular commit to record all changes in the working
2590 # directory (if there are any)
2623 # directory (if there are any)
2591 ui.callhooks = False
2624 ui.callhooks = False
2592 activebookmark = repo._bookmarks.active
2625 activebookmark = repo._bookmarks.active
2593 try:
2626 try:
2594 repo._bookmarks.active = None
2627 repo._bookmarks.active = None
2595 opts['message'] = 'temporary amend commit for %s' % old
2628 opts['message'] = 'temporary amend commit for %s' % old
2596 node = commit(ui, repo, commitfunc, pats, opts)
2629 node = commit(ui, repo, commitfunc, pats, opts)
2597 finally:
2630 finally:
2598 repo._bookmarks.active = activebookmark
2631 repo._bookmarks.active = activebookmark
2599 repo._bookmarks.recordchange(tr)
2632 repo._bookmarks.recordchange(tr)
2600 ui.callhooks = True
2633 ui.callhooks = True
2601 ctx = repo[node]
2634 ctx = repo[node]
2602
2635
2603 # Participating changesets:
2636 # Participating changesets:
2604 #
2637 #
2605 # node/ctx o - new (intermediate) commit that contains changes
2638 # node/ctx o - new (intermediate) commit that contains changes
2606 # | from working dir to go into amending commit
2639 # | from working dir to go into amending commit
2607 # | (or a workingctx if there were no changes)
2640 # | (or a workingctx if there were no changes)
2608 # |
2641 # |
2609 # old o - changeset to amend
2642 # old o - changeset to amend
2610 # |
2643 # |
2611 # base o - parent of amending changeset
2644 # base o - parent of amending changeset
2612
2645
2613 # Update extra dict from amended commit (e.g. to preserve graft
2646 # Update extra dict from amended commit (e.g. to preserve graft
2614 # source)
2647 # source)
2615 extra.update(old.extra())
2648 extra.update(old.extra())
2616
2649
2617 # Also update it from the intermediate commit or from the wctx
2650 # Also update it from the intermediate commit or from the wctx
2618 extra.update(ctx.extra())
2651 extra.update(ctx.extra())
2619
2652
2620 if len(old.parents()) > 1:
2653 if len(old.parents()) > 1:
2621 # ctx.files() isn't reliable for merges, so fall back to the
2654 # ctx.files() isn't reliable for merges, so fall back to the
2622 # slower repo.status() method
2655 # slower repo.status() method
2623 files = set([fn for st in repo.status(base, old)[:3]
2656 files = set([fn for st in repo.status(base, old)[:3]
2624 for fn in st])
2657 for fn in st])
2625 else:
2658 else:
2626 files = set(old.files())
2659 files = set(old.files())
2627
2660
2628 # Second, we use either the commit we just did, or if there were no
2661 # Second, we use either the commit we just did, or if there were no
2629 # changes the parent of the working directory as the version of the
2662 # changes the parent of the working directory as the version of the
2630 # files in the final amend commit
2663 # files in the final amend commit
2631 if node:
2664 if node:
2632 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2665 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2633
2666
2634 user = ctx.user()
2667 user = ctx.user()
2635 date = ctx.date()
2668 date = ctx.date()
2636 # Recompute copies (avoid recording a -> b -> a)
2669 # Recompute copies (avoid recording a -> b -> a)
2637 copied = copies.pathcopies(base, ctx)
2670 copied = copies.pathcopies(base, ctx)
2638 if old.p2:
2671 if old.p2:
2639 copied.update(copies.pathcopies(old.p2(), ctx))
2672 copied.update(copies.pathcopies(old.p2(), ctx))
2640
2673
2641 # Prune files which were reverted by the updates: if old
2674 # Prune files which were reverted by the updates: if old
2642 # introduced file X and our intermediate commit, node,
2675 # introduced file X and our intermediate commit, node,
2643 # renamed that file, then those two files are the same and
2676 # renamed that file, then those two files are the same and
2644 # we can discard X from our list of files. Likewise if X
2677 # we can discard X from our list of files. Likewise if X
2645 # was deleted, it's no longer relevant
2678 # was deleted, it's no longer relevant
2646 files.update(ctx.files())
2679 files.update(ctx.files())
2647
2680
2648 def samefile(f):
2681 def samefile(f):
2649 if f in ctx.manifest():
2682 if f in ctx.manifest():
2650 a = ctx.filectx(f)
2683 a = ctx.filectx(f)
2651 if f in base.manifest():
2684 if f in base.manifest():
2652 b = base.filectx(f)
2685 b = base.filectx(f)
2653 return (not a.cmp(b)
2686 return (not a.cmp(b)
2654 and a.flags() == b.flags())
2687 and a.flags() == b.flags())
2655 else:
2688 else:
2656 return False
2689 return False
2657 else:
2690 else:
2658 return f not in base.manifest()
2691 return f not in base.manifest()
2659 files = [f for f in files if not samefile(f)]
2692 files = [f for f in files if not samefile(f)]
2660
2693
2661 def filectxfn(repo, ctx_, path):
2694 def filectxfn(repo, ctx_, path):
2662 try:
2695 try:
2663 fctx = ctx[path]
2696 fctx = ctx[path]
2664 flags = fctx.flags()
2697 flags = fctx.flags()
2665 mctx = context.memfilectx(repo,
2698 mctx = context.memfilectx(repo,
2666 fctx.path(), fctx.data(),
2699 fctx.path(), fctx.data(),
2667 islink='l' in flags,
2700 islink='l' in flags,
2668 isexec='x' in flags,
2701 isexec='x' in flags,
2669 copied=copied.get(path))
2702 copied=copied.get(path))
2670 return mctx
2703 return mctx
2671 except KeyError:
2704 except KeyError:
2672 return None
2705 return None
2673 else:
2706 else:
2674 ui.note(_('copying changeset %s to %s\n') % (old, base))
2707 ui.note(_('copying changeset %s to %s\n') % (old, base))
2675
2708
2676 # Use version of files as in the old cset
2709 # Use version of files as in the old cset
2677 def filectxfn(repo, ctx_, path):
2710 def filectxfn(repo, ctx_, path):
2678 try:
2711 try:
2679 return old.filectx(path)
2712 return old.filectx(path)
2680 except KeyError:
2713 except KeyError:
2681 return None
2714 return None
2682
2715
2683 user = opts.get('user') or old.user()
2716 user = opts.get('user') or old.user()
2684 date = opts.get('date') or old.date()
2717 date = opts.get('date') or old.date()
2685 editform = mergeeditform(old, 'commit.amend')
2718 editform = mergeeditform(old, 'commit.amend')
2686 editor = getcommiteditor(editform=editform, **opts)
2719 editor = getcommiteditor(editform=editform, **opts)
2687 if not message:
2720 if not message:
2688 editor = getcommiteditor(edit=True, editform=editform)
2721 editor = getcommiteditor(edit=True, editform=editform)
2689 message = old.description()
2722 message = old.description()
2690
2723
2691 pureextra = extra.copy()
2724 pureextra = extra.copy()
2692 extra['amend_source'] = old.hex()
2725 extra['amend_source'] = old.hex()
2693
2726
2694 new = context.memctx(repo,
2727 new = context.memctx(repo,
2695 parents=[base.node(), old.p2().node()],
2728 parents=[base.node(), old.p2().node()],
2696 text=message,
2729 text=message,
2697 files=files,
2730 files=files,
2698 filectxfn=filectxfn,
2731 filectxfn=filectxfn,
2699 user=user,
2732 user=user,
2700 date=date,
2733 date=date,
2701 extra=extra,
2734 extra=extra,
2702 editor=editor)
2735 editor=editor)
2703
2736
2704 newdesc = changelog.stripdesc(new.description())
2737 newdesc = changelog.stripdesc(new.description())
2705 if ((not node)
2738 if ((not node)
2706 and newdesc == old.description()
2739 and newdesc == old.description()
2707 and user == old.user()
2740 and user == old.user()
2708 and date == old.date()
2741 and date == old.date()
2709 and pureextra == old.extra()):
2742 and pureextra == old.extra()):
2710 # nothing changed. continuing here would create a new node
2743 # nothing changed. continuing here would create a new node
2711 # anyway because of the amend_source noise.
2744 # anyway because of the amend_source noise.
2712 #
2745 #
2713 # This not what we expect from amend.
2746 # This not what we expect from amend.
2714 return old.node()
2747 return old.node()
2715
2748
2716 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2749 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2717 try:
2750 try:
2718 if opts.get('secret'):
2751 if opts.get('secret'):
2719 commitphase = 'secret'
2752 commitphase = 'secret'
2720 else:
2753 else:
2721 commitphase = old.phase()
2754 commitphase = old.phase()
2722 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2755 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2723 newid = repo.commitctx(new)
2756 newid = repo.commitctx(new)
2724 finally:
2757 finally:
2725 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2758 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2726 if newid != old.node():
2759 if newid != old.node():
2727 # Reroute the working copy parent to the new changeset
2760 # Reroute the working copy parent to the new changeset
2728 repo.setparents(newid, nullid)
2761 repo.setparents(newid, nullid)
2729
2762
2730 # Move bookmarks from old parent to amend commit
2763 # Move bookmarks from old parent to amend commit
2731 bms = repo.nodebookmarks(old.node())
2764 bms = repo.nodebookmarks(old.node())
2732 if bms:
2765 if bms:
2733 marks = repo._bookmarks
2766 marks = repo._bookmarks
2734 for bm in bms:
2767 for bm in bms:
2735 ui.debug('moving bookmarks %r from %s to %s\n' %
2768 ui.debug('moving bookmarks %r from %s to %s\n' %
2736 (marks, old.hex(), hex(newid)))
2769 (marks, old.hex(), hex(newid)))
2737 marks[bm] = newid
2770 marks[bm] = newid
2738 marks.recordchange(tr)
2771 marks.recordchange(tr)
2739 #commit the whole amend process
2772 #commit the whole amend process
2740 if createmarkers:
2773 if createmarkers:
2741 # mark the new changeset as successor of the rewritten one
2774 # mark the new changeset as successor of the rewritten one
2742 new = repo[newid]
2775 new = repo[newid]
2743 obs = [(old, (new,))]
2776 obs = [(old, (new,))]
2744 if node:
2777 if node:
2745 obs.append((ctx, ()))
2778 obs.append((ctx, ()))
2746
2779
2747 obsolete.createmarkers(repo, obs)
2780 obsolete.createmarkers(repo, obs)
2748 if not createmarkers and newid != old.node():
2781 if not createmarkers and newid != old.node():
2749 # Strip the intermediate commit (if there was one) and the amended
2782 # Strip the intermediate commit (if there was one) and the amended
2750 # commit
2783 # commit
2751 if node:
2784 if node:
2752 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2785 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2753 ui.note(_('stripping amended changeset %s\n') % old)
2786 ui.note(_('stripping amended changeset %s\n') % old)
2754 repair.strip(ui, repo, old.node(), topic='amend-backup')
2787 repair.strip(ui, repo, old.node(), topic='amend-backup')
2755 finally:
2788 finally:
2756 lockmod.release(lock, wlock)
2789 lockmod.release(lock, wlock)
2757 return newid
2790 return newid
2758
2791
2759 def commiteditor(repo, ctx, subs, editform=''):
2792 def commiteditor(repo, ctx, subs, editform=''):
2760 if ctx.description():
2793 if ctx.description():
2761 return ctx.description()
2794 return ctx.description()
2762 return commitforceeditor(repo, ctx, subs, editform=editform,
2795 return commitforceeditor(repo, ctx, subs, editform=editform,
2763 unchangedmessagedetection=True)
2796 unchangedmessagedetection=True)
2764
2797
2765 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2798 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2766 editform='', unchangedmessagedetection=False):
2799 editform='', unchangedmessagedetection=False):
2767 if not extramsg:
2800 if not extramsg:
2768 extramsg = _("Leave message empty to abort commit.")
2801 extramsg = _("Leave message empty to abort commit.")
2769
2802
2770 forms = [e for e in editform.split('.') if e]
2803 forms = [e for e in editform.split('.') if e]
2771 forms.insert(0, 'changeset')
2804 forms.insert(0, 'changeset')
2772 templatetext = None
2805 templatetext = None
2773 while forms:
2806 while forms:
2774 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2807 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2775 if tmpl:
2808 if tmpl:
2776 templatetext = committext = buildcommittemplate(
2809 templatetext = committext = buildcommittemplate(
2777 repo, ctx, subs, extramsg, tmpl)
2810 repo, ctx, subs, extramsg, tmpl)
2778 break
2811 break
2779 forms.pop()
2812 forms.pop()
2780 else:
2813 else:
2781 committext = buildcommittext(repo, ctx, subs, extramsg)
2814 committext = buildcommittext(repo, ctx, subs, extramsg)
2782
2815
2783 # run editor in the repository root
2816 # run editor in the repository root
2784 olddir = os.getcwd()
2817 olddir = os.getcwd()
2785 os.chdir(repo.root)
2818 os.chdir(repo.root)
2786
2819
2787 # make in-memory changes visible to external process
2820 # make in-memory changes visible to external process
2788 tr = repo.currenttransaction()
2821 tr = repo.currenttransaction()
2789 repo.dirstate.write(tr)
2822 repo.dirstate.write(tr)
2790 pending = tr and tr.writepending() and repo.root
2823 pending = tr and tr.writepending() and repo.root
2791
2824
2792 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
2825 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
2793 editform=editform, pending=pending)
2826 editform=editform, pending=pending)
2794 text = re.sub("(?m)^HG:.*(\n|$)", "", editortext)
2827 text = re.sub("(?m)^HG:.*(\n|$)", "", editortext)
2795 os.chdir(olddir)
2828 os.chdir(olddir)
2796
2829
2797 if finishdesc:
2830 if finishdesc:
2798 text = finishdesc(text)
2831 text = finishdesc(text)
2799 if not text.strip():
2832 if not text.strip():
2800 raise error.Abort(_("empty commit message"))
2833 raise error.Abort(_("empty commit message"))
2801 if unchangedmessagedetection and editortext == templatetext:
2834 if unchangedmessagedetection and editortext == templatetext:
2802 raise error.Abort(_("commit message unchanged"))
2835 raise error.Abort(_("commit message unchanged"))
2803
2836
2804 return text
2837 return text
2805
2838
2806 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2839 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2807 ui = repo.ui
2840 ui = repo.ui
2808 tmpl, mapfile = gettemplate(ui, tmpl, None)
2841 tmpl, mapfile = gettemplate(ui, tmpl, None)
2809
2842
2810 try:
2843 try:
2811 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2844 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2812 except SyntaxError as inst:
2845 except SyntaxError as inst:
2813 raise error.Abort(inst.args[0])
2846 raise error.Abort(inst.args[0])
2814
2847
2815 for k, v in repo.ui.configitems('committemplate'):
2848 for k, v in repo.ui.configitems('committemplate'):
2816 if k != 'changeset':
2849 if k != 'changeset':
2817 t.t.cache[k] = v
2850 t.t.cache[k] = v
2818
2851
2819 if not extramsg:
2852 if not extramsg:
2820 extramsg = '' # ensure that extramsg is string
2853 extramsg = '' # ensure that extramsg is string
2821
2854
2822 ui.pushbuffer()
2855 ui.pushbuffer()
2823 t.show(ctx, extramsg=extramsg)
2856 t.show(ctx, extramsg=extramsg)
2824 return ui.popbuffer()
2857 return ui.popbuffer()
2825
2858
2826 def hgprefix(msg):
2859 def hgprefix(msg):
2827 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
2860 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
2828
2861
2829 def buildcommittext(repo, ctx, subs, extramsg):
2862 def buildcommittext(repo, ctx, subs, extramsg):
2830 edittext = []
2863 edittext = []
2831 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2864 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2832 if ctx.description():
2865 if ctx.description():
2833 edittext.append(ctx.description())
2866 edittext.append(ctx.description())
2834 edittext.append("")
2867 edittext.append("")
2835 edittext.append("") # Empty line between message and comments.
2868 edittext.append("") # Empty line between message and comments.
2836 edittext.append(hgprefix(_("Enter commit message."
2869 edittext.append(hgprefix(_("Enter commit message."
2837 " Lines beginning with 'HG:' are removed.")))
2870 " Lines beginning with 'HG:' are removed.")))
2838 edittext.append(hgprefix(extramsg))
2871 edittext.append(hgprefix(extramsg))
2839 edittext.append("HG: --")
2872 edittext.append("HG: --")
2840 edittext.append(hgprefix(_("user: %s") % ctx.user()))
2873 edittext.append(hgprefix(_("user: %s") % ctx.user()))
2841 if ctx.p2():
2874 if ctx.p2():
2842 edittext.append(hgprefix(_("branch merge")))
2875 edittext.append(hgprefix(_("branch merge")))
2843 if ctx.branch():
2876 if ctx.branch():
2844 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
2877 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
2845 if bookmarks.isactivewdirparent(repo):
2878 if bookmarks.isactivewdirparent(repo):
2846 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
2879 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
2847 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
2880 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
2848 edittext.extend([hgprefix(_("added %s") % f) for f in added])
2881 edittext.extend([hgprefix(_("added %s") % f) for f in added])
2849 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
2882 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
2850 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
2883 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
2851 if not added and not modified and not removed:
2884 if not added and not modified and not removed:
2852 edittext.append(hgprefix(_("no files changed")))
2885 edittext.append(hgprefix(_("no files changed")))
2853 edittext.append("")
2886 edittext.append("")
2854
2887
2855 return "\n".join(edittext)
2888 return "\n".join(edittext)
2856
2889
2857 def commitstatus(repo, node, branch, bheads=None, opts=None):
2890 def commitstatus(repo, node, branch, bheads=None, opts=None):
2858 if opts is None:
2891 if opts is None:
2859 opts = {}
2892 opts = {}
2860 ctx = repo[node]
2893 ctx = repo[node]
2861 parents = ctx.parents()
2894 parents = ctx.parents()
2862
2895
2863 if (not opts.get('amend') and bheads and node not in bheads and not
2896 if (not opts.get('amend') and bheads and node not in bheads and not
2864 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2897 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2865 repo.ui.status(_('created new head\n'))
2898 repo.ui.status(_('created new head\n'))
2866 # The message is not printed for initial roots. For the other
2899 # The message is not printed for initial roots. For the other
2867 # changesets, it is printed in the following situations:
2900 # changesets, it is printed in the following situations:
2868 #
2901 #
2869 # Par column: for the 2 parents with ...
2902 # Par column: for the 2 parents with ...
2870 # N: null or no parent
2903 # N: null or no parent
2871 # B: parent is on another named branch
2904 # B: parent is on another named branch
2872 # C: parent is a regular non head changeset
2905 # C: parent is a regular non head changeset
2873 # H: parent was a branch head of the current branch
2906 # H: parent was a branch head of the current branch
2874 # Msg column: whether we print "created new head" message
2907 # Msg column: whether we print "created new head" message
2875 # In the following, it is assumed that there already exists some
2908 # In the following, it is assumed that there already exists some
2876 # initial branch heads of the current branch, otherwise nothing is
2909 # initial branch heads of the current branch, otherwise nothing is
2877 # printed anyway.
2910 # printed anyway.
2878 #
2911 #
2879 # Par Msg Comment
2912 # Par Msg Comment
2880 # N N y additional topo root
2913 # N N y additional topo root
2881 #
2914 #
2882 # B N y additional branch root
2915 # B N y additional branch root
2883 # C N y additional topo head
2916 # C N y additional topo head
2884 # H N n usual case
2917 # H N n usual case
2885 #
2918 #
2886 # B B y weird additional branch root
2919 # B B y weird additional branch root
2887 # C B y branch merge
2920 # C B y branch merge
2888 # H B n merge with named branch
2921 # H B n merge with named branch
2889 #
2922 #
2890 # C C y additional head from merge
2923 # C C y additional head from merge
2891 # C H n merge with a head
2924 # C H n merge with a head
2892 #
2925 #
2893 # H H n head merge: head count decreases
2926 # H H n head merge: head count decreases
2894
2927
2895 if not opts.get('close_branch'):
2928 if not opts.get('close_branch'):
2896 for r in parents:
2929 for r in parents:
2897 if r.closesbranch() and r.branch() == branch:
2930 if r.closesbranch() and r.branch() == branch:
2898 repo.ui.status(_('reopening closed branch head %d\n') % r)
2931 repo.ui.status(_('reopening closed branch head %d\n') % r)
2899
2932
2900 if repo.ui.debugflag:
2933 if repo.ui.debugflag:
2901 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2934 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2902 elif repo.ui.verbose:
2935 elif repo.ui.verbose:
2903 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2936 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2904
2937
2905 def postcommitstatus(repo, pats, opts):
2938 def postcommitstatus(repo, pats, opts):
2906 return repo.status(match=scmutil.match(repo[None], pats, opts))
2939 return repo.status(match=scmutil.match(repo[None], pats, opts))
2907
2940
2908 def revert(ui, repo, ctx, parents, *pats, **opts):
2941 def revert(ui, repo, ctx, parents, *pats, **opts):
2909 parent, p2 = parents
2942 parent, p2 = parents
2910 node = ctx.node()
2943 node = ctx.node()
2911
2944
2912 mf = ctx.manifest()
2945 mf = ctx.manifest()
2913 if node == p2:
2946 if node == p2:
2914 parent = p2
2947 parent = p2
2915
2948
2916 # need all matching names in dirstate and manifest of target rev,
2949 # need all matching names in dirstate and manifest of target rev,
2917 # so have to walk both. do not print errors if files exist in one
2950 # so have to walk both. do not print errors if files exist in one
2918 # but not other. in both cases, filesets should be evaluated against
2951 # but not other. in both cases, filesets should be evaluated against
2919 # workingctx to get consistent result (issue4497). this means 'set:**'
2952 # workingctx to get consistent result (issue4497). this means 'set:**'
2920 # cannot be used to select missing files from target rev.
2953 # cannot be used to select missing files from target rev.
2921
2954
2922 # `names` is a mapping for all elements in working copy and target revision
2955 # `names` is a mapping for all elements in working copy and target revision
2923 # The mapping is in the form:
2956 # The mapping is in the form:
2924 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2957 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2925 names = {}
2958 names = {}
2926
2959
2927 with repo.wlock():
2960 with repo.wlock():
2928 ## filling of the `names` mapping
2961 ## filling of the `names` mapping
2929 # walk dirstate to fill `names`
2962 # walk dirstate to fill `names`
2930
2963
2931 interactive = opts.get('interactive', False)
2964 interactive = opts.get('interactive', False)
2932 wctx = repo[None]
2965 wctx = repo[None]
2933 m = scmutil.match(wctx, pats, opts)
2966 m = scmutil.match(wctx, pats, opts)
2934
2967
2935 # we'll need this later
2968 # we'll need this later
2936 targetsubs = sorted(s for s in wctx.substate if m(s))
2969 targetsubs = sorted(s for s in wctx.substate if m(s))
2937
2970
2938 if not m.always():
2971 if not m.always():
2939 for abs in repo.walk(matchmod.badmatch(m, lambda x, y: False)):
2972 for abs in repo.walk(matchmod.badmatch(m, lambda x, y: False)):
2940 names[abs] = m.rel(abs), m.exact(abs)
2973 names[abs] = m.rel(abs), m.exact(abs)
2941
2974
2942 # walk target manifest to fill `names`
2975 # walk target manifest to fill `names`
2943
2976
2944 def badfn(path, msg):
2977 def badfn(path, msg):
2945 if path in names:
2978 if path in names:
2946 return
2979 return
2947 if path in ctx.substate:
2980 if path in ctx.substate:
2948 return
2981 return
2949 path_ = path + '/'
2982 path_ = path + '/'
2950 for f in names:
2983 for f in names:
2951 if f.startswith(path_):
2984 if f.startswith(path_):
2952 return
2985 return
2953 ui.warn("%s: %s\n" % (m.rel(path), msg))
2986 ui.warn("%s: %s\n" % (m.rel(path), msg))
2954
2987
2955 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
2988 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
2956 if abs not in names:
2989 if abs not in names:
2957 names[abs] = m.rel(abs), m.exact(abs)
2990 names[abs] = m.rel(abs), m.exact(abs)
2958
2991
2959 # Find status of all file in `names`.
2992 # Find status of all file in `names`.
2960 m = scmutil.matchfiles(repo, names)
2993 m = scmutil.matchfiles(repo, names)
2961
2994
2962 changes = repo.status(node1=node, match=m,
2995 changes = repo.status(node1=node, match=m,
2963 unknown=True, ignored=True, clean=True)
2996 unknown=True, ignored=True, clean=True)
2964 else:
2997 else:
2965 changes = repo.status(node1=node, match=m)
2998 changes = repo.status(node1=node, match=m)
2966 for kind in changes:
2999 for kind in changes:
2967 for abs in kind:
3000 for abs in kind:
2968 names[abs] = m.rel(abs), m.exact(abs)
3001 names[abs] = m.rel(abs), m.exact(abs)
2969
3002
2970 m = scmutil.matchfiles(repo, names)
3003 m = scmutil.matchfiles(repo, names)
2971
3004
2972 modified = set(changes.modified)
3005 modified = set(changes.modified)
2973 added = set(changes.added)
3006 added = set(changes.added)
2974 removed = set(changes.removed)
3007 removed = set(changes.removed)
2975 _deleted = set(changes.deleted)
3008 _deleted = set(changes.deleted)
2976 unknown = set(changes.unknown)
3009 unknown = set(changes.unknown)
2977 unknown.update(changes.ignored)
3010 unknown.update(changes.ignored)
2978 clean = set(changes.clean)
3011 clean = set(changes.clean)
2979 modadded = set()
3012 modadded = set()
2980
3013
2981 # split between files known in target manifest and the others
3014 # split between files known in target manifest and the others
2982 smf = set(mf)
3015 smf = set(mf)
2983
3016
2984 # determine the exact nature of the deleted changesets
3017 # determine the exact nature of the deleted changesets
2985 deladded = _deleted - smf
3018 deladded = _deleted - smf
2986 deleted = _deleted - deladded
3019 deleted = _deleted - deladded
2987
3020
2988 # We need to account for the state of the file in the dirstate,
3021 # We need to account for the state of the file in the dirstate,
2989 # even when we revert against something else than parent. This will
3022 # even when we revert against something else than parent. This will
2990 # slightly alter the behavior of revert (doing back up or not, delete
3023 # slightly alter the behavior of revert (doing back up or not, delete
2991 # or just forget etc).
3024 # or just forget etc).
2992 if parent == node:
3025 if parent == node:
2993 dsmodified = modified
3026 dsmodified = modified
2994 dsadded = added
3027 dsadded = added
2995 dsremoved = removed
3028 dsremoved = removed
2996 # store all local modifications, useful later for rename detection
3029 # store all local modifications, useful later for rename detection
2997 localchanges = dsmodified | dsadded
3030 localchanges = dsmodified | dsadded
2998 modified, added, removed = set(), set(), set()
3031 modified, added, removed = set(), set(), set()
2999 else:
3032 else:
3000 changes = repo.status(node1=parent, match=m)
3033 changes = repo.status(node1=parent, match=m)
3001 dsmodified = set(changes.modified)
3034 dsmodified = set(changes.modified)
3002 dsadded = set(changes.added)
3035 dsadded = set(changes.added)
3003 dsremoved = set(changes.removed)
3036 dsremoved = set(changes.removed)
3004 # store all local modifications, useful later for rename detection
3037 # store all local modifications, useful later for rename detection
3005 localchanges = dsmodified | dsadded
3038 localchanges = dsmodified | dsadded
3006
3039
3007 # only take into account for removes between wc and target
3040 # only take into account for removes between wc and target
3008 clean |= dsremoved - removed
3041 clean |= dsremoved - removed
3009 dsremoved &= removed
3042 dsremoved &= removed
3010 # distinct between dirstate remove and other
3043 # distinct between dirstate remove and other
3011 removed -= dsremoved
3044 removed -= dsremoved
3012
3045
3013 modadded = added & dsmodified
3046 modadded = added & dsmodified
3014 added -= modadded
3047 added -= modadded
3015
3048
3016 # tell newly modified apart.
3049 # tell newly modified apart.
3017 dsmodified &= modified
3050 dsmodified &= modified
3018 dsmodified |= modified & dsadded # dirstate added may needs backup
3051 dsmodified |= modified & dsadded # dirstate added may needs backup
3019 modified -= dsmodified
3052 modified -= dsmodified
3020
3053
3021 # We need to wait for some post-processing to update this set
3054 # We need to wait for some post-processing to update this set
3022 # before making the distinction. The dirstate will be used for
3055 # before making the distinction. The dirstate will be used for
3023 # that purpose.
3056 # that purpose.
3024 dsadded = added
3057 dsadded = added
3025
3058
3026 # in case of merge, files that are actually added can be reported as
3059 # in case of merge, files that are actually added can be reported as
3027 # modified, we need to post process the result
3060 # modified, we need to post process the result
3028 if p2 != nullid:
3061 if p2 != nullid:
3029 mergeadd = dsmodified - smf
3062 mergeadd = dsmodified - smf
3030 dsadded |= mergeadd
3063 dsadded |= mergeadd
3031 dsmodified -= mergeadd
3064 dsmodified -= mergeadd
3032
3065
3033 # if f is a rename, update `names` to also revert the source
3066 # if f is a rename, update `names` to also revert the source
3034 cwd = repo.getcwd()
3067 cwd = repo.getcwd()
3035 for f in localchanges:
3068 for f in localchanges:
3036 src = repo.dirstate.copied(f)
3069 src = repo.dirstate.copied(f)
3037 # XXX should we check for rename down to target node?
3070 # XXX should we check for rename down to target node?
3038 if src and src not in names and repo.dirstate[src] == 'r':
3071 if src and src not in names and repo.dirstate[src] == 'r':
3039 dsremoved.add(src)
3072 dsremoved.add(src)
3040 names[src] = (repo.pathto(src, cwd), True)
3073 names[src] = (repo.pathto(src, cwd), True)
3041
3074
3042 # distinguish between file to forget and the other
3075 # distinguish between file to forget and the other
3043 added = set()
3076 added = set()
3044 for abs in dsadded:
3077 for abs in dsadded:
3045 if repo.dirstate[abs] != 'a':
3078 if repo.dirstate[abs] != 'a':
3046 added.add(abs)
3079 added.add(abs)
3047 dsadded -= added
3080 dsadded -= added
3048
3081
3049 for abs in deladded:
3082 for abs in deladded:
3050 if repo.dirstate[abs] == 'a':
3083 if repo.dirstate[abs] == 'a':
3051 dsadded.add(abs)
3084 dsadded.add(abs)
3052 deladded -= dsadded
3085 deladded -= dsadded
3053
3086
3054 # For files marked as removed, we check if an unknown file is present at
3087 # For files marked as removed, we check if an unknown file is present at
3055 # the same path. If a such file exists it may need to be backed up.
3088 # the same path. If a such file exists it may need to be backed up.
3056 # Making the distinction at this stage helps have simpler backup
3089 # Making the distinction at this stage helps have simpler backup
3057 # logic.
3090 # logic.
3058 removunk = set()
3091 removunk = set()
3059 for abs in removed:
3092 for abs in removed:
3060 target = repo.wjoin(abs)
3093 target = repo.wjoin(abs)
3061 if os.path.lexists(target):
3094 if os.path.lexists(target):
3062 removunk.add(abs)
3095 removunk.add(abs)
3063 removed -= removunk
3096 removed -= removunk
3064
3097
3065 dsremovunk = set()
3098 dsremovunk = set()
3066 for abs in dsremoved:
3099 for abs in dsremoved:
3067 target = repo.wjoin(abs)
3100 target = repo.wjoin(abs)
3068 if os.path.lexists(target):
3101 if os.path.lexists(target):
3069 dsremovunk.add(abs)
3102 dsremovunk.add(abs)
3070 dsremoved -= dsremovunk
3103 dsremoved -= dsremovunk
3071
3104
3072 # action to be actually performed by revert
3105 # action to be actually performed by revert
3073 # (<list of file>, message>) tuple
3106 # (<list of file>, message>) tuple
3074 actions = {'revert': ([], _('reverting %s\n')),
3107 actions = {'revert': ([], _('reverting %s\n')),
3075 'add': ([], _('adding %s\n')),
3108 'add': ([], _('adding %s\n')),
3076 'remove': ([], _('removing %s\n')),
3109 'remove': ([], _('removing %s\n')),
3077 'drop': ([], _('removing %s\n')),
3110 'drop': ([], _('removing %s\n')),
3078 'forget': ([], _('forgetting %s\n')),
3111 'forget': ([], _('forgetting %s\n')),
3079 'undelete': ([], _('undeleting %s\n')),
3112 'undelete': ([], _('undeleting %s\n')),
3080 'noop': (None, _('no changes needed to %s\n')),
3113 'noop': (None, _('no changes needed to %s\n')),
3081 'unknown': (None, _('file not managed: %s\n')),
3114 'unknown': (None, _('file not managed: %s\n')),
3082 }
3115 }
3083
3116
3084 # "constant" that convey the backup strategy.
3117 # "constant" that convey the backup strategy.
3085 # All set to `discard` if `no-backup` is set do avoid checking
3118 # All set to `discard` if `no-backup` is set do avoid checking
3086 # no_backup lower in the code.
3119 # no_backup lower in the code.
3087 # These values are ordered for comparison purposes
3120 # These values are ordered for comparison purposes
3088 backup = 2 # unconditionally do backup
3121 backup = 2 # unconditionally do backup
3089 check = 1 # check if the existing file differs from target
3122 check = 1 # check if the existing file differs from target
3090 discard = 0 # never do backup
3123 discard = 0 # never do backup
3091 if opts.get('no_backup'):
3124 if opts.get('no_backup'):
3092 backup = check = discard
3125 backup = check = discard
3093
3126
3094 backupanddel = actions['remove']
3127 backupanddel = actions['remove']
3095 if not opts.get('no_backup'):
3128 if not opts.get('no_backup'):
3096 backupanddel = actions['drop']
3129 backupanddel = actions['drop']
3097
3130
3098 disptable = (
3131 disptable = (
3099 # dispatch table:
3132 # dispatch table:
3100 # file state
3133 # file state
3101 # action
3134 # action
3102 # make backup
3135 # make backup
3103
3136
3104 ## Sets that results that will change file on disk
3137 ## Sets that results that will change file on disk
3105 # Modified compared to target, no local change
3138 # Modified compared to target, no local change
3106 (modified, actions['revert'], discard),
3139 (modified, actions['revert'], discard),
3107 # Modified compared to target, but local file is deleted
3140 # Modified compared to target, but local file is deleted
3108 (deleted, actions['revert'], discard),
3141 (deleted, actions['revert'], discard),
3109 # Modified compared to target, local change
3142 # Modified compared to target, local change
3110 (dsmodified, actions['revert'], backup),
3143 (dsmodified, actions['revert'], backup),
3111 # Added since target
3144 # Added since target
3112 (added, actions['remove'], discard),
3145 (added, actions['remove'], discard),
3113 # Added in working directory
3146 # Added in working directory
3114 (dsadded, actions['forget'], discard),
3147 (dsadded, actions['forget'], discard),
3115 # Added since target, have local modification
3148 # Added since target, have local modification
3116 (modadded, backupanddel, backup),
3149 (modadded, backupanddel, backup),
3117 # Added since target but file is missing in working directory
3150 # Added since target but file is missing in working directory
3118 (deladded, actions['drop'], discard),
3151 (deladded, actions['drop'], discard),
3119 # Removed since target, before working copy parent
3152 # Removed since target, before working copy parent
3120 (removed, actions['add'], discard),
3153 (removed, actions['add'], discard),
3121 # Same as `removed` but an unknown file exists at the same path
3154 # Same as `removed` but an unknown file exists at the same path
3122 (removunk, actions['add'], check),
3155 (removunk, actions['add'], check),
3123 # Removed since targe, marked as such in working copy parent
3156 # Removed since targe, marked as such in working copy parent
3124 (dsremoved, actions['undelete'], discard),
3157 (dsremoved, actions['undelete'], discard),
3125 # Same as `dsremoved` but an unknown file exists at the same path
3158 # Same as `dsremoved` but an unknown file exists at the same path
3126 (dsremovunk, actions['undelete'], check),
3159 (dsremovunk, actions['undelete'], check),
3127 ## the following sets does not result in any file changes
3160 ## the following sets does not result in any file changes
3128 # File with no modification
3161 # File with no modification
3129 (clean, actions['noop'], discard),
3162 (clean, actions['noop'], discard),
3130 # Existing file, not tracked anywhere
3163 # Existing file, not tracked anywhere
3131 (unknown, actions['unknown'], discard),
3164 (unknown, actions['unknown'], discard),
3132 )
3165 )
3133
3166
3134 for abs, (rel, exact) in sorted(names.items()):
3167 for abs, (rel, exact) in sorted(names.items()):
3135 # target file to be touch on disk (relative to cwd)
3168 # target file to be touch on disk (relative to cwd)
3136 target = repo.wjoin(abs)
3169 target = repo.wjoin(abs)
3137 # search the entry in the dispatch table.
3170 # search the entry in the dispatch table.
3138 # if the file is in any of these sets, it was touched in the working
3171 # 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.
3172 # directory parent and we are sure it needs to be reverted.
3140 for table, (xlist, msg), dobackup in disptable:
3173 for table, (xlist, msg), dobackup in disptable:
3141 if abs not in table:
3174 if abs not in table:
3142 continue
3175 continue
3143 if xlist is not None:
3176 if xlist is not None:
3144 xlist.append(abs)
3177 xlist.append(abs)
3145 if dobackup and (backup <= dobackup
3178 if dobackup and (backup <= dobackup
3146 or wctx[abs].cmp(ctx[abs])):
3179 or wctx[abs].cmp(ctx[abs])):
3147 bakname = scmutil.origpath(ui, repo, rel)
3180 bakname = scmutil.origpath(ui, repo, rel)
3148 ui.note(_('saving current version of %s as %s\n') %
3181 ui.note(_('saving current version of %s as %s\n') %
3149 (rel, bakname))
3182 (rel, bakname))
3150 if not opts.get('dry_run'):
3183 if not opts.get('dry_run'):
3151 if interactive:
3184 if interactive:
3152 util.copyfile(target, bakname)
3185 util.copyfile(target, bakname)
3153 else:
3186 else:
3154 util.rename(target, bakname)
3187 util.rename(target, bakname)
3155 if ui.verbose or not exact:
3188 if ui.verbose or not exact:
3156 if not isinstance(msg, basestring):
3189 if not isinstance(msg, basestring):
3157 msg = msg(abs)
3190 msg = msg(abs)
3158 ui.status(msg % rel)
3191 ui.status(msg % rel)
3159 elif exact:
3192 elif exact:
3160 ui.warn(msg % rel)
3193 ui.warn(msg % rel)
3161 break
3194 break
3162
3195
3163 if not opts.get('dry_run'):
3196 if not opts.get('dry_run'):
3164 needdata = ('revert', 'add', 'undelete')
3197 needdata = ('revert', 'add', 'undelete')
3165 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3198 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3166 _performrevert(repo, parents, ctx, actions, interactive)
3199 _performrevert(repo, parents, ctx, actions, interactive)
3167
3200
3168 if targetsubs:
3201 if targetsubs:
3169 # Revert the subrepos on the revert list
3202 # Revert the subrepos on the revert list
3170 for sub in targetsubs:
3203 for sub in targetsubs:
3171 try:
3204 try:
3172 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3205 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3173 except KeyError:
3206 except KeyError:
3174 raise error.Abort("subrepository '%s' does not exist in %s!"
3207 raise error.Abort("subrepository '%s' does not exist in %s!"
3175 % (sub, short(ctx.node())))
3208 % (sub, short(ctx.node())))
3176
3209
3177 def _revertprefetch(repo, ctx, *files):
3210 def _revertprefetch(repo, ctx, *files):
3178 """Let extension changing the storage layer prefetch content"""
3211 """Let extension changing the storage layer prefetch content"""
3179 pass
3212 pass
3180
3213
3181 def _performrevert(repo, parents, ctx, actions, interactive=False):
3214 def _performrevert(repo, parents, ctx, actions, interactive=False):
3182 """function that actually perform all the actions computed for revert
3215 """function that actually perform all the actions computed for revert
3183
3216
3184 This is an independent function to let extension to plug in and react to
3217 This is an independent function to let extension to plug in and react to
3185 the imminent revert.
3218 the imminent revert.
3186
3219
3187 Make sure you have the working directory locked when calling this function.
3220 Make sure you have the working directory locked when calling this function.
3188 """
3221 """
3189 parent, p2 = parents
3222 parent, p2 = parents
3190 node = ctx.node()
3223 node = ctx.node()
3191 excluded_files = []
3224 excluded_files = []
3192 matcher_opts = {"exclude": excluded_files}
3225 matcher_opts = {"exclude": excluded_files}
3193
3226
3194 def checkout(f):
3227 def checkout(f):
3195 fc = ctx[f]
3228 fc = ctx[f]
3196 repo.wwrite(f, fc.data(), fc.flags())
3229 repo.wwrite(f, fc.data(), fc.flags())
3197
3230
3198 audit_path = pathutil.pathauditor(repo.root)
3231 audit_path = pathutil.pathauditor(repo.root)
3199 for f in actions['forget'][0]:
3232 for f in actions['forget'][0]:
3200 if interactive:
3233 if interactive:
3201 choice = \
3234 choice = \
3202 repo.ui.promptchoice(
3235 repo.ui.promptchoice(
3203 _("forget added file %s (yn)?$$ &Yes $$ &No")
3236 _("forget added file %s (yn)?$$ &Yes $$ &No")
3204 % f)
3237 % f)
3205 if choice == 0:
3238 if choice == 0:
3206 repo.dirstate.drop(f)
3239 repo.dirstate.drop(f)
3207 else:
3240 else:
3208 excluded_files.append(repo.wjoin(f))
3241 excluded_files.append(repo.wjoin(f))
3209 else:
3242 else:
3210 repo.dirstate.drop(f)
3243 repo.dirstate.drop(f)
3211 for f in actions['remove'][0]:
3244 for f in actions['remove'][0]:
3212 audit_path(f)
3245 audit_path(f)
3213 try:
3246 try:
3214 util.unlinkpath(repo.wjoin(f))
3247 util.unlinkpath(repo.wjoin(f))
3215 except OSError:
3248 except OSError:
3216 pass
3249 pass
3217 repo.dirstate.remove(f)
3250 repo.dirstate.remove(f)
3218 for f in actions['drop'][0]:
3251 for f in actions['drop'][0]:
3219 audit_path(f)
3252 audit_path(f)
3220 repo.dirstate.remove(f)
3253 repo.dirstate.remove(f)
3221
3254
3222 normal = None
3255 normal = None
3223 if node == parent:
3256 if node == parent:
3224 # We're reverting to our parent. If possible, we'd like status
3257 # We're reverting to our parent. If possible, we'd like status
3225 # to report the file as clean. We have to use normallookup for
3258 # to report the file as clean. We have to use normallookup for
3226 # merges to avoid losing information about merged/dirty files.
3259 # merges to avoid losing information about merged/dirty files.
3227 if p2 != nullid:
3260 if p2 != nullid:
3228 normal = repo.dirstate.normallookup
3261 normal = repo.dirstate.normallookup
3229 else:
3262 else:
3230 normal = repo.dirstate.normal
3263 normal = repo.dirstate.normal
3231
3264
3232 newlyaddedandmodifiedfiles = set()
3265 newlyaddedandmodifiedfiles = set()
3233 if interactive:
3266 if interactive:
3234 # Prompt the user for changes to revert
3267 # Prompt the user for changes to revert
3235 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3268 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3236 m = scmutil.match(ctx, torevert, matcher_opts)
3269 m = scmutil.match(ctx, torevert, matcher_opts)
3237 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3270 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3238 diffopts.nodates = True
3271 diffopts.nodates = True
3239 diffopts.git = True
3272 diffopts.git = True
3240 reversehunks = repo.ui.configbool('experimental',
3273 reversehunks = repo.ui.configbool('experimental',
3241 'revertalternateinteractivemode',
3274 'revertalternateinteractivemode',
3242 True)
3275 True)
3243 if reversehunks:
3276 if reversehunks:
3244 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3277 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3245 else:
3278 else:
3246 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3279 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3247 originalchunks = patch.parsepatch(diff)
3280 originalchunks = patch.parsepatch(diff)
3248
3281
3249 try:
3282 try:
3250
3283
3251 chunks, opts = recordfilter(repo.ui, originalchunks)
3284 chunks, opts = recordfilter(repo.ui, originalchunks)
3252 if reversehunks:
3285 if reversehunks:
3253 chunks = patch.reversehunks(chunks)
3286 chunks = patch.reversehunks(chunks)
3254
3287
3255 except patch.PatchError as err:
3288 except patch.PatchError as err:
3256 raise error.Abort(_('error parsing patch: %s') % err)
3289 raise error.Abort(_('error parsing patch: %s') % err)
3257
3290
3258 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3291 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3259 # Apply changes
3292 # Apply changes
3260 fp = cStringIO.StringIO()
3293 fp = cStringIO.StringIO()
3261 for c in chunks:
3294 for c in chunks:
3262 c.write(fp)
3295 c.write(fp)
3263 dopatch = fp.tell()
3296 dopatch = fp.tell()
3264 fp.seek(0)
3297 fp.seek(0)
3265 if dopatch:
3298 if dopatch:
3266 try:
3299 try:
3267 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3300 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3268 except patch.PatchError as err:
3301 except patch.PatchError as err:
3269 raise error.Abort(str(err))
3302 raise error.Abort(str(err))
3270 del fp
3303 del fp
3271 else:
3304 else:
3272 for f in actions['revert'][0]:
3305 for f in actions['revert'][0]:
3273 checkout(f)
3306 checkout(f)
3274 if normal:
3307 if normal:
3275 normal(f)
3308 normal(f)
3276
3309
3277 for f in actions['add'][0]:
3310 for f in actions['add'][0]:
3278 # Don't checkout modified files, they are already created by the diff
3311 # Don't checkout modified files, they are already created by the diff
3279 if f not in newlyaddedandmodifiedfiles:
3312 if f not in newlyaddedandmodifiedfiles:
3280 checkout(f)
3313 checkout(f)
3281 repo.dirstate.add(f)
3314 repo.dirstate.add(f)
3282
3315
3283 normal = repo.dirstate.normallookup
3316 normal = repo.dirstate.normallookup
3284 if node == parent and p2 == nullid:
3317 if node == parent and p2 == nullid:
3285 normal = repo.dirstate.normal
3318 normal = repo.dirstate.normal
3286 for f in actions['undelete'][0]:
3319 for f in actions['undelete'][0]:
3287 checkout(f)
3320 checkout(f)
3288 normal(f)
3321 normal(f)
3289
3322
3290 copied = copies.pathcopies(repo[parent], ctx)
3323 copied = copies.pathcopies(repo[parent], ctx)
3291
3324
3292 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3325 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3293 if f in copied:
3326 if f in copied:
3294 repo.dirstate.copy(copied[f], f)
3327 repo.dirstate.copy(copied[f], f)
3295
3328
3296 def command(table):
3329 def command(table):
3297 """Returns a function object to be used as a decorator for making commands.
3330 """Returns a function object to be used as a decorator for making commands.
3298
3331
3299 This function receives a command table as its argument. The table should
3332 This function receives a command table as its argument. The table should
3300 be a dict.
3333 be a dict.
3301
3334
3302 The returned function can be used as a decorator for adding commands
3335 The returned function can be used as a decorator for adding commands
3303 to that command table. This function accepts multiple arguments to define
3336 to that command table. This function accepts multiple arguments to define
3304 a command.
3337 a command.
3305
3338
3306 The first argument is the command name.
3339 The first argument is the command name.
3307
3340
3308 The options argument is an iterable of tuples defining command arguments.
3341 The options argument is an iterable of tuples defining command arguments.
3309 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
3342 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
3310
3343
3311 The synopsis argument defines a short, one line summary of how to use the
3344 The synopsis argument defines a short, one line summary of how to use the
3312 command. This shows up in the help output.
3345 command. This shows up in the help output.
3313
3346
3314 The norepo argument defines whether the command does not require a
3347 The norepo argument defines whether the command does not require a
3315 local repository. Most commands operate against a repository, thus the
3348 local repository. Most commands operate against a repository, thus the
3316 default is False.
3349 default is False.
3317
3350
3318 The optionalrepo argument defines whether the command optionally requires
3351 The optionalrepo argument defines whether the command optionally requires
3319 a local repository.
3352 a local repository.
3320
3353
3321 The inferrepo argument defines whether to try to find a repository from the
3354 The inferrepo argument defines whether to try to find a repository from the
3322 command line arguments. If True, arguments will be examined for potential
3355 command line arguments. If True, arguments will be examined for potential
3323 repository locations. See ``findrepo()``. If a repository is found, it
3356 repository locations. See ``findrepo()``. If a repository is found, it
3324 will be used.
3357 will be used.
3325 """
3358 """
3326 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
3359 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
3327 inferrepo=False):
3360 inferrepo=False):
3328 def decorator(func):
3361 def decorator(func):
3329 func.norepo = norepo
3362 func.norepo = norepo
3330 func.optionalrepo = optionalrepo
3363 func.optionalrepo = optionalrepo
3331 func.inferrepo = inferrepo
3364 func.inferrepo = inferrepo
3332 if synopsis:
3365 if synopsis:
3333 table[name] = func, list(options), synopsis
3366 table[name] = func, list(options), synopsis
3334 else:
3367 else:
3335 table[name] = func, list(options)
3368 table[name] = func, list(options)
3336 return func
3369 return func
3337 return decorator
3370 return decorator
3338
3371
3339 return cmd
3372 return cmd
3340
3373
3341 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3374 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3342 # commands.outgoing. "missing" is "missing" of the result of
3375 # commands.outgoing. "missing" is "missing" of the result of
3343 # "findcommonoutgoing()"
3376 # "findcommonoutgoing()"
3344 outgoinghooks = util.hooks()
3377 outgoinghooks = util.hooks()
3345
3378
3346 # a list of (ui, repo) functions called by commands.summary
3379 # a list of (ui, repo) functions called by commands.summary
3347 summaryhooks = util.hooks()
3380 summaryhooks = util.hooks()
3348
3381
3349 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3382 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3350 #
3383 #
3351 # functions should return tuple of booleans below, if 'changes' is None:
3384 # functions should return tuple of booleans below, if 'changes' is None:
3352 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3385 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3353 #
3386 #
3354 # otherwise, 'changes' is a tuple of tuples below:
3387 # otherwise, 'changes' is a tuple of tuples below:
3355 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3388 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3356 # - (desturl, destbranch, destpeer, outgoing)
3389 # - (desturl, destbranch, destpeer, outgoing)
3357 summaryremotehooks = util.hooks()
3390 summaryremotehooks = util.hooks()
3358
3391
3359 # A list of state files kept by multistep operations like graft.
3392 # A list of state files kept by multistep operations like graft.
3360 # Since graft cannot be aborted, it is considered 'clearable' by update.
3393 # Since graft cannot be aborted, it is considered 'clearable' by update.
3361 # note: bisect is intentionally excluded
3394 # note: bisect is intentionally excluded
3362 # (state file, clearable, allowcommit, error, hint)
3395 # (state file, clearable, allowcommit, error, hint)
3363 unfinishedstates = [
3396 unfinishedstates = [
3364 ('graftstate', True, False, _('graft in progress'),
3397 ('graftstate', True, False, _('graft in progress'),
3365 _("use 'hg graft --continue' or 'hg update' to abort")),
3398 _("use 'hg graft --continue' or 'hg update' to abort")),
3366 ('updatestate', True, False, _('last update was interrupted'),
3399 ('updatestate', True, False, _('last update was interrupted'),
3367 _("use 'hg update' to get a consistent checkout"))
3400 _("use 'hg update' to get a consistent checkout"))
3368 ]
3401 ]
3369
3402
3370 def checkunfinished(repo, commit=False):
3403 def checkunfinished(repo, commit=False):
3371 '''Look for an unfinished multistep operation, like graft, and abort
3404 '''Look for an unfinished multistep operation, like graft, and abort
3372 if found. It's probably good to check this right before
3405 if found. It's probably good to check this right before
3373 bailifchanged().
3406 bailifchanged().
3374 '''
3407 '''
3375 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3408 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3376 if commit and allowcommit:
3409 if commit and allowcommit:
3377 continue
3410 continue
3378 if repo.vfs.exists(f):
3411 if repo.vfs.exists(f):
3379 raise error.Abort(msg, hint=hint)
3412 raise error.Abort(msg, hint=hint)
3380
3413
3381 def clearunfinished(repo):
3414 def clearunfinished(repo):
3382 '''Check for unfinished operations (as above), and clear the ones
3415 '''Check for unfinished operations (as above), and clear the ones
3383 that are clearable.
3416 that are clearable.
3384 '''
3417 '''
3385 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3418 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3386 if not clearable and repo.vfs.exists(f):
3419 if not clearable and repo.vfs.exists(f):
3387 raise error.Abort(msg, hint=hint)
3420 raise error.Abort(msg, hint=hint)
3388 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3421 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3389 if clearable and repo.vfs.exists(f):
3422 if clearable and repo.vfs.exists(f):
3390 util.unlink(repo.join(f))
3423 util.unlink(repo.join(f))
3391
3424
3392 afterresolvedstates = [
3425 afterresolvedstates = [
3393 ('graftstate',
3426 ('graftstate',
3394 _('hg graft --continue')),
3427 _('hg graft --continue')),
3395 ]
3428 ]
3396
3429
3397 def howtocontinue(repo):
3430 def howtocontinue(repo):
3398 '''Check for an unfinished operation and return the command to finish
3431 '''Check for an unfinished operation and return the command to finish
3399 it.
3432 it.
3400
3433
3401 afterresolvedstates tupples define a .hg/{file} and the corresponding
3434 afterresolvedstates tupples define a .hg/{file} and the corresponding
3402 command needed to finish it.
3435 command needed to finish it.
3403
3436
3404 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3437 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3405 a boolean.
3438 a boolean.
3406 '''
3439 '''
3407 contmsg = _("continue: %s")
3440 contmsg = _("continue: %s")
3408 for f, msg in afterresolvedstates:
3441 for f, msg in afterresolvedstates:
3409 if repo.vfs.exists(f):
3442 if repo.vfs.exists(f):
3410 return contmsg % msg, True
3443 return contmsg % msg, True
3411 workingctx = repo[None]
3444 workingctx = repo[None]
3412 dirty = any(repo.status()) or any(workingctx.sub(s).dirty()
3445 dirty = any(repo.status()) or any(workingctx.sub(s).dirty()
3413 for s in workingctx.substate)
3446 for s in workingctx.substate)
3414 if dirty:
3447 if dirty:
3415 return contmsg % _("hg commit"), False
3448 return contmsg % _("hg commit"), False
3416 return None, None
3449 return None, None
3417
3450
3418 def checkafterresolved(repo):
3451 def checkafterresolved(repo):
3419 '''Inform the user about the next action after completing hg resolve
3452 '''Inform the user about the next action after completing hg resolve
3420
3453
3421 If there's a matching afterresolvedstates, howtocontinue will yield
3454 If there's a matching afterresolvedstates, howtocontinue will yield
3422 repo.ui.warn as the reporter.
3455 repo.ui.warn as the reporter.
3423
3456
3424 Otherwise, it will yield repo.ui.note.
3457 Otherwise, it will yield repo.ui.note.
3425 '''
3458 '''
3426 msg, warning = howtocontinue(repo)
3459 msg, warning = howtocontinue(repo)
3427 if msg is not None:
3460 if msg is not None:
3428 if warning:
3461 if warning:
3429 repo.ui.warn("%s\n" % msg)
3462 repo.ui.warn("%s\n" % msg)
3430 else:
3463 else:
3431 repo.ui.note("%s\n" % msg)
3464 repo.ui.note("%s\n" % msg)
3432
3465
3433 def wrongtooltocontinue(repo, task):
3466 def wrongtooltocontinue(repo, task):
3434 '''Raise an abort suggesting how to properly continue if there is an
3467 '''Raise an abort suggesting how to properly continue if there is an
3435 active task.
3468 active task.
3436
3469
3437 Uses howtocontinue() to find the active task.
3470 Uses howtocontinue() to find the active task.
3438
3471
3439 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3472 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3440 a hint.
3473 a hint.
3441 '''
3474 '''
3442 after = howtocontinue(repo)
3475 after = howtocontinue(repo)
3443 hint = None
3476 hint = None
3444 if after[1]:
3477 if after[1]:
3445 hint = after[0]
3478 hint = after[0]
3446 raise error.Abort(_('no %s in progress') % task, hint=hint)
3479 raise error.Abort(_('no %s in progress') % task, hint=hint)
3447
3480
3448 class dirstateguard(object):
3481 class dirstateguard(object):
3449 '''Restore dirstate at unexpected failure.
3482 '''Restore dirstate at unexpected failure.
3450
3483
3451 At the construction, this class does:
3484 At the construction, this class does:
3452
3485
3453 - write current ``repo.dirstate`` out, and
3486 - write current ``repo.dirstate`` out, and
3454 - save ``.hg/dirstate`` into the backup file
3487 - save ``.hg/dirstate`` into the backup file
3455
3488
3456 This restores ``.hg/dirstate`` from backup file, if ``release()``
3489 This restores ``.hg/dirstate`` from backup file, if ``release()``
3457 is invoked before ``close()``.
3490 is invoked before ``close()``.
3458
3491
3459 This just removes the backup file at ``close()`` before ``release()``.
3492 This just removes the backup file at ``close()`` before ``release()``.
3460 '''
3493 '''
3461
3494
3462 def __init__(self, repo, name):
3495 def __init__(self, repo, name):
3463 self._repo = repo
3496 self._repo = repo
3464 self._suffix = '.backup.%s.%d' % (name, id(self))
3497 self._suffix = '.backup.%s.%d' % (name, id(self))
3465 repo.dirstate._savebackup(repo.currenttransaction(), self._suffix)
3498 repo.dirstate._savebackup(repo.currenttransaction(), self._suffix)
3466 self._active = True
3499 self._active = True
3467 self._closed = False
3500 self._closed = False
3468
3501
3469 def __del__(self):
3502 def __del__(self):
3470 if self._active: # still active
3503 if self._active: # still active
3471 # this may occur, even if this class is used correctly:
3504 # this may occur, even if this class is used correctly:
3472 # for example, releasing other resources like transaction
3505 # for example, releasing other resources like transaction
3473 # may raise exception before ``dirstateguard.release`` in
3506 # may raise exception before ``dirstateguard.release`` in
3474 # ``release(tr, ....)``.
3507 # ``release(tr, ....)``.
3475 self._abort()
3508 self._abort()
3476
3509
3477 def close(self):
3510 def close(self):
3478 if not self._active: # already inactivated
3511 if not self._active: # already inactivated
3479 msg = (_("can't close already inactivated backup: dirstate%s")
3512 msg = (_("can't close already inactivated backup: dirstate%s")
3480 % self._suffix)
3513 % self._suffix)
3481 raise error.Abort(msg)
3514 raise error.Abort(msg)
3482
3515
3483 self._repo.dirstate._clearbackup(self._repo.currenttransaction(),
3516 self._repo.dirstate._clearbackup(self._repo.currenttransaction(),
3484 self._suffix)
3517 self._suffix)
3485 self._active = False
3518 self._active = False
3486 self._closed = True
3519 self._closed = True
3487
3520
3488 def _abort(self):
3521 def _abort(self):
3489 self._repo.dirstate._restorebackup(self._repo.currenttransaction(),
3522 self._repo.dirstate._restorebackup(self._repo.currenttransaction(),
3490 self._suffix)
3523 self._suffix)
3491 self._active = False
3524 self._active = False
3492
3525
3493 def release(self):
3526 def release(self):
3494 if not self._closed:
3527 if not self._closed:
3495 if not self._active: # already inactivated
3528 if not self._active: # already inactivated
3496 msg = (_("can't release already inactivated backup:"
3529 msg = (_("can't release already inactivated backup:"
3497 " dirstate%s")
3530 " dirstate%s")
3498 % self._suffix)
3531 % self._suffix)
3499 raise error.Abort(msg)
3532 raise error.Abort(msg)
3500 self._abort()
3533 self._abort()
@@ -1,350 +1,482
1 $ remove() {
1 $ remove() {
2 > hg rm $@
2 > hg rm $@
3 > echo "exit code: $?"
3 > echo "exit code: $?"
4 > hg st
4 > hg st
5 > # do not use ls -R, which recurses in .hg subdirs on Mac OS X 10.5
5 > # do not use ls -R, which recurses in .hg subdirs on Mac OS X 10.5
6 > find . -name .hg -prune -o -type f -print | sort
6 > find . -name .hg -prune -o -type f -print | sort
7 > hg up -C
7 > hg up -C
8 > }
8 > }
9
9
10 $ cat >> $HGRCPATH <<EOF
10 $ cat >> $HGRCPATH <<EOF
11 > [progress]
11 > [progress]
12 > disable=False
12 > disable=False
13 > assume-tty = 1
13 > assume-tty = 1
14 > delay = 0
14 > delay = 0
15 > # set changedelay really large so we don't see nested topics
15 > # set changedelay really large so we don't see nested topics
16 > changedelay = 30000
16 > changedelay = 30000
17 > format = topic bar number
17 > format = topic bar number
18 > refresh = 0
18 > refresh = 0
19 > width = 60
19 > width = 60
20 > EOF
20 > EOF
21
21
22 $ hg init a
22 $ hg init a
23 $ cd a
23 $ cd a
24 $ echo a > foo
24 $ echo a > foo
25
25
26 file not managed
26 file not managed
27
27
28 $ remove foo
28 $ remove foo
29 \r (no-eol) (esc)
30 deleting [===========================================>] 1/1\r (no-eol) (esc)
31 \r (no-eol) (esc)
29 not removing foo: file is untracked
32 not removing foo: file is untracked
30 exit code: 1
33 exit code: 1
31 ? foo
34 ? foo
32 ./foo
35 ./foo
33 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
36 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
34
37
35 $ hg add foo
38 $ hg add foo
36 $ hg commit -m1
39 $ hg commit -m1
37
40
38 the table cases
41 the table cases
39 00 state added, options none
42 00 state added, options none
40
43
41 $ echo b > bar
44 $ echo b > bar
42 $ hg add bar
45 $ hg add bar
43 $ remove bar
46 $ remove bar
47 \r (no-eol) (esc)
48 deleting [===========================================>] 1/1\r (no-eol) (esc)
49 \r (no-eol) (esc)
50 \r (no-eol) (esc)
51 skipping [===========================================>] 1/1\r (no-eol) (esc)
52 \r (no-eol) (esc)
44 not removing bar: file has been marked for add (use forget to undo)
53 not removing bar: file has been marked for add (use forget to undo)
45 exit code: 1
54 exit code: 1
46 A bar
55 A bar
47 ./bar
56 ./bar
48 ./foo
57 ./foo
49 \r (no-eol) (esc)
58 \r (no-eol) (esc)
50 updating [===========================================>] 1/1\r (no-eol) (esc)
59 updating [===========================================>] 1/1\r (no-eol) (esc)
51 \r (no-eol) (esc)
60 \r (no-eol) (esc)
52 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
61 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
53
62
54 01 state clean, options none
63 01 state clean, options none
55
64
56 $ remove foo
65 $ remove foo
66 \r (no-eol) (esc)
67 deleting [===========================================>] 1/1\r (no-eol) (esc)
68 \r (no-eol) (esc)
57 exit code: 0
69 exit code: 0
58 R foo
70 R foo
59 ? bar
71 ? bar
60 ./bar
72 ./bar
61 \r (no-eol) (esc)
73 \r (no-eol) (esc)
62 updating [===========================================>] 1/1\r (no-eol) (esc)
74 updating [===========================================>] 1/1\r (no-eol) (esc)
63 \r (no-eol) (esc)
75 \r (no-eol) (esc)
64 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
76 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
65
77
66 02 state modified, options none
78 02 state modified, options none
67
79
68 $ echo b >> foo
80 $ echo b >> foo
69 $ remove foo
81 $ remove foo
82 \r (no-eol) (esc)
83 deleting [===========================================>] 1/1\r (no-eol) (esc)
84 \r (no-eol) (esc)
85 \r (no-eol) (esc)
86 skipping [===========================================>] 1/1\r (no-eol) (esc)
87 \r (no-eol) (esc)
70 not removing foo: file is modified (use -f to force removal)
88 not removing foo: file is modified (use -f to force removal)
71 exit code: 1
89 exit code: 1
72 M foo
90 M foo
73 ? bar
91 ? bar
74 ./bar
92 ./bar
75 ./foo
93 ./foo
76 \r (no-eol) (esc)
94 \r (no-eol) (esc)
77 updating [===========================================>] 1/1\r (no-eol) (esc)
95 updating [===========================================>] 1/1\r (no-eol) (esc)
78 \r (no-eol) (esc)
96 \r (no-eol) (esc)
79 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
97 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
80
98
81 03 state missing, options none
99 03 state missing, options none
82
100
83 $ rm foo
101 $ rm foo
84 $ remove foo
102 $ remove foo
103 \r (no-eol) (esc)
104 deleting [===========================================>] 1/1\r (no-eol) (esc)
105 \r (no-eol) (esc)
85 exit code: 0
106 exit code: 0
86 R foo
107 R foo
87 ? bar
108 ? bar
88 ./bar
109 ./bar
89 \r (no-eol) (esc)
110 \r (no-eol) (esc)
90 updating [===========================================>] 1/1\r (no-eol) (esc)
111 updating [===========================================>] 1/1\r (no-eol) (esc)
91 \r (no-eol) (esc)
112 \r (no-eol) (esc)
92 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
113 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
93
114
94 10 state added, options -f
115 10 state added, options -f
95
116
96 $ echo b > bar
117 $ echo b > bar
97 $ hg add bar
118 $ hg add bar
98 $ remove -f bar
119 $ remove -f bar
120 \r (no-eol) (esc)
121 deleting [===========================================>] 1/1\r (no-eol) (esc)
122 \r (no-eol) (esc)
99 exit code: 0
123 exit code: 0
100 ? bar
124 ? bar
101 ./bar
125 ./bar
102 ./foo
126 ./foo
103 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
127 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
104 $ rm bar
128 $ rm bar
105
129
106 11 state clean, options -f
130 11 state clean, options -f
107
131
108 $ remove -f foo
132 $ remove -f foo
133 \r (no-eol) (esc)
134 deleting [===========================================>] 1/1\r (no-eol) (esc)
135 \r (no-eol) (esc)
109 exit code: 0
136 exit code: 0
110 R foo
137 R foo
111 \r (no-eol) (esc)
138 \r (no-eol) (esc)
112 updating [===========================================>] 1/1\r (no-eol) (esc)
139 updating [===========================================>] 1/1\r (no-eol) (esc)
113 \r (no-eol) (esc)
140 \r (no-eol) (esc)
114 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
115
142
116 12 state modified, options -f
143 12 state modified, options -f
117
144
118 $ echo b >> foo
145 $ echo b >> foo
119 $ remove -f foo
146 $ remove -f foo
147 \r (no-eol) (esc)
148 deleting [===========================================>] 1/1\r (no-eol) (esc)
149 \r (no-eol) (esc)
120 exit code: 0
150 exit code: 0
121 R foo
151 R foo
122 \r (no-eol) (esc)
152 \r (no-eol) (esc)
123 updating [===========================================>] 1/1\r (no-eol) (esc)
153 updating [===========================================>] 1/1\r (no-eol) (esc)
124 \r (no-eol) (esc)
154 \r (no-eol) (esc)
125 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
155 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
126
156
127 13 state missing, options -f
157 13 state missing, options -f
128
158
129 $ rm foo
159 $ rm foo
130 $ remove -f foo
160 $ remove -f foo
161 \r (no-eol) (esc)
162 deleting [===========================================>] 1/1\r (no-eol) (esc)
163 \r (no-eol) (esc)
131 exit code: 0
164 exit code: 0
132 R foo
165 R foo
133 \r (no-eol) (esc)
166 \r (no-eol) (esc)
134 updating [===========================================>] 1/1\r (no-eol) (esc)
167 updating [===========================================>] 1/1\r (no-eol) (esc)
135 \r (no-eol) (esc)
168 \r (no-eol) (esc)
136 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
169 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
137
170
138 20 state added, options -A
171 20 state added, options -A
139
172
140 $ echo b > bar
173 $ echo b > bar
141 $ hg add bar
174 $ hg add bar
142 $ remove -A bar
175 $ remove -A bar
176 \r (no-eol) (esc)
177 deleting [===========================================>] 1/1\r (no-eol) (esc)
178 \r (no-eol) (esc)
179 \r (no-eol) (esc)
180 skipping [===========================================>] 1/1\r (no-eol) (esc)
181 \r (no-eol) (esc)
143 not removing bar: file still exists
182 not removing bar: file still exists
144 exit code: 1
183 exit code: 1
145 A bar
184 A bar
146 ./bar
185 ./bar
147 ./foo
186 ./foo
148 \r (no-eol) (esc)
187 \r (no-eol) (esc)
149 updating [===========================================>] 1/1\r (no-eol) (esc)
188 updating [===========================================>] 1/1\r (no-eol) (esc)
150 \r (no-eol) (esc)
189 \r (no-eol) (esc)
151 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
190 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
152
191
153 21 state clean, options -A
192 21 state clean, options -A
154
193
155 $ remove -A foo
194 $ remove -A foo
195 \r (no-eol) (esc)
196 deleting [===========================================>] 1/1\r (no-eol) (esc)
197 \r (no-eol) (esc)
198 \r (no-eol) (esc)
199 skipping [===========================================>] 1/1\r (no-eol) (esc)
200 \r (no-eol) (esc)
156 not removing foo: file still exists
201 not removing foo: file still exists
157 exit code: 1
202 exit code: 1
158 ? bar
203 ? bar
159 ./bar
204 ./bar
160 ./foo
205 ./foo
161 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
206 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
162
207
163 22 state modified, options -A
208 22 state modified, options -A
164
209
165 $ echo b >> foo
210 $ echo b >> foo
166 $ remove -A foo
211 $ remove -A foo
212 \r (no-eol) (esc)
213 deleting [===========================================>] 1/1\r (no-eol) (esc)
214 \r (no-eol) (esc)
215 \r (no-eol) (esc)
216 skipping [===========================================>] 1/1\r (no-eol) (esc)
217 \r (no-eol) (esc)
167 not removing foo: file still exists
218 not removing foo: file still exists
168 exit code: 1
219 exit code: 1
169 M foo
220 M foo
170 ? bar
221 ? bar
171 ./bar
222 ./bar
172 ./foo
223 ./foo
173 \r (no-eol) (esc)
224 \r (no-eol) (esc)
174 updating [===========================================>] 1/1\r (no-eol) (esc)
225 updating [===========================================>] 1/1\r (no-eol) (esc)
175 \r (no-eol) (esc)
226 \r (no-eol) (esc)
176 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
227 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
177
228
178 23 state missing, options -A
229 23 state missing, options -A
179
230
180 $ rm foo
231 $ rm foo
181 $ remove -A foo
232 $ remove -A foo
233 \r (no-eol) (esc)
234 deleting [===========================================>] 1/1\r (no-eol) (esc)
235 \r (no-eol) (esc)
182 exit code: 0
236 exit code: 0
183 R foo
237 R foo
184 ? bar
238 ? bar
185 ./bar
239 ./bar
186 \r (no-eol) (esc)
240 \r (no-eol) (esc)
187 updating [===========================================>] 1/1\r (no-eol) (esc)
241 updating [===========================================>] 1/1\r (no-eol) (esc)
188 \r (no-eol) (esc)
242 \r (no-eol) (esc)
189 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
243 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
190
244
191 30 state added, options -Af
245 30 state added, options -Af
192
246
193 $ echo b > bar
247 $ echo b > bar
194 $ hg add bar
248 $ hg add bar
195 $ remove -Af bar
249 $ remove -Af bar
250 \r (no-eol) (esc)
251 deleting [===========================================>] 1/1\r (no-eol) (esc)
252 \r (no-eol) (esc)
196 exit code: 0
253 exit code: 0
197 ? bar
254 ? bar
198 ./bar
255 ./bar
199 ./foo
256 ./foo
200 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
257 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
201 $ rm bar
258 $ rm bar
202
259
203 31 state clean, options -Af
260 31 state clean, options -Af
204
261
205 $ remove -Af foo
262 $ remove -Af foo
263 \r (no-eol) (esc)
264 deleting [===========================================>] 1/1\r (no-eol) (esc)
265 \r (no-eol) (esc)
206 exit code: 0
266 exit code: 0
207 R foo
267 R foo
208 ./foo
268 ./foo
209 \r (no-eol) (esc)
269 \r (no-eol) (esc)
210 updating [===========================================>] 1/1\r (no-eol) (esc)
270 updating [===========================================>] 1/1\r (no-eol) (esc)
211 \r (no-eol) (esc)
271 \r (no-eol) (esc)
212 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
272 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
213
273
214 32 state modified, options -Af
274 32 state modified, options -Af
215
275
216 $ echo b >> foo
276 $ echo b >> foo
217 $ remove -Af foo
277 $ remove -Af foo
278 \r (no-eol) (esc)
279 deleting [===========================================>] 1/1\r (no-eol) (esc)
280 \r (no-eol) (esc)
218 exit code: 0
281 exit code: 0
219 R foo
282 R foo
220 ./foo
283 ./foo
221 \r (no-eol) (esc)
284 \r (no-eol) (esc)
222 updating [===========================================>] 1/1\r (no-eol) (esc)
285 updating [===========================================>] 1/1\r (no-eol) (esc)
223 \r (no-eol) (esc)
286 \r (no-eol) (esc)
224 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
287 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
225
288
226 33 state missing, options -Af
289 33 state missing, options -Af
227
290
228 $ rm foo
291 $ rm foo
229 $ remove -Af foo
292 $ remove -Af foo
293 \r (no-eol) (esc)
294 deleting [===========================================>] 1/1\r (no-eol) (esc)
295 \r (no-eol) (esc)
230 exit code: 0
296 exit code: 0
231 R foo
297 R foo
232 \r (no-eol) (esc)
298 \r (no-eol) (esc)
233 updating [===========================================>] 1/1\r (no-eol) (esc)
299 updating [===========================================>] 1/1\r (no-eol) (esc)
234 \r (no-eol) (esc)
300 \r (no-eol) (esc)
235 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
301 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
236
302
237 test some directory stuff
303 test some directory stuff
238
304
239 $ mkdir test
305 $ mkdir test
240 $ echo a > test/foo
306 $ echo a > test/foo
241 $ echo b > test/bar
307 $ echo b > test/bar
242 $ hg ci -Am2
308 $ hg ci -Am2
243 adding test/bar
309 adding test/bar
244 adding test/foo
310 adding test/foo
245
311
246 dir, options none
312 dir, options none
247
313
248 $ rm test/bar
314 $ rm test/bar
249 $ remove test
315 $ remove test
316 \r (no-eol) (esc)
317 deleting [===========================================>] 1/1\r (no-eol) (esc)
318 \r (no-eol) (esc)
319 \r (no-eol) (esc)
320 deleting [=====================> ] 1/2\r (no-eol) (esc)
321 \r (no-eol) (esc)
322 \r (no-eol) (esc)
323 deleting [===========================================>] 2/2\r (no-eol) (esc)
324 \r (no-eol) (esc)
325 \r (no-eol) (esc)
326 \r (no-eol) (esc)
250 removing test/bar (glob)
327 removing test/bar (glob)
251 removing test/foo (glob)
328 removing test/foo (glob)
252 exit code: 0
329 exit code: 0
253 R test/bar
330 R test/bar
254 R test/foo
331 R test/foo
255 ./foo
332 ./foo
256 \r (no-eol) (esc)
333 \r (no-eol) (esc)
257 updating [===========================================>] 2/2\r (no-eol) (esc)
334 updating [===========================================>] 2/2\r (no-eol) (esc)
258 \r (no-eol) (esc)
335 \r (no-eol) (esc)
259 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
336 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
260
337
261 dir, options -f
338 dir, options -f
262
339
263 $ rm test/bar
340 $ rm test/bar
264 $ remove -f test
341 $ remove -f test
342 \r (no-eol) (esc)
343 deleting [===========================================>] 1/1\r (no-eol) (esc)
344 \r (no-eol) (esc)
345 \r (no-eol) (esc)
346 deleting [=====================> ] 1/2\r (no-eol) (esc)
347 \r (no-eol) (esc)
348 \r (no-eol) (esc)
349 deleting [===========================================>] 2/2\r (no-eol) (esc)
350 \r (no-eol) (esc)
351 \r (no-eol) (esc)
352 \r (no-eol) (esc)
265 removing test/bar (glob)
353 removing test/bar (glob)
266 removing test/foo (glob)
354 removing test/foo (glob)
267 exit code: 0
355 exit code: 0
268 R test/bar
356 R test/bar
269 R test/foo
357 R test/foo
270 ./foo
358 ./foo
271 \r (no-eol) (esc)
359 \r (no-eol) (esc)
272 updating [===========================================>] 2/2\r (no-eol) (esc)
360 updating [===========================================>] 2/2\r (no-eol) (esc)
273 \r (no-eol) (esc)
361 \r (no-eol) (esc)
274 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
362 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
275
363
276 dir, options -A
364 dir, options -A
277
365
278 $ rm test/bar
366 $ rm test/bar
279 $ remove -A test
367 $ remove -A test
368 \r (no-eol) (esc)
369 deleting [===========================================>] 1/1\r (no-eol) (esc)
370 \r (no-eol) (esc)
371 \r (no-eol) (esc)
372 skipping [===========================================>] 1/1\r (no-eol) (esc)
373 \r (no-eol) (esc)
374 \r (no-eol) (esc)
375 deleting [===========================================>] 1/1\r (no-eol) (esc)
376 \r (no-eol) (esc)
377 \r (no-eol) (esc)
378 \r (no-eol) (esc)
280 removing test/bar (glob)
379 removing test/bar (glob)
281 not removing test/foo: file still exists (glob)
380 not removing test/foo: file still exists (glob)
282 exit code: 1
381 exit code: 1
283 R test/bar
382 R test/bar
284 ./foo
383 ./foo
285 ./test/foo
384 ./test/foo
286 \r (no-eol) (esc)
385 \r (no-eol) (esc)
287 updating [===========================================>] 1/1\r (no-eol) (esc)
386 updating [===========================================>] 1/1\r (no-eol) (esc)
288 \r (no-eol) (esc)
387 \r (no-eol) (esc)
289 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
388 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
290
389
291 dir, options -Af
390 dir, options -Af
292
391
293 $ rm test/bar
392 $ rm test/bar
294 $ remove -Af test
393 $ remove -Af test
394 \r (no-eol) (esc)
395 deleting [===========================================>] 1/1\r (no-eol) (esc)
396 \r (no-eol) (esc)
397 \r (no-eol) (esc)
398 deleting [=====================> ] 1/2\r (no-eol) (esc)
399 \r (no-eol) (esc)
400 \r (no-eol) (esc)
401 deleting [===========================================>] 2/2\r (no-eol) (esc)
402 \r (no-eol) (esc)
403 \r (no-eol) (esc)
404 \r (no-eol) (esc)
295 removing test/bar (glob)
405 removing test/bar (glob)
296 removing test/foo (glob)
406 removing test/foo (glob)
297 exit code: 0
407 exit code: 0
298 R test/bar
408 R test/bar
299 R test/foo
409 R test/foo
300 ./foo
410 ./foo
301 ./test/foo
411 ./test/foo
302 \r (no-eol) (esc)
412 \r (no-eol) (esc)
303 updating [===========================================>] 2/2\r (no-eol) (esc)
413 updating [===========================================>] 2/2\r (no-eol) (esc)
304 \r (no-eol) (esc)
414 \r (no-eol) (esc)
305 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
415 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
306
416
307 test remove dropping empty trees (issue1861)
417 test remove dropping empty trees (issue1861)
308
418
309 $ mkdir -p issue1861/b/c
419 $ mkdir -p issue1861/b/c
310 $ echo x > issue1861/x
420 $ echo x > issue1861/x
311 $ echo y > issue1861/b/c/y
421 $ echo y > issue1861/b/c/y
312 $ hg ci -Am add
422 $ hg ci -Am add
313 adding issue1861/b/c/y
423 adding issue1861/b/c/y
314 adding issue1861/x
424 adding issue1861/x
315 $ hg rm issue1861/b
425 $ hg rm issue1861/b
426 \r (no-eol) (esc)
427 deleting [===========================================>] 1/1\r (no-eol) (esc)
428 \r (no-eol) (esc)
429 \r (no-eol) (esc)
430 deleting [===========================================>] 1/1\r (no-eol) (esc)
431 \r (no-eol) (esc)
432 \r (no-eol) (esc)
433 \r (no-eol) (esc)
316 removing issue1861/b/c/y (glob)
434 removing issue1861/b/c/y (glob)
317 $ hg ci -m remove
435 $ hg ci -m remove
318 $ ls issue1861
436 $ ls issue1861
319 x
437 x
320
438
321 test that commit does not crash if the user removes a newly added file
439 test that commit does not crash if the user removes a newly added file
322
440
323 $ touch f1
441 $ touch f1
324 $ hg add f1
442 $ hg add f1
325 $ rm f1
443 $ rm f1
326 $ hg ci -A -mx
444 $ hg ci -A -mx
327 removing f1
445 removing f1
328 nothing changed
446 nothing changed
329 [1]
447 [1]
330
448
331 handling of untracked directories and missing files
449 handling of untracked directories and missing files
332
450
333 $ mkdir d1
451 $ mkdir d1
334 $ echo a > d1/a
452 $ echo a > d1/a
335 $ hg rm --after d1
453 $ hg rm --after d1
454 \r (no-eol) (esc)
455 deleting [===========================================>] 1/1\r (no-eol) (esc)
456 \r (no-eol) (esc)
336 not removing d1: no tracked files
457 not removing d1: no tracked files
337 [1]
458 [1]
338 $ hg add d1/a
459 $ hg add d1/a
339 $ rm d1/a
460 $ rm d1/a
340 $ hg rm --after d1
461 $ hg rm --after d1
462 \r (no-eol) (esc)
463 deleting [===========================================>] 1/1\r (no-eol) (esc)
464 \r (no-eol) (esc)
465 \r (no-eol) (esc)
466 deleting [===========================================>] 1/1\r (no-eol) (esc)
467 \r (no-eol) (esc)
468 \r (no-eol) (esc)
469 \r (no-eol) (esc)
341 removing d1/a (glob)
470 removing d1/a (glob)
342 #if windows
471 #if windows
343 $ hg rm --after nosuch
472 $ hg rm --after nosuch
344 nosuch: * (glob)
473 nosuch: * (glob)
345 [1]
474 [1]
346 #else
475 #else
347 $ hg rm --after nosuch
476 $ hg rm --after nosuch
348 nosuch: No such file or directory
477 nosuch: No such file or directory
478 \r (no-eol) (esc)
479 deleting [===========================================>] 1/1\r (no-eol) (esc)
480 \r (no-eol) (esc)
349 [1]
481 [1]
350 #endif
482 #endif
@@ -1,1052 +1,1099
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 Cleaning both repositories, just as a clone -U
76 Cleaning both repositories, just as a clone -U
77
77
78 $ hg up -C -R sub2 null
78 $ hg up -C -R sub2 null
79 \r (no-eol) (esc)
79 \r (no-eol) (esc)
80 updating [===========================================>] 1/1\r (no-eol) (esc)
80 updating [===========================================>] 1/1\r (no-eol) (esc)
81 \r (no-eol) (esc)
81 \r (no-eol) (esc)
82 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
82 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
83 $ hg up -C -R sub1 null
83 $ hg up -C -R sub1 null
84 \r (no-eol) (esc)
84 \r (no-eol) (esc)
85 updating [===========================================>] 1/1\r (no-eol) (esc)
85 updating [===========================================>] 1/1\r (no-eol) (esc)
86 \r (no-eol) (esc)
86 \r (no-eol) (esc)
87 \r (no-eol) (esc)
87 \r (no-eol) (esc)
88 updating [===========================================>] 3/3\r (no-eol) (esc)
88 updating [===========================================>] 3/3\r (no-eol) (esc)
89 \r (no-eol) (esc)
89 \r (no-eol) (esc)
90 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
90 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
91 $ hg up -C -R main null
91 $ hg up -C -R main null
92 \r (no-eol) (esc)
92 \r (no-eol) (esc)
93 updating [===========================================>] 1/1\r (no-eol) (esc)
93 updating [===========================================>] 1/1\r (no-eol) (esc)
94 \r (no-eol) (esc)
94 \r (no-eol) (esc)
95 \r (no-eol) (esc)
95 \r (no-eol) (esc)
96 updating [===========================================>] 3/3\r (no-eol) (esc)
96 updating [===========================================>] 3/3\r (no-eol) (esc)
97 \r (no-eol) (esc)
97 \r (no-eol) (esc)
98 \r (no-eol) (esc)
98 \r (no-eol) (esc)
99 updating [===========================================>] 3/3\r (no-eol) (esc)
99 updating [===========================================>] 3/3\r (no-eol) (esc)
100 \r (no-eol) (esc)
100 \r (no-eol) (esc)
101 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
101 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
102 $ rm -rf main/sub1
102 $ rm -rf main/sub1
103 $ rm -rf sub1/sub2
103 $ rm -rf sub1/sub2
104
104
105 Clone main
105 Clone main
106
106
107 $ hg --config extensions.largefiles= clone main cloned
107 $ hg --config extensions.largefiles= clone main cloned
108 \r (no-eol) (esc)
108 \r (no-eol) (esc)
109 linking [ <=> ] 1\r (no-eol) (esc)
109 linking [ <=> ] 1\r (no-eol) (esc)
110 linking [ <=> ] 2\r (no-eol) (esc)
110 linking [ <=> ] 2\r (no-eol) (esc)
111 linking [ <=> ] 3\r (no-eol) (esc)
111 linking [ <=> ] 3\r (no-eol) (esc)
112 linking [ <=> ] 4\r (no-eol) (esc)
112 linking [ <=> ] 4\r (no-eol) (esc)
113 linking [ <=> ] 5\r (no-eol) (esc)
113 linking [ <=> ] 5\r (no-eol) (esc)
114 linking [ <=> ] 6\r (no-eol) (esc)
114 linking [ <=> ] 6\r (no-eol) (esc)
115 linking [ <=> ] 7\r (no-eol) (esc)
115 linking [ <=> ] 7\r (no-eol) (esc)
116 linking [ <=> ] 8\r (no-eol) (esc)
116 linking [ <=> ] 8\r (no-eol) (esc)
117 \r (no-eol) (esc)
117 \r (no-eol) (esc)
118 \r (no-eol) (esc)
118 \r (no-eol) (esc)
119 updating [===========================================>] 3/3\r (no-eol) (esc)
119 updating [===========================================>] 3/3\r (no-eol) (esc)
120 updating [===========================================>] 3/3\r (no-eol) (esc)
120 updating [===========================================>] 3/3\r (no-eol) (esc)
121 updating [===========================================>] 1/1\r (no-eol) (esc)
121 updating [===========================================>] 1/1\r (no-eol) (esc)
122 \r (no-eol) (esc)
122 \r (no-eol) (esc)
123 updating to branch default
123 updating to branch default
124 cloning subrepo sub1 from $TESTTMP/sub1
124 cloning subrepo sub1 from $TESTTMP/sub1
125 cloning subrepo sub1/sub2 from $TESTTMP/sub2 (glob)
125 cloning subrepo sub1/sub2 from $TESTTMP/sub2 (glob)
126 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
126 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
127
127
128 Largefiles is NOT enabled in the clone if the source repo doesn't require it
128 Largefiles is NOT enabled in the clone if the source repo doesn't require it
129 $ cat cloned/.hg/hgrc
129 $ cat cloned/.hg/hgrc
130 # example repository config (see "hg help config" for more info)
130 # example repository config (see "hg help config" for more info)
131 [paths]
131 [paths]
132 default = $TESTTMP/main (glob)
132 default = $TESTTMP/main (glob)
133
133
134 # path aliases to other clones of this repo in URLs or filesystem paths
134 # path aliases to other clones of this repo in URLs or filesystem paths
135 # (see "hg help config.paths" for more info)
135 # (see "hg help config.paths" for more info)
136 #
136 #
137 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
137 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
138 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
138 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
139 # my-clone = /home/jdoe/jdoes-clone
139 # my-clone = /home/jdoe/jdoes-clone
140
140
141 [ui]
141 [ui]
142 # name and email (local to this repository, optional), e.g.
142 # name and email (local to this repository, optional), e.g.
143 # username = Jane Doe <jdoe@example.com>
143 # username = Jane Doe <jdoe@example.com>
144
144
145 Checking cloned repo ids
145 Checking cloned repo ids
146
146
147 $ printf "cloned " ; hg id -R cloned
147 $ printf "cloned " ; hg id -R cloned
148 cloned 7f491f53a367 tip
148 cloned 7f491f53a367 tip
149 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
149 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
150 cloned/sub1 fc3b4ce2696f tip
150 cloned/sub1 fc3b4ce2696f tip
151 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
151 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
152 cloned/sub1/sub2 c57a0840e3ba tip
152 cloned/sub1/sub2 c57a0840e3ba tip
153
153
154 debugsub output for main and sub1
154 debugsub output for main and sub1
155
155
156 $ hg debugsub -R cloned
156 $ hg debugsub -R cloned
157 path sub1
157 path sub1
158 source ../sub1
158 source ../sub1
159 revision fc3b4ce2696f7741438c79207583768f2ce6b0dd
159 revision fc3b4ce2696f7741438c79207583768f2ce6b0dd
160 $ hg debugsub -R cloned/sub1
160 $ hg debugsub -R cloned/sub1
161 path sub2
161 path sub2
162 source ../sub2
162 source ../sub2
163 revision c57a0840e3badd667ef3c3ef65471609acb2ba3c
163 revision c57a0840e3badd667ef3c3ef65471609acb2ba3c
164
164
165 Modifying deeply nested 'sub2'
165 Modifying deeply nested 'sub2'
166
166
167 $ echo modified > cloned/sub1/sub2/sub2
167 $ echo modified > cloned/sub1/sub2/sub2
168 $ hg commit --subrepos -m "deep nested modif should trigger a commit" -R cloned
168 $ hg commit --subrepos -m "deep nested modif should trigger a commit" -R cloned
169 committing subrepository sub1
169 committing subrepository sub1
170 committing subrepository sub1/sub2 (glob)
170 committing subrepository sub1/sub2 (glob)
171
171
172 Checking modified node ids
172 Checking modified node ids
173
173
174 $ printf "cloned " ; hg id -R cloned
174 $ printf "cloned " ; hg id -R cloned
175 cloned ffe6649062fe tip
175 cloned ffe6649062fe tip
176 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
176 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
177 cloned/sub1 2ecb03bf44a9 tip
177 cloned/sub1 2ecb03bf44a9 tip
178 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
178 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
179 cloned/sub1/sub2 53dd3430bcaf tip
179 cloned/sub1/sub2 53dd3430bcaf tip
180
180
181 debugsub output for main and sub1
181 debugsub output for main and sub1
182
182
183 $ hg debugsub -R cloned
183 $ hg debugsub -R cloned
184 path sub1
184 path sub1
185 source ../sub1
185 source ../sub1
186 revision 2ecb03bf44a94e749e8669481dd9069526ce7cb9
186 revision 2ecb03bf44a94e749e8669481dd9069526ce7cb9
187 $ hg debugsub -R cloned/sub1
187 $ hg debugsub -R cloned/sub1
188 path sub2
188 path sub2
189 source ../sub2
189 source ../sub2
190 revision 53dd3430bcaf5ab4a7c48262bcad6d441f510487
190 revision 53dd3430bcaf5ab4a7c48262bcad6d441f510487
191
191
192 Check that deep archiving works
192 Check that deep archiving works
193
193
194 $ cd cloned
194 $ cd cloned
195 $ echo 'test' > sub1/sub2/test.txt
195 $ echo 'test' > sub1/sub2/test.txt
196 $ hg --config extensions.largefiles=! add sub1/sub2/test.txt
196 $ hg --config extensions.largefiles=! add sub1/sub2/test.txt
197 $ mkdir sub1/sub2/folder
197 $ mkdir sub1/sub2/folder
198 $ echo 'subfolder' > sub1/sub2/folder/test.txt
198 $ echo 'subfolder' > sub1/sub2/folder/test.txt
199 $ hg ci -ASm "add test.txt"
199 $ hg ci -ASm "add test.txt"
200 adding sub1/sub2/folder/test.txt
200 adding sub1/sub2/folder/test.txt
201 committing subrepository sub1
201 committing subrepository sub1
202 committing subrepository sub1/sub2 (glob)
202 committing subrepository sub1/sub2 (glob)
203
203
204 .. but first take a detour through some deep removal testing
204 .. but first take a detour through some deep removal testing
205
205
206 $ hg remove -S -I 're:.*.txt' .
206 $ hg remove -S -I 're:.*.txt' .
207 \r (no-eol) (esc)
208 searching [==========================================>] 1/1\r (no-eol) (esc)
209 searching [==========================================>] 1/1\r (no-eol) (esc)
210 \r (no-eol) (esc)
211 \r (no-eol) (esc)
212 deleting [=====================> ] 1/2\r (no-eol) (esc)
213 \r (no-eol) (esc)
214 \r (no-eol) (esc)
215 deleting [===========================================>] 2/2\r (no-eol) (esc)
216 \r (no-eol) (esc)
217 \r (no-eol) (esc)
218 \r (no-eol) (esc)
207 removing sub1/sub2/folder/test.txt (glob)
219 removing sub1/sub2/folder/test.txt (glob)
208 removing sub1/sub2/test.txt (glob)
220 removing sub1/sub2/test.txt (glob)
209 $ hg status -S
221 $ hg status -S
210 R sub1/sub2/folder/test.txt
222 R sub1/sub2/folder/test.txt
211 R sub1/sub2/test.txt
223 R sub1/sub2/test.txt
212 $ hg update -Cq
224 $ hg update -Cq
213 $ hg remove -I 're:.*.txt' sub1
225 $ hg remove -I 're:.*.txt' sub1
226 \r (no-eol) (esc)
227 searching [==========================================>] 1/1\r (no-eol) (esc)
228 \r (no-eol) (esc)
229 \r (no-eol) (esc)
230 deleting [===========================================>] 1/1\r (no-eol) (esc)
231 \r (no-eol) (esc)
214 $ hg status -S
232 $ hg status -S
215 $ hg remove sub1/sub2/folder/test.txt
233 $ hg remove sub1/sub2/folder/test.txt
234 \r (no-eol) (esc)
235 searching [==========================================>] 1/1\r (no-eol) (esc)
236 searching [==========================================>] 1/1\r (no-eol) (esc)
237 \r (no-eol) (esc)
238 \r (no-eol) (esc)
239 deleting [===========================================>] 1/1\r (no-eol) (esc)
240 \r (no-eol) (esc)
241 \r (no-eol) (esc)
242 deleting [===========================================>] 1/1\r (no-eol) (esc)
243 \r (no-eol) (esc)
244 \r (no-eol) (esc)
245 deleting [===========================================>] 1/1\r (no-eol) (esc)
246 \r (no-eol) (esc)
216 $ hg remove sub1/.hgsubstate
247 $ hg remove sub1/.hgsubstate
248 \r (no-eol) (esc)
249 searching [==========================================>] 1/1\r (no-eol) (esc)
250 \r (no-eol) (esc)
251 \r (no-eol) (esc)
252 deleting [===========================================>] 1/1\r (no-eol) (esc)
253 \r (no-eol) (esc)
254 \r (no-eol) (esc)
255 deleting [===========================================>] 1/1\r (no-eol) (esc)
256 \r (no-eol) (esc)
217 $ mv sub1/.hgsub sub1/x.hgsub
257 $ mv sub1/.hgsub sub1/x.hgsub
218 $ hg status -S
258 $ hg status -S
219 warning: subrepo spec file 'sub1/.hgsub' not found
259 warning: subrepo spec file 'sub1/.hgsub' not found
220 R sub1/.hgsubstate
260 R sub1/.hgsubstate
221 R sub1/sub2/folder/test.txt
261 R sub1/sub2/folder/test.txt
222 ! sub1/.hgsub
262 ! sub1/.hgsub
223 ? sub1/x.hgsub
263 ? sub1/x.hgsub
224 $ mv sub1/x.hgsub sub1/.hgsub
264 $ mv sub1/x.hgsub sub1/.hgsub
225 $ hg update -Cq
265 $ hg update -Cq
226 $ touch sub1/foo
266 $ touch sub1/foo
227 $ hg forget sub1/sub2/folder/test.txt
267 $ hg forget sub1/sub2/folder/test.txt
228 $ rm sub1/sub2/test.txt
268 $ rm sub1/sub2/test.txt
229
269
230 Test relative path printing + subrepos
270 Test relative path printing + subrepos
231 $ mkdir -p foo/bar
271 $ mkdir -p foo/bar
232 $ cd foo
272 $ cd foo
233 $ touch bar/abc
273 $ touch bar/abc
234 $ hg addremove -S ..
274 $ hg addremove -S ..
235 \r (no-eol) (esc)
275 \r (no-eol) (esc)
236 searching for exact renames [ ] 0/1\r (no-eol) (esc)
276 searching for exact renames [ ] 0/1\r (no-eol) (esc)
237 \r (no-eol) (esc)
277 \r (no-eol) (esc)
238 adding ../sub1/sub2/folder/test.txt (glob)
278 adding ../sub1/sub2/folder/test.txt (glob)
239 removing ../sub1/sub2/test.txt (glob)
279 removing ../sub1/sub2/test.txt (glob)
240 adding ../sub1/foo (glob)
280 adding ../sub1/foo (glob)
241 adding bar/abc (glob)
281 adding bar/abc (glob)
242 $ cd ..
282 $ cd ..
243 $ hg status -S
283 $ hg status -S
244 A foo/bar/abc
284 A foo/bar/abc
245 A sub1/foo
285 A sub1/foo
246 R sub1/sub2/test.txt
286 R sub1/sub2/test.txt
247
287
248 Archive wdir() with subrepos
288 Archive wdir() with subrepos
249 $ hg rm main
289 $ hg rm main
290 \r (no-eol) (esc)
291 deleting [===========================================>] 1/1\r (no-eol) (esc)
292 \r (no-eol) (esc)
250 $ hg archive -S -r 'wdir()' ../wdir
293 $ hg archive -S -r 'wdir()' ../wdir
251 \r (no-eol) (esc)
294 \r (no-eol) (esc)
252 archiving [ ] 0/3\r (no-eol) (esc)
295 archiving [ ] 0/3\r (no-eol) (esc)
253 archiving [=============> ] 1/3\r (no-eol) (esc)
296 archiving [=============> ] 1/3\r (no-eol) (esc)
254 archiving [===========================> ] 2/3\r (no-eol) (esc)
297 archiving [===========================> ] 2/3\r (no-eol) (esc)
255 archiving [==========================================>] 3/3\r (no-eol) (esc)
298 archiving [==========================================>] 3/3\r (no-eol) (esc)
256 \r (no-eol) (esc)
299 \r (no-eol) (esc)
257 \r (no-eol) (esc)
300 \r (no-eol) (esc)
258 archiving (sub1) [ ] 0/4\r (no-eol) (esc)
301 archiving (sub1) [ ] 0/4\r (no-eol) (esc)
259 archiving (sub1) [========> ] 1/4\r (no-eol) (esc)
302 archiving (sub1) [========> ] 1/4\r (no-eol) (esc)
260 archiving (sub1) [=================> ] 2/4\r (no-eol) (esc)
303 archiving (sub1) [=================> ] 2/4\r (no-eol) (esc)
261 archiving (sub1) [==========================> ] 3/4\r (no-eol) (esc)
304 archiving (sub1) [==========================> ] 3/4\r (no-eol) (esc)
262 archiving (sub1) [===================================>] 4/4\r (no-eol) (esc)
305 archiving (sub1) [===================================>] 4/4\r (no-eol) (esc)
263 \r (no-eol) (esc)
306 \r (no-eol) (esc)
264 \r (no-eol) (esc)
307 \r (no-eol) (esc)
265 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc)
308 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc)
266 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc)
309 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc)
267 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc)
310 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc)
268 \r (no-eol) (esc)
311 \r (no-eol) (esc)
269 $ diff -r . ../wdir | egrep -v '\.hg$|^Common subdirectories:'
312 $ diff -r . ../wdir | egrep -v '\.hg$|^Common subdirectories:'
270 Only in ../wdir: .hg_archival.txt
313 Only in ../wdir: .hg_archival.txt
271
314
272 $ find ../wdir -type f | sort
315 $ find ../wdir -type f | sort
273 ../wdir/.hg_archival.txt
316 ../wdir/.hg_archival.txt
274 ../wdir/.hgsub
317 ../wdir/.hgsub
275 ../wdir/.hgsubstate
318 ../wdir/.hgsubstate
276 ../wdir/foo/bar/abc
319 ../wdir/foo/bar/abc
277 ../wdir/sub1/.hgsub
320 ../wdir/sub1/.hgsub
278 ../wdir/sub1/.hgsubstate
321 ../wdir/sub1/.hgsubstate
279 ../wdir/sub1/foo
322 ../wdir/sub1/foo
280 ../wdir/sub1/sub1
323 ../wdir/sub1/sub1
281 ../wdir/sub1/sub2/folder/test.txt
324 ../wdir/sub1/sub2/folder/test.txt
282 ../wdir/sub1/sub2/sub2
325 ../wdir/sub1/sub2/sub2
283
326
284 $ cat ../wdir/.hg_archival.txt
327 $ cat ../wdir/.hg_archival.txt
285 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
328 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
286 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd+
329 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd+
287 branch: default
330 branch: default
288 latesttag: null
331 latesttag: null
289 latesttagdistance: 4
332 latesttagdistance: 4
290 changessincelatesttag: 4
333 changessincelatesttag: 4
291
334
292 Attempting to archive 'wdir()' with a missing file is handled gracefully
335 Attempting to archive 'wdir()' with a missing file is handled gracefully
293 $ rm sub1/sub1
336 $ rm sub1/sub1
294 $ rm -r ../wdir
337 $ rm -r ../wdir
295 $ hg archive -v -S -r 'wdir()' ../wdir
338 $ hg archive -v -S -r 'wdir()' ../wdir
296 \r (no-eol) (esc)
339 \r (no-eol) (esc)
297 archiving [ ] 0/3\r (no-eol) (esc)
340 archiving [ ] 0/3\r (no-eol) (esc)
298 archiving [=============> ] 1/3\r (no-eol) (esc)
341 archiving [=============> ] 1/3\r (no-eol) (esc)
299 archiving [===========================> ] 2/3\r (no-eol) (esc)
342 archiving [===========================> ] 2/3\r (no-eol) (esc)
300 archiving [==========================================>] 3/3\r (no-eol) (esc)
343 archiving [==========================================>] 3/3\r (no-eol) (esc)
301 \r (no-eol) (esc)
344 \r (no-eol) (esc)
302 \r (no-eol) (esc)
345 \r (no-eol) (esc)
303 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
346 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
304 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
347 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
305 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
348 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
306 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
349 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
307 \r (no-eol) (esc)
350 \r (no-eol) (esc)
308 \r (no-eol) (esc)
351 \r (no-eol) (esc)
309 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc)
352 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc)
310 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc)
353 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc)
311 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc)
354 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc)
312 \r (no-eol) (esc)
355 \r (no-eol) (esc)
313 $ find ../wdir -type f | sort
356 $ find ../wdir -type f | sort
314 ../wdir/.hg_archival.txt
357 ../wdir/.hg_archival.txt
315 ../wdir/.hgsub
358 ../wdir/.hgsub
316 ../wdir/.hgsubstate
359 ../wdir/.hgsubstate
317 ../wdir/foo/bar/abc
360 ../wdir/foo/bar/abc
318 ../wdir/sub1/.hgsub
361 ../wdir/sub1/.hgsub
319 ../wdir/sub1/.hgsubstate
362 ../wdir/sub1/.hgsubstate
320 ../wdir/sub1/foo
363 ../wdir/sub1/foo
321 ../wdir/sub1/sub2/folder/test.txt
364 ../wdir/sub1/sub2/folder/test.txt
322 ../wdir/sub1/sub2/sub2
365 ../wdir/sub1/sub2/sub2
323
366
324 Continue relative path printing + subrepos
367 Continue relative path printing + subrepos
325 $ hg update -Cq
368 $ hg update -Cq
326 $ rm -r ../wdir
369 $ rm -r ../wdir
327 $ hg archive -S -r 'wdir()' ../wdir
370 $ hg archive -S -r 'wdir()' ../wdir
328 \r (no-eol) (esc)
371 \r (no-eol) (esc)
329 archiving [ ] 0/3\r (no-eol) (esc)
372 archiving [ ] 0/3\r (no-eol) (esc)
330 archiving [=============> ] 1/3\r (no-eol) (esc)
373 archiving [=============> ] 1/3\r (no-eol) (esc)
331 archiving [===========================> ] 2/3\r (no-eol) (esc)
374 archiving [===========================> ] 2/3\r (no-eol) (esc)
332 archiving [==========================================>] 3/3\r (no-eol) (esc)
375 archiving [==========================================>] 3/3\r (no-eol) (esc)
333 \r (no-eol) (esc)
376 \r (no-eol) (esc)
334 \r (no-eol) (esc)
377 \r (no-eol) (esc)
335 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
378 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
336 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
379 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
337 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
380 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
338 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
381 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
339 \r (no-eol) (esc)
382 \r (no-eol) (esc)
340 \r (no-eol) (esc)
383 \r (no-eol) (esc)
341 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc)
384 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc)
342 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc)
385 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc)
343 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc)
386 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc)
344 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc)
387 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc)
345 \r (no-eol) (esc)
388 \r (no-eol) (esc)
346 $ cat ../wdir/.hg_archival.txt
389 $ cat ../wdir/.hg_archival.txt
347 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
390 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
348 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd
391 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd
349 branch: default
392 branch: default
350 latesttag: null
393 latesttag: null
351 latesttagdistance: 4
394 latesttagdistance: 4
352 changessincelatesttag: 4
395 changessincelatesttag: 4
353
396
354 $ touch sub1/sub2/folder/bar
397 $ touch sub1/sub2/folder/bar
355 $ hg addremove sub1/sub2
398 $ hg addremove sub1/sub2
356 adding sub1/sub2/folder/bar (glob)
399 adding sub1/sub2/folder/bar (glob)
357 $ hg status -S
400 $ hg status -S
358 A sub1/sub2/folder/bar
401 A sub1/sub2/folder/bar
359 ? foo/bar/abc
402 ? foo/bar/abc
360 ? sub1/foo
403 ? sub1/foo
361 $ hg update -Cq
404 $ hg update -Cq
362 $ hg addremove sub1
405 $ hg addremove sub1
363 adding sub1/sub2/folder/bar (glob)
406 adding sub1/sub2/folder/bar (glob)
364 adding sub1/foo (glob)
407 adding sub1/foo (glob)
365 $ hg update -Cq
408 $ hg update -Cq
366 $ rm sub1/sub2/folder/test.txt
409 $ rm sub1/sub2/folder/test.txt
367 $ rm sub1/sub2/test.txt
410 $ rm sub1/sub2/test.txt
368 $ hg ci -ASm "remove test.txt"
411 $ hg ci -ASm "remove test.txt"
369 adding sub1/sub2/folder/bar
412 adding sub1/sub2/folder/bar
370 removing sub1/sub2/folder/test.txt
413 removing sub1/sub2/folder/test.txt
371 removing sub1/sub2/test.txt
414 removing sub1/sub2/test.txt
372 adding sub1/foo
415 adding sub1/foo
373 adding foo/bar/abc
416 adding foo/bar/abc
374 committing subrepository sub1
417 committing subrepository sub1
375 committing subrepository sub1/sub2 (glob)
418 committing subrepository sub1/sub2 (glob)
376
419
377 $ hg forget sub1/sub2/sub2
420 $ hg forget sub1/sub2/sub2
378 $ echo x > sub1/sub2/x.txt
421 $ echo x > sub1/sub2/x.txt
379 $ hg add sub1/sub2/x.txt
422 $ hg add sub1/sub2/x.txt
380
423
381 Files sees uncommitted adds and removes in subrepos
424 Files sees uncommitted adds and removes in subrepos
382 $ hg files -S
425 $ hg files -S
383 .hgsub
426 .hgsub
384 .hgsubstate
427 .hgsubstate
385 foo/bar/abc (glob)
428 foo/bar/abc (glob)
386 main
429 main
387 sub1/.hgsub (glob)
430 sub1/.hgsub (glob)
388 sub1/.hgsubstate (glob)
431 sub1/.hgsubstate (glob)
389 sub1/foo (glob)
432 sub1/foo (glob)
390 sub1/sub1 (glob)
433 sub1/sub1 (glob)
391 sub1/sub2/folder/bar (glob)
434 sub1/sub2/folder/bar (glob)
392 sub1/sub2/x.txt (glob)
435 sub1/sub2/x.txt (glob)
393
436
394 $ hg files -S "set:eol('dos') or eol('unix') or size('<= 0')"
437 $ hg files -S "set:eol('dos') or eol('unix') or size('<= 0')"
395 .hgsub
438 .hgsub
396 .hgsubstate
439 .hgsubstate
397 foo/bar/abc (glob)
440 foo/bar/abc (glob)
398 main
441 main
399 sub1/.hgsub (glob)
442 sub1/.hgsub (glob)
400 sub1/.hgsubstate (glob)
443 sub1/.hgsubstate (glob)
401 sub1/foo (glob)
444 sub1/foo (glob)
402 sub1/sub1 (glob)
445 sub1/sub1 (glob)
403 sub1/sub2/folder/bar (glob)
446 sub1/sub2/folder/bar (glob)
404 sub1/sub2/x.txt (glob)
447 sub1/sub2/x.txt (glob)
405
448
406 $ hg files -r '.^' -S "set:eol('dos') or eol('unix')"
449 $ hg files -r '.^' -S "set:eol('dos') or eol('unix')"
407 .hgsub
450 .hgsub
408 .hgsubstate
451 .hgsubstate
409 main
452 main
410 sub1/.hgsub (glob)
453 sub1/.hgsub (glob)
411 sub1/.hgsubstate (glob)
454 sub1/.hgsubstate (glob)
412 sub1/sub1 (glob)
455 sub1/sub1 (glob)
413 sub1/sub2/folder/test.txt (glob)
456 sub1/sub2/folder/test.txt (glob)
414 sub1/sub2/sub2 (glob)
457 sub1/sub2/sub2 (glob)
415 sub1/sub2/test.txt (glob)
458 sub1/sub2/test.txt (glob)
416
459
417 $ hg files sub1
460 $ hg files sub1
418 sub1/.hgsub (glob)
461 sub1/.hgsub (glob)
419 sub1/.hgsubstate (glob)
462 sub1/.hgsubstate (glob)
420 sub1/foo (glob)
463 sub1/foo (glob)
421 sub1/sub1 (glob)
464 sub1/sub1 (glob)
422 sub1/sub2/folder/bar (glob)
465 sub1/sub2/folder/bar (glob)
423 sub1/sub2/x.txt (glob)
466 sub1/sub2/x.txt (glob)
424
467
425 $ hg files sub1/sub2
468 $ hg files sub1/sub2
426 sub1/sub2/folder/bar (glob)
469 sub1/sub2/folder/bar (glob)
427 sub1/sub2/x.txt (glob)
470 sub1/sub2/x.txt (glob)
428
471
429 $ hg files
472 $ hg files
430 .hgsub
473 .hgsub
431 .hgsubstate
474 .hgsubstate
432 foo/bar/abc (glob)
475 foo/bar/abc (glob)
433 main
476 main
434
477
435 $ hg files -S -r '.^' sub1/sub2/folder
478 $ hg files -S -r '.^' sub1/sub2/folder
436 sub1/sub2/folder/test.txt (glob)
479 sub1/sub2/folder/test.txt (glob)
437
480
438 $ hg files -S -r '.^' sub1/sub2/missing
481 $ hg files -S -r '.^' sub1/sub2/missing
439 sub1/sub2/missing: no such file in rev 78026e779ea6 (glob)
482 sub1/sub2/missing: no such file in rev 78026e779ea6 (glob)
440 [1]
483 [1]
441
484
442 $ hg files -r '.^' sub1/
485 $ hg files -r '.^' sub1/
443 sub1/.hgsub (glob)
486 sub1/.hgsub (glob)
444 sub1/.hgsubstate (glob)
487 sub1/.hgsubstate (glob)
445 sub1/sub1 (glob)
488 sub1/sub1 (glob)
446 sub1/sub2/folder/test.txt (glob)
489 sub1/sub2/folder/test.txt (glob)
447 sub1/sub2/sub2 (glob)
490 sub1/sub2/sub2 (glob)
448 sub1/sub2/test.txt (glob)
491 sub1/sub2/test.txt (glob)
449
492
450 $ hg files -r '.^' sub1/sub2
493 $ hg files -r '.^' sub1/sub2
451 sub1/sub2/folder/test.txt (glob)
494 sub1/sub2/folder/test.txt (glob)
452 sub1/sub2/sub2 (glob)
495 sub1/sub2/sub2 (glob)
453 sub1/sub2/test.txt (glob)
496 sub1/sub2/test.txt (glob)
454
497
455 $ hg rollback -q
498 $ hg rollback -q
456 $ hg up -Cq
499 $ hg up -Cq
457
500
458 $ hg --config extensions.largefiles=! archive -S ../archive_all
501 $ hg --config extensions.largefiles=! archive -S ../archive_all
459 \r (no-eol) (esc)
502 \r (no-eol) (esc)
460 archiving [ ] 0/3\r (no-eol) (esc)
503 archiving [ ] 0/3\r (no-eol) (esc)
461 archiving [=============> ] 1/3\r (no-eol) (esc)
504 archiving [=============> ] 1/3\r (no-eol) (esc)
462 archiving [===========================> ] 2/3\r (no-eol) (esc)
505 archiving [===========================> ] 2/3\r (no-eol) (esc)
463 archiving [==========================================>] 3/3\r (no-eol) (esc)
506 archiving [==========================================>] 3/3\r (no-eol) (esc)
464 \r (no-eol) (esc)
507 \r (no-eol) (esc)
465 \r (no-eol) (esc)
508 \r (no-eol) (esc)
466 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
509 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
467 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
510 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
468 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
511 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
469 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
512 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
470 \r (no-eol) (esc)
513 \r (no-eol) (esc)
471 \r (no-eol) (esc)
514 \r (no-eol) (esc)
472 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc)
515 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc)
473 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc)
516 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc)
474 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc)
517 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc)
475 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc)
518 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc)
476 \r (no-eol) (esc)
519 \r (no-eol) (esc)
477 $ find ../archive_all | sort
520 $ find ../archive_all | sort
478 ../archive_all
521 ../archive_all
479 ../archive_all/.hg_archival.txt
522 ../archive_all/.hg_archival.txt
480 ../archive_all/.hgsub
523 ../archive_all/.hgsub
481 ../archive_all/.hgsubstate
524 ../archive_all/.hgsubstate
482 ../archive_all/main
525 ../archive_all/main
483 ../archive_all/sub1
526 ../archive_all/sub1
484 ../archive_all/sub1/.hgsub
527 ../archive_all/sub1/.hgsub
485 ../archive_all/sub1/.hgsubstate
528 ../archive_all/sub1/.hgsubstate
486 ../archive_all/sub1/sub1
529 ../archive_all/sub1/sub1
487 ../archive_all/sub1/sub2
530 ../archive_all/sub1/sub2
488 ../archive_all/sub1/sub2/folder
531 ../archive_all/sub1/sub2/folder
489 ../archive_all/sub1/sub2/folder/test.txt
532 ../archive_all/sub1/sub2/folder/test.txt
490 ../archive_all/sub1/sub2/sub2
533 ../archive_all/sub1/sub2/sub2
491 ../archive_all/sub1/sub2/test.txt
534 ../archive_all/sub1/sub2/test.txt
492
535
493 Check that archive -X works in deep subrepos
536 Check that archive -X works in deep subrepos
494
537
495 $ hg --config extensions.largefiles=! archive -S -X '**test*' ../archive_exclude
538 $ hg --config extensions.largefiles=! archive -S -X '**test*' ../archive_exclude
496 \r (no-eol) (esc)
539 \r (no-eol) (esc)
497 archiving [ ] 0/3\r (no-eol) (esc)
540 archiving [ ] 0/3\r (no-eol) (esc)
498 archiving [=============> ] 1/3\r (no-eol) (esc)
541 archiving [=============> ] 1/3\r (no-eol) (esc)
499 archiving [===========================> ] 2/3\r (no-eol) (esc)
542 archiving [===========================> ] 2/3\r (no-eol) (esc)
500 archiving [==========================================>] 3/3\r (no-eol) (esc)
543 archiving [==========================================>] 3/3\r (no-eol) (esc)
501 \r (no-eol) (esc)
544 \r (no-eol) (esc)
502 \r (no-eol) (esc)
545 \r (no-eol) (esc)
503 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
546 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
504 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
547 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
505 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
548 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
506 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
549 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
507 \r (no-eol) (esc)
550 \r (no-eol) (esc)
508 \r (no-eol) (esc)
551 \r (no-eol) (esc)
509 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc)
552 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc)
510 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc)
553 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc)
511 \r (no-eol) (esc)
554 \r (no-eol) (esc)
512 $ find ../archive_exclude | sort
555 $ find ../archive_exclude | sort
513 ../archive_exclude
556 ../archive_exclude
514 ../archive_exclude/.hg_archival.txt
557 ../archive_exclude/.hg_archival.txt
515 ../archive_exclude/.hgsub
558 ../archive_exclude/.hgsub
516 ../archive_exclude/.hgsubstate
559 ../archive_exclude/.hgsubstate
517 ../archive_exclude/main
560 ../archive_exclude/main
518 ../archive_exclude/sub1
561 ../archive_exclude/sub1
519 ../archive_exclude/sub1/.hgsub
562 ../archive_exclude/sub1/.hgsub
520 ../archive_exclude/sub1/.hgsubstate
563 ../archive_exclude/sub1/.hgsubstate
521 ../archive_exclude/sub1/sub1
564 ../archive_exclude/sub1/sub1
522 ../archive_exclude/sub1/sub2
565 ../archive_exclude/sub1/sub2
523 ../archive_exclude/sub1/sub2/sub2
566 ../archive_exclude/sub1/sub2/sub2
524
567
525 $ hg --config extensions.largefiles=! archive -S -I '**test*' ../archive_include
568 $ hg --config extensions.largefiles=! archive -S -I '**test*' ../archive_include
526 \r (no-eol) (esc)
569 \r (no-eol) (esc)
527 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
570 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
528 \r (no-eol) (esc)
571 \r (no-eol) (esc)
529 \r (no-eol) (esc)
572 \r (no-eol) (esc)
530 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc)
573 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc)
531 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc)
574 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc)
532 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc)
575 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc)
533 \r (no-eol) (esc)
576 \r (no-eol) (esc)
534 $ find ../archive_include | sort
577 $ find ../archive_include | sort
535 ../archive_include
578 ../archive_include
536 ../archive_include/sub1
579 ../archive_include/sub1
537 ../archive_include/sub1/sub2
580 ../archive_include/sub1/sub2
538 ../archive_include/sub1/sub2/folder
581 ../archive_include/sub1/sub2/folder
539 ../archive_include/sub1/sub2/folder/test.txt
582 ../archive_include/sub1/sub2/folder/test.txt
540 ../archive_include/sub1/sub2/test.txt
583 ../archive_include/sub1/sub2/test.txt
541
584
542 Check that deep archive works with largefiles (which overrides hgsubrepo impl)
585 Check that deep archive works with largefiles (which overrides hgsubrepo impl)
543 This also tests the repo.ui regression in 43fb170a23bd, and that lf subrepo
586 This also tests the repo.ui regression in 43fb170a23bd, and that lf subrepo
544 subrepos are archived properly.
587 subrepos are archived properly.
545 Note that add --large through a subrepo currently adds the file as a normal file
588 Note that add --large through a subrepo currently adds the file as a normal file
546
589
547 $ echo "large" > sub1/sub2/large.bin
590 $ echo "large" > sub1/sub2/large.bin
548 $ hg --config extensions.largefiles= add --large -R sub1/sub2 sub1/sub2/large.bin
591 $ hg --config extensions.largefiles= add --large -R sub1/sub2 sub1/sub2/large.bin
549 $ echo "large" > large.bin
592 $ echo "large" > large.bin
550 $ hg --config extensions.largefiles= add --large large.bin
593 $ hg --config extensions.largefiles= add --large large.bin
551 $ hg --config extensions.largefiles= ci -S -m "add large files"
594 $ hg --config extensions.largefiles= ci -S -m "add large files"
552 committing subrepository sub1
595 committing subrepository sub1
553 committing subrepository sub1/sub2 (glob)
596 committing subrepository sub1/sub2 (glob)
554
597
555 $ hg --config extensions.largefiles= archive -S ../archive_lf
598 $ hg --config extensions.largefiles= archive -S ../archive_lf
556 $ find ../archive_lf | sort
599 $ find ../archive_lf | sort
557 ../archive_lf
600 ../archive_lf
558 ../archive_lf/.hg_archival.txt
601 ../archive_lf/.hg_archival.txt
559 ../archive_lf/.hgsub
602 ../archive_lf/.hgsub
560 ../archive_lf/.hgsubstate
603 ../archive_lf/.hgsubstate
561 ../archive_lf/large.bin
604 ../archive_lf/large.bin
562 ../archive_lf/main
605 ../archive_lf/main
563 ../archive_lf/sub1
606 ../archive_lf/sub1
564 ../archive_lf/sub1/.hgsub
607 ../archive_lf/sub1/.hgsub
565 ../archive_lf/sub1/.hgsubstate
608 ../archive_lf/sub1/.hgsubstate
566 ../archive_lf/sub1/sub1
609 ../archive_lf/sub1/sub1
567 ../archive_lf/sub1/sub2
610 ../archive_lf/sub1/sub2
568 ../archive_lf/sub1/sub2/folder
611 ../archive_lf/sub1/sub2/folder
569 ../archive_lf/sub1/sub2/folder/test.txt
612 ../archive_lf/sub1/sub2/folder/test.txt
570 ../archive_lf/sub1/sub2/large.bin
613 ../archive_lf/sub1/sub2/large.bin
571 ../archive_lf/sub1/sub2/sub2
614 ../archive_lf/sub1/sub2/sub2
572 ../archive_lf/sub1/sub2/test.txt
615 ../archive_lf/sub1/sub2/test.txt
573 $ rm -rf ../archive_lf
616 $ rm -rf ../archive_lf
574
617
575 Exclude large files from main and sub-sub repo
618 Exclude large files from main and sub-sub repo
576
619
577 $ hg --config extensions.largefiles= archive -S -X '**.bin' ../archive_lf
620 $ hg --config extensions.largefiles= archive -S -X '**.bin' ../archive_lf
578 $ find ../archive_lf | sort
621 $ find ../archive_lf | sort
579 ../archive_lf
622 ../archive_lf
580 ../archive_lf/.hg_archival.txt
623 ../archive_lf/.hg_archival.txt
581 ../archive_lf/.hgsub
624 ../archive_lf/.hgsub
582 ../archive_lf/.hgsubstate
625 ../archive_lf/.hgsubstate
583 ../archive_lf/main
626 ../archive_lf/main
584 ../archive_lf/sub1
627 ../archive_lf/sub1
585 ../archive_lf/sub1/.hgsub
628 ../archive_lf/sub1/.hgsub
586 ../archive_lf/sub1/.hgsubstate
629 ../archive_lf/sub1/.hgsubstate
587 ../archive_lf/sub1/sub1
630 ../archive_lf/sub1/sub1
588 ../archive_lf/sub1/sub2
631 ../archive_lf/sub1/sub2
589 ../archive_lf/sub1/sub2/folder
632 ../archive_lf/sub1/sub2/folder
590 ../archive_lf/sub1/sub2/folder/test.txt
633 ../archive_lf/sub1/sub2/folder/test.txt
591 ../archive_lf/sub1/sub2/sub2
634 ../archive_lf/sub1/sub2/sub2
592 ../archive_lf/sub1/sub2/test.txt
635 ../archive_lf/sub1/sub2/test.txt
593 $ rm -rf ../archive_lf
636 $ rm -rf ../archive_lf
594
637
595 Exclude normal files from main and sub-sub repo
638 Exclude normal files from main and sub-sub repo
596
639
597 $ hg --config extensions.largefiles= archive -S -X '**.txt' -p '.' ../archive_lf.tgz
640 $ hg --config extensions.largefiles= archive -S -X '**.txt' -p '.' ../archive_lf.tgz
598 $ tar -tzf ../archive_lf.tgz | sort
641 $ tar -tzf ../archive_lf.tgz | sort
599 .hgsub
642 .hgsub
600 .hgsubstate
643 .hgsubstate
601 large.bin
644 large.bin
602 main
645 main
603 sub1/.hgsub
646 sub1/.hgsub
604 sub1/.hgsubstate
647 sub1/.hgsubstate
605 sub1/sub1
648 sub1/sub1
606 sub1/sub2/large.bin
649 sub1/sub2/large.bin
607 sub1/sub2/sub2
650 sub1/sub2/sub2
608
651
609 Include normal files from within a largefiles subrepo
652 Include normal files from within a largefiles subrepo
610
653
611 $ hg --config extensions.largefiles= archive -S -I '**.txt' ../archive_lf
654 $ hg --config extensions.largefiles= archive -S -I '**.txt' ../archive_lf
612 $ find ../archive_lf | sort
655 $ find ../archive_lf | sort
613 ../archive_lf
656 ../archive_lf
614 ../archive_lf/.hg_archival.txt
657 ../archive_lf/.hg_archival.txt
615 ../archive_lf/sub1
658 ../archive_lf/sub1
616 ../archive_lf/sub1/sub2
659 ../archive_lf/sub1/sub2
617 ../archive_lf/sub1/sub2/folder
660 ../archive_lf/sub1/sub2/folder
618 ../archive_lf/sub1/sub2/folder/test.txt
661 ../archive_lf/sub1/sub2/folder/test.txt
619 ../archive_lf/sub1/sub2/test.txt
662 ../archive_lf/sub1/sub2/test.txt
620 $ rm -rf ../archive_lf
663 $ rm -rf ../archive_lf
621
664
622 Include large files from within a largefiles subrepo
665 Include large files from within a largefiles subrepo
623
666
624 $ hg --config extensions.largefiles= archive -S -I '**.bin' ../archive_lf
667 $ hg --config extensions.largefiles= archive -S -I '**.bin' ../archive_lf
625 $ find ../archive_lf | sort
668 $ find ../archive_lf | sort
626 ../archive_lf
669 ../archive_lf
627 ../archive_lf/large.bin
670 ../archive_lf/large.bin
628 ../archive_lf/sub1
671 ../archive_lf/sub1
629 ../archive_lf/sub1/sub2
672 ../archive_lf/sub1/sub2
630 ../archive_lf/sub1/sub2/large.bin
673 ../archive_lf/sub1/sub2/large.bin
631 $ rm -rf ../archive_lf
674 $ rm -rf ../archive_lf
632
675
633 Find an exact largefile match in a largefiles subrepo
676 Find an exact largefile match in a largefiles subrepo
634
677
635 $ hg --config extensions.largefiles= archive -S -I 'sub1/sub2/large.bin' ../archive_lf
678 $ hg --config extensions.largefiles= archive -S -I 'sub1/sub2/large.bin' ../archive_lf
636 $ find ../archive_lf | sort
679 $ find ../archive_lf | sort
637 ../archive_lf
680 ../archive_lf
638 ../archive_lf/sub1
681 ../archive_lf/sub1
639 ../archive_lf/sub1/sub2
682 ../archive_lf/sub1/sub2
640 ../archive_lf/sub1/sub2/large.bin
683 ../archive_lf/sub1/sub2/large.bin
641 $ rm -rf ../archive_lf
684 $ rm -rf ../archive_lf
642
685
643 The local repo enables largefiles if a largefiles repo is cloned
686 The local repo enables largefiles if a largefiles repo is cloned
644 $ hg showconfig extensions
687 $ hg showconfig extensions
645 abort: repository requires features unknown to this Mercurial: largefiles!
688 abort: repository requires features unknown to this Mercurial: largefiles!
646 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
689 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
647 [255]
690 [255]
648 $ hg --config extensions.largefiles= clone -qU . ../lfclone
691 $ hg --config extensions.largefiles= clone -qU . ../lfclone
649 $ cat ../lfclone/.hg/hgrc
692 $ cat ../lfclone/.hg/hgrc
650 # example repository config (see "hg help config" for more info)
693 # example repository config (see "hg help config" for more info)
651 [paths]
694 [paths]
652 default = $TESTTMP/cloned (glob)
695 default = $TESTTMP/cloned (glob)
653
696
654 # path aliases to other clones of this repo in URLs or filesystem paths
697 # path aliases to other clones of this repo in URLs or filesystem paths
655 # (see "hg help config.paths" for more info)
698 # (see "hg help config.paths" for more info)
656 #
699 #
657 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
700 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
658 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
701 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
659 # my-clone = /home/jdoe/jdoes-clone
702 # my-clone = /home/jdoe/jdoes-clone
660
703
661 [ui]
704 [ui]
662 # name and email (local to this repository, optional), e.g.
705 # name and email (local to this repository, optional), e.g.
663 # username = Jane Doe <jdoe@example.com>
706 # username = Jane Doe <jdoe@example.com>
664
707
665 [extensions]
708 [extensions]
666 largefiles=
709 largefiles=
667
710
668 Find an exact match to a standin (should archive nothing)
711 Find an exact match to a standin (should archive nothing)
669 $ hg --config extensions.largefiles= archive -S -I 'sub/sub2/.hglf/large.bin' ../archive_lf
712 $ hg --config extensions.largefiles= archive -S -I 'sub/sub2/.hglf/large.bin' ../archive_lf
670 $ find ../archive_lf 2> /dev/null | sort
713 $ find ../archive_lf 2> /dev/null | sort
671
714
672 $ cat >> $HGRCPATH <<EOF
715 $ cat >> $HGRCPATH <<EOF
673 > [extensions]
716 > [extensions]
674 > largefiles=
717 > largefiles=
675 > [largefiles]
718 > [largefiles]
676 > patterns=glob:**.dat
719 > patterns=glob:**.dat
677 > EOF
720 > EOF
678
721
679 Test forget through a deep subrepo with the largefiles extension, both a
722 Test forget through a deep subrepo with the largefiles extension, both a
680 largefile and a normal file. Then a largefile that hasn't been committed yet.
723 largefile and a normal file. Then a largefile that hasn't been committed yet.
681 $ touch sub1/sub2/untracked.txt
724 $ touch sub1/sub2/untracked.txt
682 $ touch sub1/sub2/large.dat
725 $ touch sub1/sub2/large.dat
683 $ hg forget sub1/sub2/large.bin sub1/sub2/test.txt sub1/sub2/untracked.txt
726 $ hg forget sub1/sub2/large.bin sub1/sub2/test.txt sub1/sub2/untracked.txt
684 not removing sub1/sub2/untracked.txt: file is already untracked (glob)
727 not removing sub1/sub2/untracked.txt: file is already untracked (glob)
685 [1]
728 [1]
686 $ hg add --large --dry-run -v sub1/sub2/untracked.txt
729 $ hg add --large --dry-run -v sub1/sub2/untracked.txt
687 adding sub1/sub2/untracked.txt as a largefile (glob)
730 adding sub1/sub2/untracked.txt as a largefile (glob)
688 $ hg add --large -v sub1/sub2/untracked.txt
731 $ hg add --large -v sub1/sub2/untracked.txt
689 adding sub1/sub2/untracked.txt as a largefile (glob)
732 adding sub1/sub2/untracked.txt as a largefile (glob)
690 $ hg add --normal -v sub1/sub2/large.dat
733 $ hg add --normal -v sub1/sub2/large.dat
691 adding sub1/sub2/large.dat (glob)
734 adding sub1/sub2/large.dat (glob)
692 $ hg forget -v sub1/sub2/untracked.txt
735 $ hg forget -v sub1/sub2/untracked.txt
693 removing sub1/sub2/untracked.txt (glob)
736 removing sub1/sub2/untracked.txt (glob)
694 $ hg status -S
737 $ hg status -S
695 A sub1/sub2/large.dat
738 A sub1/sub2/large.dat
696 R sub1/sub2/large.bin
739 R sub1/sub2/large.bin
697 R sub1/sub2/test.txt
740 R sub1/sub2/test.txt
698 ? foo/bar/abc
741 ? foo/bar/abc
699 ? sub1/sub2/untracked.txt
742 ? sub1/sub2/untracked.txt
700 ? sub1/sub2/x.txt
743 ? sub1/sub2/x.txt
701 $ hg add sub1/sub2
744 $ hg add sub1/sub2
702
745
703 $ hg archive -S -r 'wdir()' ../wdir2
746 $ hg archive -S -r 'wdir()' ../wdir2
704 $ diff -r . ../wdir2 | egrep -v '\.hg$|^Common subdirectories:'
747 $ diff -r . ../wdir2 | egrep -v '\.hg$|^Common subdirectories:'
705 Only in ../wdir2: .hg_archival.txt
748 Only in ../wdir2: .hg_archival.txt
706 Only in .: .hglf
749 Only in .: .hglf
707 Only in .: foo
750 Only in .: foo
708 Only in ./sub1/sub2: large.bin
751 Only in ./sub1/sub2: large.bin
709 Only in ./sub1/sub2: test.txt
752 Only in ./sub1/sub2: test.txt
710 Only in ./sub1/sub2: untracked.txt
753 Only in ./sub1/sub2: untracked.txt
711 Only in ./sub1/sub2: x.txt
754 Only in ./sub1/sub2: x.txt
712 $ find ../wdir2 -type f | sort
755 $ find ../wdir2 -type f | sort
713 ../wdir2/.hg_archival.txt
756 ../wdir2/.hg_archival.txt
714 ../wdir2/.hgsub
757 ../wdir2/.hgsub
715 ../wdir2/.hgsubstate
758 ../wdir2/.hgsubstate
716 ../wdir2/large.bin
759 ../wdir2/large.bin
717 ../wdir2/main
760 ../wdir2/main
718 ../wdir2/sub1/.hgsub
761 ../wdir2/sub1/.hgsub
719 ../wdir2/sub1/.hgsubstate
762 ../wdir2/sub1/.hgsubstate
720 ../wdir2/sub1/sub1
763 ../wdir2/sub1/sub1
721 ../wdir2/sub1/sub2/folder/test.txt
764 ../wdir2/sub1/sub2/folder/test.txt
722 ../wdir2/sub1/sub2/large.dat
765 ../wdir2/sub1/sub2/large.dat
723 ../wdir2/sub1/sub2/sub2
766 ../wdir2/sub1/sub2/sub2
724 $ hg status -S -mac -n | sort
767 $ hg status -S -mac -n | sort
725 .hgsub
768 .hgsub
726 .hgsubstate
769 .hgsubstate
727 large.bin
770 large.bin
728 main
771 main
729 sub1/.hgsub
772 sub1/.hgsub
730 sub1/.hgsubstate
773 sub1/.hgsubstate
731 sub1/sub1
774 sub1/sub1
732 sub1/sub2/folder/test.txt
775 sub1/sub2/folder/test.txt
733 sub1/sub2/large.dat
776 sub1/sub2/large.dat
734 sub1/sub2/sub2
777 sub1/sub2/sub2
735
778
736 $ hg ci -Sqm 'forget testing'
779 $ hg ci -Sqm 'forget testing'
737
780
738 Test 'wdir()' modified file archiving with largefiles
781 Test 'wdir()' modified file archiving with largefiles
739 $ echo 'mod' > main
782 $ echo 'mod' > main
740 $ echo 'mod' > large.bin
783 $ echo 'mod' > large.bin
741 $ echo 'mod' > sub1/sub2/large.dat
784 $ echo 'mod' > sub1/sub2/large.dat
742 $ hg archive -S -r 'wdir()' ../wdir3
785 $ hg archive -S -r 'wdir()' ../wdir3
743 $ diff -r . ../wdir3 | egrep -v '\.hg$|^Common subdirectories'
786 $ diff -r . ../wdir3 | egrep -v '\.hg$|^Common subdirectories'
744 Only in ../wdir3: .hg_archival.txt
787 Only in ../wdir3: .hg_archival.txt
745 Only in .: .hglf
788 Only in .: .hglf
746 Only in .: foo
789 Only in .: foo
747 Only in ./sub1/sub2: large.bin
790 Only in ./sub1/sub2: large.bin
748 Only in ./sub1/sub2: test.txt
791 Only in ./sub1/sub2: test.txt
749 Only in ./sub1/sub2: untracked.txt
792 Only in ./sub1/sub2: untracked.txt
750 Only in ./sub1/sub2: x.txt
793 Only in ./sub1/sub2: x.txt
751 $ find ../wdir3 -type f | sort
794 $ find ../wdir3 -type f | sort
752 ../wdir3/.hg_archival.txt
795 ../wdir3/.hg_archival.txt
753 ../wdir3/.hgsub
796 ../wdir3/.hgsub
754 ../wdir3/.hgsubstate
797 ../wdir3/.hgsubstate
755 ../wdir3/large.bin
798 ../wdir3/large.bin
756 ../wdir3/main
799 ../wdir3/main
757 ../wdir3/sub1/.hgsub
800 ../wdir3/sub1/.hgsub
758 ../wdir3/sub1/.hgsubstate
801 ../wdir3/sub1/.hgsubstate
759 ../wdir3/sub1/sub1
802 ../wdir3/sub1/sub1
760 ../wdir3/sub1/sub2/folder/test.txt
803 ../wdir3/sub1/sub2/folder/test.txt
761 ../wdir3/sub1/sub2/large.dat
804 ../wdir3/sub1/sub2/large.dat
762 ../wdir3/sub1/sub2/sub2
805 ../wdir3/sub1/sub2/sub2
763 $ hg up -Cq
806 $ hg up -Cq
764
807
765 Test issue4330: commit a directory where only normal files have changed
808 Test issue4330: commit a directory where only normal files have changed
766 $ touch foo/bar/large.dat
809 $ touch foo/bar/large.dat
767 $ hg add --large foo/bar/large.dat
810 $ hg add --large foo/bar/large.dat
768 $ hg ci -m 'add foo/bar/large.dat'
811 $ hg ci -m 'add foo/bar/large.dat'
769 $ touch a.txt
812 $ touch a.txt
770 $ touch a.dat
813 $ touch a.dat
771 $ hg add -v foo/bar/abc a.txt a.dat
814 $ hg add -v foo/bar/abc a.txt a.dat
772 adding a.dat as a largefile
815 adding a.dat as a largefile
773 adding a.txt
816 adding a.txt
774 adding foo/bar/abc (glob)
817 adding foo/bar/abc (glob)
775 $ hg ci -m 'dir commit with only normal file deltas' foo/bar
818 $ hg ci -m 'dir commit with only normal file deltas' foo/bar
776 $ hg status
819 $ hg status
777 A a.dat
820 A a.dat
778 A a.txt
821 A a.txt
779
822
780 Test a directory commit with a changed largefile and a changed normal file
823 Test a directory commit with a changed largefile and a changed normal file
781 $ echo changed > foo/bar/large.dat
824 $ echo changed > foo/bar/large.dat
782 $ echo changed > foo/bar/abc
825 $ echo changed > foo/bar/abc
783 $ hg ci -m 'dir commit with normal and lf file deltas' foo
826 $ hg ci -m 'dir commit with normal and lf file deltas' foo
784 $ hg status
827 $ hg status
785 A a.dat
828 A a.dat
786 A a.txt
829 A a.txt
787
830
788 $ hg ci -m "add a.*"
831 $ hg ci -m "add a.*"
789 $ hg mv a.dat b.dat
832 $ hg mv a.dat b.dat
790 $ hg mv foo/bar/abc foo/bar/def
833 $ hg mv foo/bar/abc foo/bar/def
791 $ hg status -C
834 $ hg status -C
792 A b.dat
835 A b.dat
793 a.dat
836 a.dat
794 A foo/bar/def
837 A foo/bar/def
795 foo/bar/abc
838 foo/bar/abc
796 R a.dat
839 R a.dat
797 R foo/bar/abc
840 R foo/bar/abc
798
841
799 $ hg ci -m "move large and normal"
842 $ hg ci -m "move large and normal"
800 $ hg status -C --rev '.^' --rev .
843 $ hg status -C --rev '.^' --rev .
801 A b.dat
844 A b.dat
802 a.dat
845 a.dat
803 A foo/bar/def
846 A foo/bar/def
804 foo/bar/abc
847 foo/bar/abc
805 R a.dat
848 R a.dat
806 R foo/bar/abc
849 R foo/bar/abc
807
850
808
851
809 $ echo foo > main
852 $ echo foo > main
810 $ hg ci -m "mod parent only"
853 $ hg ci -m "mod parent only"
811 $ hg init sub3
854 $ hg init sub3
812 $ echo "sub3 = sub3" >> .hgsub
855 $ echo "sub3 = sub3" >> .hgsub
813 $ echo xyz > sub3/a.txt
856 $ echo xyz > sub3/a.txt
814 $ hg add sub3/a.txt
857 $ hg add sub3/a.txt
815 $ hg ci -Sm "add sub3"
858 $ hg ci -Sm "add sub3"
816 committing subrepository sub3
859 committing subrepository sub3
817 $ cat .hgsub | grep -v sub3 > .hgsub1
860 $ cat .hgsub | grep -v sub3 > .hgsub1
818 $ mv .hgsub1 .hgsub
861 $ mv .hgsub1 .hgsub
819 $ hg ci -m "remove sub3"
862 $ hg ci -m "remove sub3"
820
863
821 $ hg log -r "subrepo()" --style compact
864 $ hg log -r "subrepo()" --style compact
822 0 7f491f53a367 1970-01-01 00:00 +0000 test
865 0 7f491f53a367 1970-01-01 00:00 +0000 test
823 main import
866 main import
824
867
825 1 ffe6649062fe 1970-01-01 00:00 +0000 test
868 1 ffe6649062fe 1970-01-01 00:00 +0000 test
826 deep nested modif should trigger a commit
869 deep nested modif should trigger a commit
827
870
828 2 9bb10eebee29 1970-01-01 00:00 +0000 test
871 2 9bb10eebee29 1970-01-01 00:00 +0000 test
829 add test.txt
872 add test.txt
830
873
831 3 7c64f035294f 1970-01-01 00:00 +0000 test
874 3 7c64f035294f 1970-01-01 00:00 +0000 test
832 add large files
875 add large files
833
876
834 4 f734a59e2e35 1970-01-01 00:00 +0000 test
877 4 f734a59e2e35 1970-01-01 00:00 +0000 test
835 forget testing
878 forget testing
836
879
837 11 9685a22af5db 1970-01-01 00:00 +0000 test
880 11 9685a22af5db 1970-01-01 00:00 +0000 test
838 add sub3
881 add sub3
839
882
840 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
883 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
841 remove sub3
884 remove sub3
842
885
843 $ hg log -r "subrepo('sub3')" --style compact
886 $ hg log -r "subrepo('sub3')" --style compact
844 11 9685a22af5db 1970-01-01 00:00 +0000 test
887 11 9685a22af5db 1970-01-01 00:00 +0000 test
845 add sub3
888 add sub3
846
889
847 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
890 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
848 remove sub3
891 remove sub3
849
892
850 $ hg log -r "subrepo('bogus')" --style compact
893 $ hg log -r "subrepo('bogus')" --style compact
851
894
852
895
853 Test .hgsubstate in the R state
896 Test .hgsubstate in the R state
854
897
855 $ hg rm .hgsub .hgsubstate
898 $ hg rm .hgsub .hgsubstate
899 \r (no-eol) (esc)
900 deleting [=====================> ] 1/2\r (no-eol) (esc)
901 deleting [===========================================>] 2/2\r (no-eol) (esc)
902 \r (no-eol) (esc)
856 $ hg ci -m 'trash subrepo tracking'
903 $ hg ci -m 'trash subrepo tracking'
857
904
858 $ hg log -r "subrepo('re:sub\d+')" --style compact
905 $ hg log -r "subrepo('re:sub\d+')" --style compact
859 0 7f491f53a367 1970-01-01 00:00 +0000 test
906 0 7f491f53a367 1970-01-01 00:00 +0000 test
860 main import
907 main import
861
908
862 1 ffe6649062fe 1970-01-01 00:00 +0000 test
909 1 ffe6649062fe 1970-01-01 00:00 +0000 test
863 deep nested modif should trigger a commit
910 deep nested modif should trigger a commit
864
911
865 2 9bb10eebee29 1970-01-01 00:00 +0000 test
912 2 9bb10eebee29 1970-01-01 00:00 +0000 test
866 add test.txt
913 add test.txt
867
914
868 3 7c64f035294f 1970-01-01 00:00 +0000 test
915 3 7c64f035294f 1970-01-01 00:00 +0000 test
869 add large files
916 add large files
870
917
871 4 f734a59e2e35 1970-01-01 00:00 +0000 test
918 4 f734a59e2e35 1970-01-01 00:00 +0000 test
872 forget testing
919 forget testing
873
920
874 11 9685a22af5db 1970-01-01 00:00 +0000 test
921 11 9685a22af5db 1970-01-01 00:00 +0000 test
875 add sub3
922 add sub3
876
923
877 12 2e0485b475b9 1970-01-01 00:00 +0000 test
924 12 2e0485b475b9 1970-01-01 00:00 +0000 test
878 remove sub3
925 remove sub3
879
926
880 13[tip] a68b2c361653 1970-01-01 00:00 +0000 test
927 13[tip] a68b2c361653 1970-01-01 00:00 +0000 test
881 trash subrepo tracking
928 trash subrepo tracking
882
929
883
930
884 Restore the trashed subrepo tracking
931 Restore the trashed subrepo tracking
885
932
886 $ hg rollback -q
933 $ hg rollback -q
887 $ hg update -Cq .
934 $ hg update -Cq .
888
935
889 Interaction with extdiff, largefiles and subrepos
936 Interaction with extdiff, largefiles and subrepos
890
937
891 $ hg --config extensions.extdiff= pdiff -S
938 $ hg --config extensions.extdiff= pdiff -S
892
939
893 $ hg --config extensions.extdiff= pdiff -r '.^' -S
940 $ hg --config extensions.extdiff= pdiff -r '.^' -S
894 \r (no-eol) (esc)
941 \r (no-eol) (esc)
895 archiving [ ] 0/2\r (no-eol) (esc)
942 archiving [ ] 0/2\r (no-eol) (esc)
896 archiving [====================> ] 1/2\r (no-eol) (esc)
943 archiving [====================> ] 1/2\r (no-eol) (esc)
897 archiving [==========================================>] 2/2\r (no-eol) (esc)
944 archiving [==========================================>] 2/2\r (no-eol) (esc)
898 \r (no-eol) (esc)
945 \r (no-eol) (esc)
899 \r (no-eol) (esc)
946 \r (no-eol) (esc)
900 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
947 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
901 \r (no-eol) (esc)
948 \r (no-eol) (esc)
902 \r (no-eol) (esc)
949 \r (no-eol) (esc)
903 archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (esc)
950 archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (esc)
904 \r (no-eol) (esc)
951 \r (no-eol) (esc)
905 \r (no-eol) (esc)
952 \r (no-eol) (esc)
906 archiving (sub3) [ <=> ] 0\r (no-eol) (esc)
953 archiving (sub3) [ <=> ] 0\r (no-eol) (esc)
907 \r (no-eol) (esc)
954 \r (no-eol) (esc)
908 \r (no-eol) (esc)
955 \r (no-eol) (esc)
909 archiving [ ] 0/2\r (no-eol) (esc)
956 archiving [ ] 0/2\r (no-eol) (esc)
910 archiving [====================> ] 1/2\r (no-eol) (esc)
957 archiving [====================> ] 1/2\r (no-eol) (esc)
911 archiving [==========================================>] 2/2\r (no-eol) (esc)
958 archiving [==========================================>] 2/2\r (no-eol) (esc)
912 \r (no-eol) (esc)
959 \r (no-eol) (esc)
913 \r (no-eol) (esc)
960 \r (no-eol) (esc)
914 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
961 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
915 \r (no-eol) (esc)
962 \r (no-eol) (esc)
916 \r (no-eol) (esc)
963 \r (no-eol) (esc)
917 archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (esc)
964 archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (esc)
918 \r (no-eol) (esc)
965 \r (no-eol) (esc)
919 diff -Nru cloned.*/.hgsub cloned/.hgsub (glob)
966 diff -Nru cloned.*/.hgsub cloned/.hgsub (glob)
920 --- cloned.*/.hgsub * (glob)
967 --- cloned.*/.hgsub * (glob)
921 +++ cloned/.hgsub * (glob)
968 +++ cloned/.hgsub * (glob)
922 @@ -1,2 +1* @@ (glob)
969 @@ -1,2 +1* @@ (glob)
923 sub1 = ../sub1
970 sub1 = ../sub1
924 -sub3 = sub3
971 -sub3 = sub3
925 diff -Nru cloned.*/.hgsubstate cloned/.hgsubstate (glob)
972 diff -Nru cloned.*/.hgsubstate cloned/.hgsubstate (glob)
926 --- cloned.*/.hgsubstate * (glob)
973 --- cloned.*/.hgsubstate * (glob)
927 +++ cloned/.hgsubstate * (glob)
974 +++ cloned/.hgsubstate * (glob)
928 @@ -1,2 +1* @@ (glob)
975 @@ -1,2 +1* @@ (glob)
929 7a36fa02b66e61f27f3d4a822809f159479b8ab2 sub1
976 7a36fa02b66e61f27f3d4a822809f159479b8ab2 sub1
930 -b1a26de6f2a045a9f079323693614ee322f1ff7e sub3
977 -b1a26de6f2a045a9f079323693614ee322f1ff7e sub3
931 [1]
978 [1]
932
979
933 $ hg --config extensions.extdiff= pdiff -r 0 -r '.^' -S
980 $ hg --config extensions.extdiff= pdiff -r 0 -r '.^' -S
934 \r (no-eol) (esc)
981 \r (no-eol) (esc)
935 archiving [ ] 0/3\r (no-eol) (esc)
982 archiving [ ] 0/3\r (no-eol) (esc)
936 archiving [=============> ] 1/3\r (no-eol) (esc)
983 archiving [=============> ] 1/3\r (no-eol) (esc)
937 archiving [===========================> ] 2/3\r (no-eol) (esc)
984 archiving [===========================> ] 2/3\r (no-eol) (esc)
938 archiving [==========================================>] 3/3\r (no-eol) (esc)
985 archiving [==========================================>] 3/3\r (no-eol) (esc)
939 \r (no-eol) (esc)
986 \r (no-eol) (esc)
940 \r (no-eol) (esc)
987 \r (no-eol) (esc)
941 archiving (sub1) [ ] 0/1\r (no-eol) (esc)
988 archiving (sub1) [ ] 0/1\r (no-eol) (esc)
942 archiving (sub1) [===================================>] 1/1\r (no-eol) (esc)
989 archiving (sub1) [===================================>] 1/1\r (no-eol) (esc)
943 \r (no-eol) (esc)
990 \r (no-eol) (esc)
944 \r (no-eol) (esc)
991 \r (no-eol) (esc)
945 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc)
992 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc)
946 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc)
993 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc)
947 \r (no-eol) (esc)
994 \r (no-eol) (esc)
948 \r (no-eol) (esc)
995 \r (no-eol) (esc)
949 archiving [ ] 0/8\r (no-eol) (esc)
996 archiving [ ] 0/8\r (no-eol) (esc)
950 archiving [====> ] 1/8\r (no-eol) (esc)
997 archiving [====> ] 1/8\r (no-eol) (esc)
951 archiving [=========> ] 2/8\r (no-eol) (esc)
998 archiving [=========> ] 2/8\r (no-eol) (esc)
952 archiving [===============> ] 3/8\r (no-eol) (esc)
999 archiving [===============> ] 3/8\r (no-eol) (esc)
953 archiving [====================> ] 4/8\r (no-eol) (esc)
1000 archiving [====================> ] 4/8\r (no-eol) (esc)
954 archiving [=========================> ] 5/8\r (no-eol) (esc)
1001 archiving [=========================> ] 5/8\r (no-eol) (esc)
955 archiving [===============================> ] 6/8\r (no-eol) (esc)
1002 archiving [===============================> ] 6/8\r (no-eol) (esc)
956 archiving [====================================> ] 7/8\r (no-eol) (esc)
1003 archiving [====================================> ] 7/8\r (no-eol) (esc)
957 archiving [==========================================>] 8/8\r (no-eol) (esc)
1004 archiving [==========================================>] 8/8\r (no-eol) (esc)
958 \r (no-eol) (esc)
1005 \r (no-eol) (esc)
959 \r (no-eol) (esc)
1006 \r (no-eol) (esc)
960 archiving (sub1) [ ] 0/1\r (no-eol) (esc)
1007 archiving (sub1) [ ] 0/1\r (no-eol) (esc)
961 archiving (sub1) [===================================>] 1/1\r (no-eol) (esc)
1008 archiving (sub1) [===================================>] 1/1\r (no-eol) (esc)
962 \r (no-eol) (esc)
1009 \r (no-eol) (esc)
963 \r (no-eol) (esc)
1010 \r (no-eol) (esc)
964 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc)
1011 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc)
965 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc)
1012 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc)
966 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc)
1013 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc)
967 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc)
1014 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc)
968 \r (no-eol) (esc)
1015 \r (no-eol) (esc)
969 \r (no-eol) (esc)
1016 \r (no-eol) (esc)
970 archiving (sub3) [ ] 0/1\r (no-eol) (esc)
1017 archiving (sub3) [ ] 0/1\r (no-eol) (esc)
971 archiving (sub3) [===================================>] 1/1\r (no-eol) (esc)
1018 archiving (sub3) [===================================>] 1/1\r (no-eol) (esc)
972 \r (no-eol) (esc)
1019 \r (no-eol) (esc)
973 diff -Nru cloned.*/.hglf/b.dat cloned.*/.hglf/b.dat (glob)
1020 diff -Nru cloned.*/.hglf/b.dat cloned.*/.hglf/b.dat (glob)
974 --- cloned.*/.hglf/b.dat * (glob)
1021 --- cloned.*/.hglf/b.dat * (glob)
975 +++ cloned.*/.hglf/b.dat * (glob)
1022 +++ cloned.*/.hglf/b.dat * (glob)
976 @@ -*,0 +1* @@ (glob)
1023 @@ -*,0 +1* @@ (glob)
977 +da39a3ee5e6b4b0d3255bfef95601890afd80709
1024 +da39a3ee5e6b4b0d3255bfef95601890afd80709
978 diff -Nru cloned.*/.hglf/foo/bar/large.dat cloned.*/.hglf/foo/bar/large.dat (glob)
1025 diff -Nru cloned.*/.hglf/foo/bar/large.dat cloned.*/.hglf/foo/bar/large.dat (glob)
979 --- cloned.*/.hglf/foo/bar/large.dat * (glob)
1026 --- cloned.*/.hglf/foo/bar/large.dat * (glob)
980 +++ cloned.*/.hglf/foo/bar/large.dat * (glob)
1027 +++ cloned.*/.hglf/foo/bar/large.dat * (glob)
981 @@ -*,0 +1* @@ (glob)
1028 @@ -*,0 +1* @@ (glob)
982 +2f6933b5ee0f5fdd823d9717d8729f3c2523811b
1029 +2f6933b5ee0f5fdd823d9717d8729f3c2523811b
983 diff -Nru cloned.*/.hglf/large.bin cloned.*/.hglf/large.bin (glob)
1030 diff -Nru cloned.*/.hglf/large.bin cloned.*/.hglf/large.bin (glob)
984 --- cloned.*/.hglf/large.bin * (glob)
1031 --- cloned.*/.hglf/large.bin * (glob)
985 +++ cloned.*/.hglf/large.bin * (glob)
1032 +++ cloned.*/.hglf/large.bin * (glob)
986 @@ -*,0 +1* @@ (glob)
1033 @@ -*,0 +1* @@ (glob)
987 +7f7097b041ccf68cc5561e9600da4655d21c6d18
1034 +7f7097b041ccf68cc5561e9600da4655d21c6d18
988 diff -Nru cloned.*/.hgsub cloned.*/.hgsub (glob)
1035 diff -Nru cloned.*/.hgsub cloned.*/.hgsub (glob)
989 --- cloned.*/.hgsub * (glob)
1036 --- cloned.*/.hgsub * (glob)
990 +++ cloned.*/.hgsub * (glob)
1037 +++ cloned.*/.hgsub * (glob)
991 @@ -1* +1,2 @@ (glob)
1038 @@ -1* +1,2 @@ (glob)
992 sub1 = ../sub1
1039 sub1 = ../sub1
993 +sub3 = sub3
1040 +sub3 = sub3
994 diff -Nru cloned.*/.hgsubstate cloned.*/.hgsubstate (glob)
1041 diff -Nru cloned.*/.hgsubstate cloned.*/.hgsubstate (glob)
995 --- cloned.*/.hgsubstate * (glob)
1042 --- cloned.*/.hgsubstate * (glob)
996 +++ cloned.*/.hgsubstate * (glob)
1043 +++ cloned.*/.hgsubstate * (glob)
997 @@ -1* +1,2 @@ (glob)
1044 @@ -1* +1,2 @@ (glob)
998 -fc3b4ce2696f7741438c79207583768f2ce6b0dd sub1
1045 -fc3b4ce2696f7741438c79207583768f2ce6b0dd sub1
999 +7a36fa02b66e61f27f3d4a822809f159479b8ab2 sub1
1046 +7a36fa02b66e61f27f3d4a822809f159479b8ab2 sub1
1000 +b1a26de6f2a045a9f079323693614ee322f1ff7e sub3
1047 +b1a26de6f2a045a9f079323693614ee322f1ff7e sub3
1001 diff -Nru cloned.*/foo/bar/def cloned.*/foo/bar/def (glob)
1048 diff -Nru cloned.*/foo/bar/def cloned.*/foo/bar/def (glob)
1002 --- cloned.*/foo/bar/def * (glob)
1049 --- cloned.*/foo/bar/def * (glob)
1003 +++ cloned.*/foo/bar/def * (glob)
1050 +++ cloned.*/foo/bar/def * (glob)
1004 @@ -*,0 +1* @@ (glob)
1051 @@ -*,0 +1* @@ (glob)
1005 +changed
1052 +changed
1006 diff -Nru cloned.*/main cloned.*/main (glob)
1053 diff -Nru cloned.*/main cloned.*/main (glob)
1007 --- cloned.*/main * (glob)
1054 --- cloned.*/main * (glob)
1008 +++ cloned.*/main * (glob)
1055 +++ cloned.*/main * (glob)
1009 @@ -1* +1* @@ (glob)
1056 @@ -1* +1* @@ (glob)
1010 -main
1057 -main
1011 +foo
1058 +foo
1012 diff -Nru cloned.*/sub1/.hgsubstate cloned.*/sub1/.hgsubstate (glob)
1059 diff -Nru cloned.*/sub1/.hgsubstate cloned.*/sub1/.hgsubstate (glob)
1013 --- cloned.*/sub1/.hgsubstate * (glob)
1060 --- cloned.*/sub1/.hgsubstate * (glob)
1014 +++ cloned.*/sub1/.hgsubstate * (glob)
1061 +++ cloned.*/sub1/.hgsubstate * (glob)
1015 @@ -1* +1* @@ (glob)
1062 @@ -1* +1* @@ (glob)
1016 -c57a0840e3badd667ef3c3ef65471609acb2ba3c sub2
1063 -c57a0840e3badd667ef3c3ef65471609acb2ba3c sub2
1017 +c77908c81ccea3794a896c79e98b0e004aee2e9e sub2
1064 +c77908c81ccea3794a896c79e98b0e004aee2e9e sub2
1018 diff -Nru cloned.*/sub1/sub2/folder/test.txt cloned.*/sub1/sub2/folder/test.txt (glob)
1065 diff -Nru cloned.*/sub1/sub2/folder/test.txt cloned.*/sub1/sub2/folder/test.txt (glob)
1019 --- cloned.*/sub1/sub2/folder/test.txt * (glob)
1066 --- cloned.*/sub1/sub2/folder/test.txt * (glob)
1020 +++ cloned.*/sub1/sub2/folder/test.txt * (glob)
1067 +++ cloned.*/sub1/sub2/folder/test.txt * (glob)
1021 @@ -*,0 +1* @@ (glob)
1068 @@ -*,0 +1* @@ (glob)
1022 +subfolder
1069 +subfolder
1023 diff -Nru cloned.*/sub1/sub2/sub2 cloned.*/sub1/sub2/sub2 (glob)
1070 diff -Nru cloned.*/sub1/sub2/sub2 cloned.*/sub1/sub2/sub2 (glob)
1024 --- cloned.*/sub1/sub2/sub2 * (glob)
1071 --- cloned.*/sub1/sub2/sub2 * (glob)
1025 +++ cloned.*/sub1/sub2/sub2 * (glob)
1072 +++ cloned.*/sub1/sub2/sub2 * (glob)
1026 @@ -1* +1* @@ (glob)
1073 @@ -1* +1* @@ (glob)
1027 -sub2
1074 -sub2
1028 +modified
1075 +modified
1029 diff -Nru cloned.*/sub3/a.txt cloned.*/sub3/a.txt (glob)
1076 diff -Nru cloned.*/sub3/a.txt cloned.*/sub3/a.txt (glob)
1030 --- cloned.*/sub3/a.txt * (glob)
1077 --- cloned.*/sub3/a.txt * (glob)
1031 +++ cloned.*/sub3/a.txt * (glob)
1078 +++ cloned.*/sub3/a.txt * (glob)
1032 @@ -*,0 +1* @@ (glob)
1079 @@ -*,0 +1* @@ (glob)
1033 +xyz
1080 +xyz
1034 [1]
1081 [1]
1035
1082
1036 $ echo mod > sub1/sub2/sub2
1083 $ echo mod > sub1/sub2/sub2
1037 $ hg --config extensions.extdiff= pdiff -S
1084 $ hg --config extensions.extdiff= pdiff -S
1038 \r (no-eol) (esc)
1085 \r (no-eol) (esc)
1039 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
1086 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
1040 \r (no-eol) (esc)
1087 \r (no-eol) (esc)
1041 \r (no-eol) (esc)
1088 \r (no-eol) (esc)
1042 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc)
1089 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc)
1043 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc)
1090 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc)
1044 \r (no-eol) (esc)
1091 \r (no-eol) (esc)
1045 --- */cloned.*/sub1/sub2/sub2 * (glob)
1092 --- */cloned.*/sub1/sub2/sub2 * (glob)
1046 +++ */cloned/sub1/sub2/sub2 * (glob)
1093 +++ */cloned/sub1/sub2/sub2 * (glob)
1047 @@ -1* +1* @@ (glob)
1094 @@ -1* +1* @@ (glob)
1048 -modified
1095 -modified
1049 +mod
1096 +mod
1050 [1]
1097 [1]
1051
1098
1052 $ cd ..
1099 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now