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