##// END OF EJS Templates
amend: add noise in extra to avoid creating obsolescence cycle (issue3664)...
Pierre-Yves David -
r17811:a8aba292 default
parent child Browse files
Show More
@@ -1,1981 +1,1998 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, errno, re, tempfile
10 import os, sys, errno, re, tempfile
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
12 import match as matchmod
12 import match as matchmod
13 import subrepo, context, repair, bookmarks, graphmod, revset, phases, obsolete
13 import subrepo, context, repair, bookmarks, graphmod, revset, phases, obsolete
14 import changelog
14 import lock as lockmod
15 import lock as lockmod
15
16
16 def parsealiases(cmd):
17 def parsealiases(cmd):
17 return cmd.lstrip("^").split("|")
18 return cmd.lstrip("^").split("|")
18
19
19 def findpossible(cmd, table, strict=False):
20 def findpossible(cmd, table, strict=False):
20 """
21 """
21 Return cmd -> (aliases, command table entry)
22 Return cmd -> (aliases, command table entry)
22 for each matching command.
23 for each matching command.
23 Return debug commands (or their aliases) only if no normal command matches.
24 Return debug commands (or their aliases) only if no normal command matches.
24 """
25 """
25 choice = {}
26 choice = {}
26 debugchoice = {}
27 debugchoice = {}
27
28
28 if cmd in table:
29 if cmd in table:
29 # short-circuit exact matches, "log" alias beats "^log|history"
30 # short-circuit exact matches, "log" alias beats "^log|history"
30 keys = [cmd]
31 keys = [cmd]
31 else:
32 else:
32 keys = table.keys()
33 keys = table.keys()
33
34
34 for e in keys:
35 for e in keys:
35 aliases = parsealiases(e)
36 aliases = parsealiases(e)
36 found = None
37 found = None
37 if cmd in aliases:
38 if cmd in aliases:
38 found = cmd
39 found = cmd
39 elif not strict:
40 elif not strict:
40 for a in aliases:
41 for a in aliases:
41 if a.startswith(cmd):
42 if a.startswith(cmd):
42 found = a
43 found = a
43 break
44 break
44 if found is not None:
45 if found is not None:
45 if aliases[0].startswith("debug") or found.startswith("debug"):
46 if aliases[0].startswith("debug") or found.startswith("debug"):
46 debugchoice[found] = (aliases, table[e])
47 debugchoice[found] = (aliases, table[e])
47 else:
48 else:
48 choice[found] = (aliases, table[e])
49 choice[found] = (aliases, table[e])
49
50
50 if not choice and debugchoice:
51 if not choice and debugchoice:
51 choice = debugchoice
52 choice = debugchoice
52
53
53 return choice
54 return choice
54
55
55 def findcmd(cmd, table, strict=True):
56 def findcmd(cmd, table, strict=True):
56 """Return (aliases, command table entry) for command string."""
57 """Return (aliases, command table entry) for command string."""
57 choice = findpossible(cmd, table, strict)
58 choice = findpossible(cmd, table, strict)
58
59
59 if cmd in choice:
60 if cmd in choice:
60 return choice[cmd]
61 return choice[cmd]
61
62
62 if len(choice) > 1:
63 if len(choice) > 1:
63 clist = choice.keys()
64 clist = choice.keys()
64 clist.sort()
65 clist.sort()
65 raise error.AmbiguousCommand(cmd, clist)
66 raise error.AmbiguousCommand(cmd, clist)
66
67
67 if choice:
68 if choice:
68 return choice.values()[0]
69 return choice.values()[0]
69
70
70 raise error.UnknownCommand(cmd)
71 raise error.UnknownCommand(cmd)
71
72
72 def findrepo(p):
73 def findrepo(p):
73 while not os.path.isdir(os.path.join(p, ".hg")):
74 while not os.path.isdir(os.path.join(p, ".hg")):
74 oldp, p = p, os.path.dirname(p)
75 oldp, p = p, os.path.dirname(p)
75 if p == oldp:
76 if p == oldp:
76 return None
77 return None
77
78
78 return p
79 return p
79
80
80 def bailifchanged(repo):
81 def bailifchanged(repo):
81 if repo.dirstate.p2() != nullid:
82 if repo.dirstate.p2() != nullid:
82 raise util.Abort(_('outstanding uncommitted merge'))
83 raise util.Abort(_('outstanding uncommitted merge'))
83 modified, added, removed, deleted = repo.status()[:4]
84 modified, added, removed, deleted = repo.status()[:4]
84 if modified or added or removed or deleted:
85 if modified or added or removed or deleted:
85 raise util.Abort(_("outstanding uncommitted changes"))
86 raise util.Abort(_("outstanding uncommitted changes"))
86 ctx = repo[None]
87 ctx = repo[None]
87 for s in ctx.substate:
88 for s in ctx.substate:
88 if ctx.sub(s).dirty():
89 if ctx.sub(s).dirty():
89 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
90 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
90
91
91 def logmessage(ui, opts):
92 def logmessage(ui, opts):
92 """ get the log message according to -m and -l option """
93 """ get the log message according to -m and -l option """
93 message = opts.get('message')
94 message = opts.get('message')
94 logfile = opts.get('logfile')
95 logfile = opts.get('logfile')
95
96
96 if message and logfile:
97 if message and logfile:
97 raise util.Abort(_('options --message and --logfile are mutually '
98 raise util.Abort(_('options --message and --logfile are mutually '
98 'exclusive'))
99 'exclusive'))
99 if not message and logfile:
100 if not message and logfile:
100 try:
101 try:
101 if logfile == '-':
102 if logfile == '-':
102 message = ui.fin.read()
103 message = ui.fin.read()
103 else:
104 else:
104 message = '\n'.join(util.readfile(logfile).splitlines())
105 message = '\n'.join(util.readfile(logfile).splitlines())
105 except IOError, inst:
106 except IOError, inst:
106 raise util.Abort(_("can't read commit message '%s': %s") %
107 raise util.Abort(_("can't read commit message '%s': %s") %
107 (logfile, inst.strerror))
108 (logfile, inst.strerror))
108 return message
109 return message
109
110
110 def loglimit(opts):
111 def loglimit(opts):
111 """get the log limit according to option -l/--limit"""
112 """get the log limit according to option -l/--limit"""
112 limit = opts.get('limit')
113 limit = opts.get('limit')
113 if limit:
114 if limit:
114 try:
115 try:
115 limit = int(limit)
116 limit = int(limit)
116 except ValueError:
117 except ValueError:
117 raise util.Abort(_('limit must be a positive integer'))
118 raise util.Abort(_('limit must be a positive integer'))
118 if limit <= 0:
119 if limit <= 0:
119 raise util.Abort(_('limit must be positive'))
120 raise util.Abort(_('limit must be positive'))
120 else:
121 else:
121 limit = None
122 limit = None
122 return limit
123 return limit
123
124
124 def makefilename(repo, pat, node, desc=None,
125 def makefilename(repo, pat, node, desc=None,
125 total=None, seqno=None, revwidth=None, pathname=None):
126 total=None, seqno=None, revwidth=None, pathname=None):
126 node_expander = {
127 node_expander = {
127 'H': lambda: hex(node),
128 'H': lambda: hex(node),
128 'R': lambda: str(repo.changelog.rev(node)),
129 'R': lambda: str(repo.changelog.rev(node)),
129 'h': lambda: short(node),
130 'h': lambda: short(node),
130 'm': lambda: re.sub('[^\w]', '_', str(desc))
131 'm': lambda: re.sub('[^\w]', '_', str(desc))
131 }
132 }
132 expander = {
133 expander = {
133 '%': lambda: '%',
134 '%': lambda: '%',
134 'b': lambda: os.path.basename(repo.root),
135 'b': lambda: os.path.basename(repo.root),
135 }
136 }
136
137
137 try:
138 try:
138 if node:
139 if node:
139 expander.update(node_expander)
140 expander.update(node_expander)
140 if node:
141 if node:
141 expander['r'] = (lambda:
142 expander['r'] = (lambda:
142 str(repo.changelog.rev(node)).zfill(revwidth or 0))
143 str(repo.changelog.rev(node)).zfill(revwidth or 0))
143 if total is not None:
144 if total is not None:
144 expander['N'] = lambda: str(total)
145 expander['N'] = lambda: str(total)
145 if seqno is not None:
146 if seqno is not None:
146 expander['n'] = lambda: str(seqno)
147 expander['n'] = lambda: str(seqno)
147 if total is not None and seqno is not None:
148 if total is not None and seqno is not None:
148 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
149 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
149 if pathname is not None:
150 if pathname is not None:
150 expander['s'] = lambda: os.path.basename(pathname)
151 expander['s'] = lambda: os.path.basename(pathname)
151 expander['d'] = lambda: os.path.dirname(pathname) or '.'
152 expander['d'] = lambda: os.path.dirname(pathname) or '.'
152 expander['p'] = lambda: pathname
153 expander['p'] = lambda: pathname
153
154
154 newname = []
155 newname = []
155 patlen = len(pat)
156 patlen = len(pat)
156 i = 0
157 i = 0
157 while i < patlen:
158 while i < patlen:
158 c = pat[i]
159 c = pat[i]
159 if c == '%':
160 if c == '%':
160 i += 1
161 i += 1
161 c = pat[i]
162 c = pat[i]
162 c = expander[c]()
163 c = expander[c]()
163 newname.append(c)
164 newname.append(c)
164 i += 1
165 i += 1
165 return ''.join(newname)
166 return ''.join(newname)
166 except KeyError, inst:
167 except KeyError, inst:
167 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
168 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
168 inst.args[0])
169 inst.args[0])
169
170
170 def makefileobj(repo, pat, node=None, desc=None, total=None,
171 def makefileobj(repo, pat, node=None, desc=None, total=None,
171 seqno=None, revwidth=None, mode='wb', pathname=None):
172 seqno=None, revwidth=None, mode='wb', pathname=None):
172
173
173 writable = mode not in ('r', 'rb')
174 writable = mode not in ('r', 'rb')
174
175
175 if not pat or pat == '-':
176 if not pat or pat == '-':
176 fp = writable and repo.ui.fout or repo.ui.fin
177 fp = writable and repo.ui.fout or repo.ui.fin
177 if util.safehasattr(fp, 'fileno'):
178 if util.safehasattr(fp, 'fileno'):
178 return os.fdopen(os.dup(fp.fileno()), mode)
179 return os.fdopen(os.dup(fp.fileno()), mode)
179 else:
180 else:
180 # if this fp can't be duped properly, return
181 # if this fp can't be duped properly, return
181 # a dummy object that can be closed
182 # a dummy object that can be closed
182 class wrappedfileobj(object):
183 class wrappedfileobj(object):
183 noop = lambda x: None
184 noop = lambda x: None
184 def __init__(self, f):
185 def __init__(self, f):
185 self.f = f
186 self.f = f
186 def __getattr__(self, attr):
187 def __getattr__(self, attr):
187 if attr == 'close':
188 if attr == 'close':
188 return self.noop
189 return self.noop
189 else:
190 else:
190 return getattr(self.f, attr)
191 return getattr(self.f, attr)
191
192
192 return wrappedfileobj(fp)
193 return wrappedfileobj(fp)
193 if util.safehasattr(pat, 'write') and writable:
194 if util.safehasattr(pat, 'write') and writable:
194 return pat
195 return pat
195 if util.safehasattr(pat, 'read') and 'r' in mode:
196 if util.safehasattr(pat, 'read') and 'r' in mode:
196 return pat
197 return pat
197 return open(makefilename(repo, pat, node, desc, total, seqno, revwidth,
198 return open(makefilename(repo, pat, node, desc, total, seqno, revwidth,
198 pathname),
199 pathname),
199 mode)
200 mode)
200
201
201 def openrevlog(repo, cmd, file_, opts):
202 def openrevlog(repo, cmd, file_, opts):
202 """opens the changelog, manifest, a filelog or a given revlog"""
203 """opens the changelog, manifest, a filelog or a given revlog"""
203 cl = opts['changelog']
204 cl = opts['changelog']
204 mf = opts['manifest']
205 mf = opts['manifest']
205 msg = None
206 msg = None
206 if cl and mf:
207 if cl and mf:
207 msg = _('cannot specify --changelog and --manifest at the same time')
208 msg = _('cannot specify --changelog and --manifest at the same time')
208 elif cl or mf:
209 elif cl or mf:
209 if file_:
210 if file_:
210 msg = _('cannot specify filename with --changelog or --manifest')
211 msg = _('cannot specify filename with --changelog or --manifest')
211 elif not repo:
212 elif not repo:
212 msg = _('cannot specify --changelog or --manifest '
213 msg = _('cannot specify --changelog or --manifest '
213 'without a repository')
214 'without a repository')
214 if msg:
215 if msg:
215 raise util.Abort(msg)
216 raise util.Abort(msg)
216
217
217 r = None
218 r = None
218 if repo:
219 if repo:
219 if cl:
220 if cl:
220 r = repo.changelog
221 r = repo.changelog
221 elif mf:
222 elif mf:
222 r = repo.manifest
223 r = repo.manifest
223 elif file_:
224 elif file_:
224 filelog = repo.file(file_)
225 filelog = repo.file(file_)
225 if len(filelog):
226 if len(filelog):
226 r = filelog
227 r = filelog
227 if not r:
228 if not r:
228 if not file_:
229 if not file_:
229 raise error.CommandError(cmd, _('invalid arguments'))
230 raise error.CommandError(cmd, _('invalid arguments'))
230 if not os.path.isfile(file_):
231 if not os.path.isfile(file_):
231 raise util.Abort(_("revlog '%s' not found") % file_)
232 raise util.Abort(_("revlog '%s' not found") % file_)
232 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
233 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
233 file_[:-2] + ".i")
234 file_[:-2] + ".i")
234 return r
235 return r
235
236
236 def copy(ui, repo, pats, opts, rename=False):
237 def copy(ui, repo, pats, opts, rename=False):
237 # called with the repo lock held
238 # called with the repo lock held
238 #
239 #
239 # hgsep => pathname that uses "/" to separate directories
240 # hgsep => pathname that uses "/" to separate directories
240 # ossep => pathname that uses os.sep to separate directories
241 # ossep => pathname that uses os.sep to separate directories
241 cwd = repo.getcwd()
242 cwd = repo.getcwd()
242 targets = {}
243 targets = {}
243 after = opts.get("after")
244 after = opts.get("after")
244 dryrun = opts.get("dry_run")
245 dryrun = opts.get("dry_run")
245 wctx = repo[None]
246 wctx = repo[None]
246
247
247 def walkpat(pat):
248 def walkpat(pat):
248 srcs = []
249 srcs = []
249 badstates = after and '?' or '?r'
250 badstates = after and '?' or '?r'
250 m = scmutil.match(repo[None], [pat], opts, globbed=True)
251 m = scmutil.match(repo[None], [pat], opts, globbed=True)
251 for abs in repo.walk(m):
252 for abs in repo.walk(m):
252 state = repo.dirstate[abs]
253 state = repo.dirstate[abs]
253 rel = m.rel(abs)
254 rel = m.rel(abs)
254 exact = m.exact(abs)
255 exact = m.exact(abs)
255 if state in badstates:
256 if state in badstates:
256 if exact and state == '?':
257 if exact and state == '?':
257 ui.warn(_('%s: not copying - file is not managed\n') % rel)
258 ui.warn(_('%s: not copying - file is not managed\n') % rel)
258 if exact and state == 'r':
259 if exact and state == 'r':
259 ui.warn(_('%s: not copying - file has been marked for'
260 ui.warn(_('%s: not copying - file has been marked for'
260 ' remove\n') % rel)
261 ' remove\n') % rel)
261 continue
262 continue
262 # abs: hgsep
263 # abs: hgsep
263 # rel: ossep
264 # rel: ossep
264 srcs.append((abs, rel, exact))
265 srcs.append((abs, rel, exact))
265 return srcs
266 return srcs
266
267
267 # abssrc: hgsep
268 # abssrc: hgsep
268 # relsrc: ossep
269 # relsrc: ossep
269 # otarget: ossep
270 # otarget: ossep
270 def copyfile(abssrc, relsrc, otarget, exact):
271 def copyfile(abssrc, relsrc, otarget, exact):
271 abstarget = scmutil.canonpath(repo.root, cwd, otarget)
272 abstarget = scmutil.canonpath(repo.root, cwd, otarget)
272 if '/' in abstarget:
273 if '/' in abstarget:
273 # We cannot normalize abstarget itself, this would prevent
274 # We cannot normalize abstarget itself, this would prevent
274 # case only renames, like a => A.
275 # case only renames, like a => A.
275 abspath, absname = abstarget.rsplit('/', 1)
276 abspath, absname = abstarget.rsplit('/', 1)
276 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
277 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
277 reltarget = repo.pathto(abstarget, cwd)
278 reltarget = repo.pathto(abstarget, cwd)
278 target = repo.wjoin(abstarget)
279 target = repo.wjoin(abstarget)
279 src = repo.wjoin(abssrc)
280 src = repo.wjoin(abssrc)
280 state = repo.dirstate[abstarget]
281 state = repo.dirstate[abstarget]
281
282
282 scmutil.checkportable(ui, abstarget)
283 scmutil.checkportable(ui, abstarget)
283
284
284 # check for collisions
285 # check for collisions
285 prevsrc = targets.get(abstarget)
286 prevsrc = targets.get(abstarget)
286 if prevsrc is not None:
287 if prevsrc is not None:
287 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
288 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
288 (reltarget, repo.pathto(abssrc, cwd),
289 (reltarget, repo.pathto(abssrc, cwd),
289 repo.pathto(prevsrc, cwd)))
290 repo.pathto(prevsrc, cwd)))
290 return
291 return
291
292
292 # check for overwrites
293 # check for overwrites
293 exists = os.path.lexists(target)
294 exists = os.path.lexists(target)
294 samefile = False
295 samefile = False
295 if exists and abssrc != abstarget:
296 if exists and abssrc != abstarget:
296 if (repo.dirstate.normalize(abssrc) ==
297 if (repo.dirstate.normalize(abssrc) ==
297 repo.dirstate.normalize(abstarget)):
298 repo.dirstate.normalize(abstarget)):
298 if not rename:
299 if not rename:
299 ui.warn(_("%s: can't copy - same file\n") % reltarget)
300 ui.warn(_("%s: can't copy - same file\n") % reltarget)
300 return
301 return
301 exists = False
302 exists = False
302 samefile = True
303 samefile = True
303
304
304 if not after and exists or after and state in 'mn':
305 if not after and exists or after and state in 'mn':
305 if not opts['force']:
306 if not opts['force']:
306 ui.warn(_('%s: not overwriting - file exists\n') %
307 ui.warn(_('%s: not overwriting - file exists\n') %
307 reltarget)
308 reltarget)
308 return
309 return
309
310
310 if after:
311 if after:
311 if not exists:
312 if not exists:
312 if rename:
313 if rename:
313 ui.warn(_('%s: not recording move - %s does not exist\n') %
314 ui.warn(_('%s: not recording move - %s does not exist\n') %
314 (relsrc, reltarget))
315 (relsrc, reltarget))
315 else:
316 else:
316 ui.warn(_('%s: not recording copy - %s does not exist\n') %
317 ui.warn(_('%s: not recording copy - %s does not exist\n') %
317 (relsrc, reltarget))
318 (relsrc, reltarget))
318 return
319 return
319 elif not dryrun:
320 elif not dryrun:
320 try:
321 try:
321 if exists:
322 if exists:
322 os.unlink(target)
323 os.unlink(target)
323 targetdir = os.path.dirname(target) or '.'
324 targetdir = os.path.dirname(target) or '.'
324 if not os.path.isdir(targetdir):
325 if not os.path.isdir(targetdir):
325 os.makedirs(targetdir)
326 os.makedirs(targetdir)
326 if samefile:
327 if samefile:
327 tmp = target + "~hgrename"
328 tmp = target + "~hgrename"
328 os.rename(src, tmp)
329 os.rename(src, tmp)
329 os.rename(tmp, target)
330 os.rename(tmp, target)
330 else:
331 else:
331 util.copyfile(src, target)
332 util.copyfile(src, target)
332 srcexists = True
333 srcexists = True
333 except IOError, inst:
334 except IOError, inst:
334 if inst.errno == errno.ENOENT:
335 if inst.errno == errno.ENOENT:
335 ui.warn(_('%s: deleted in working copy\n') % relsrc)
336 ui.warn(_('%s: deleted in working copy\n') % relsrc)
336 srcexists = False
337 srcexists = False
337 else:
338 else:
338 ui.warn(_('%s: cannot copy - %s\n') %
339 ui.warn(_('%s: cannot copy - %s\n') %
339 (relsrc, inst.strerror))
340 (relsrc, inst.strerror))
340 return True # report a failure
341 return True # report a failure
341
342
342 if ui.verbose or not exact:
343 if ui.verbose or not exact:
343 if rename:
344 if rename:
344 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
345 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
345 else:
346 else:
346 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
347 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
347
348
348 targets[abstarget] = abssrc
349 targets[abstarget] = abssrc
349
350
350 # fix up dirstate
351 # fix up dirstate
351 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
352 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
352 dryrun=dryrun, cwd=cwd)
353 dryrun=dryrun, cwd=cwd)
353 if rename and not dryrun:
354 if rename and not dryrun:
354 if not after and srcexists and not samefile:
355 if not after and srcexists and not samefile:
355 util.unlinkpath(repo.wjoin(abssrc))
356 util.unlinkpath(repo.wjoin(abssrc))
356 wctx.forget([abssrc])
357 wctx.forget([abssrc])
357
358
358 # pat: ossep
359 # pat: ossep
359 # dest ossep
360 # dest ossep
360 # srcs: list of (hgsep, hgsep, ossep, bool)
361 # srcs: list of (hgsep, hgsep, ossep, bool)
361 # return: function that takes hgsep and returns ossep
362 # return: function that takes hgsep and returns ossep
362 def targetpathfn(pat, dest, srcs):
363 def targetpathfn(pat, dest, srcs):
363 if os.path.isdir(pat):
364 if os.path.isdir(pat):
364 abspfx = scmutil.canonpath(repo.root, cwd, pat)
365 abspfx = scmutil.canonpath(repo.root, cwd, pat)
365 abspfx = util.localpath(abspfx)
366 abspfx = util.localpath(abspfx)
366 if destdirexists:
367 if destdirexists:
367 striplen = len(os.path.split(abspfx)[0])
368 striplen = len(os.path.split(abspfx)[0])
368 else:
369 else:
369 striplen = len(abspfx)
370 striplen = len(abspfx)
370 if striplen:
371 if striplen:
371 striplen += len(os.sep)
372 striplen += len(os.sep)
372 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
373 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
373 elif destdirexists:
374 elif destdirexists:
374 res = lambda p: os.path.join(dest,
375 res = lambda p: os.path.join(dest,
375 os.path.basename(util.localpath(p)))
376 os.path.basename(util.localpath(p)))
376 else:
377 else:
377 res = lambda p: dest
378 res = lambda p: dest
378 return res
379 return res
379
380
380 # pat: ossep
381 # pat: ossep
381 # dest ossep
382 # dest ossep
382 # srcs: list of (hgsep, hgsep, ossep, bool)
383 # srcs: list of (hgsep, hgsep, ossep, bool)
383 # return: function that takes hgsep and returns ossep
384 # return: function that takes hgsep and returns ossep
384 def targetpathafterfn(pat, dest, srcs):
385 def targetpathafterfn(pat, dest, srcs):
385 if matchmod.patkind(pat):
386 if matchmod.patkind(pat):
386 # a mercurial pattern
387 # a mercurial pattern
387 res = lambda p: os.path.join(dest,
388 res = lambda p: os.path.join(dest,
388 os.path.basename(util.localpath(p)))
389 os.path.basename(util.localpath(p)))
389 else:
390 else:
390 abspfx = scmutil.canonpath(repo.root, cwd, pat)
391 abspfx = scmutil.canonpath(repo.root, cwd, pat)
391 if len(abspfx) < len(srcs[0][0]):
392 if len(abspfx) < len(srcs[0][0]):
392 # A directory. Either the target path contains the last
393 # A directory. Either the target path contains the last
393 # component of the source path or it does not.
394 # component of the source path or it does not.
394 def evalpath(striplen):
395 def evalpath(striplen):
395 score = 0
396 score = 0
396 for s in srcs:
397 for s in srcs:
397 t = os.path.join(dest, util.localpath(s[0])[striplen:])
398 t = os.path.join(dest, util.localpath(s[0])[striplen:])
398 if os.path.lexists(t):
399 if os.path.lexists(t):
399 score += 1
400 score += 1
400 return score
401 return score
401
402
402 abspfx = util.localpath(abspfx)
403 abspfx = util.localpath(abspfx)
403 striplen = len(abspfx)
404 striplen = len(abspfx)
404 if striplen:
405 if striplen:
405 striplen += len(os.sep)
406 striplen += len(os.sep)
406 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
407 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
407 score = evalpath(striplen)
408 score = evalpath(striplen)
408 striplen1 = len(os.path.split(abspfx)[0])
409 striplen1 = len(os.path.split(abspfx)[0])
409 if striplen1:
410 if striplen1:
410 striplen1 += len(os.sep)
411 striplen1 += len(os.sep)
411 if evalpath(striplen1) > score:
412 if evalpath(striplen1) > score:
412 striplen = striplen1
413 striplen = striplen1
413 res = lambda p: os.path.join(dest,
414 res = lambda p: os.path.join(dest,
414 util.localpath(p)[striplen:])
415 util.localpath(p)[striplen:])
415 else:
416 else:
416 # a file
417 # a file
417 if destdirexists:
418 if destdirexists:
418 res = lambda p: os.path.join(dest,
419 res = lambda p: os.path.join(dest,
419 os.path.basename(util.localpath(p)))
420 os.path.basename(util.localpath(p)))
420 else:
421 else:
421 res = lambda p: dest
422 res = lambda p: dest
422 return res
423 return res
423
424
424
425
425 pats = scmutil.expandpats(pats)
426 pats = scmutil.expandpats(pats)
426 if not pats:
427 if not pats:
427 raise util.Abort(_('no source or destination specified'))
428 raise util.Abort(_('no source or destination specified'))
428 if len(pats) == 1:
429 if len(pats) == 1:
429 raise util.Abort(_('no destination specified'))
430 raise util.Abort(_('no destination specified'))
430 dest = pats.pop()
431 dest = pats.pop()
431 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
432 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
432 if not destdirexists:
433 if not destdirexists:
433 if len(pats) > 1 or matchmod.patkind(pats[0]):
434 if len(pats) > 1 or matchmod.patkind(pats[0]):
434 raise util.Abort(_('with multiple sources, destination must be an '
435 raise util.Abort(_('with multiple sources, destination must be an '
435 'existing directory'))
436 'existing directory'))
436 if util.endswithsep(dest):
437 if util.endswithsep(dest):
437 raise util.Abort(_('destination %s is not a directory') % dest)
438 raise util.Abort(_('destination %s is not a directory') % dest)
438
439
439 tfn = targetpathfn
440 tfn = targetpathfn
440 if after:
441 if after:
441 tfn = targetpathafterfn
442 tfn = targetpathafterfn
442 copylist = []
443 copylist = []
443 for pat in pats:
444 for pat in pats:
444 srcs = walkpat(pat)
445 srcs = walkpat(pat)
445 if not srcs:
446 if not srcs:
446 continue
447 continue
447 copylist.append((tfn(pat, dest, srcs), srcs))
448 copylist.append((tfn(pat, dest, srcs), srcs))
448 if not copylist:
449 if not copylist:
449 raise util.Abort(_('no files to copy'))
450 raise util.Abort(_('no files to copy'))
450
451
451 errors = 0
452 errors = 0
452 for targetpath, srcs in copylist:
453 for targetpath, srcs in copylist:
453 for abssrc, relsrc, exact in srcs:
454 for abssrc, relsrc, exact in srcs:
454 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
455 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
455 errors += 1
456 errors += 1
456
457
457 if errors:
458 if errors:
458 ui.warn(_('(consider using --after)\n'))
459 ui.warn(_('(consider using --after)\n'))
459
460
460 return errors != 0
461 return errors != 0
461
462
462 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
463 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
463 runargs=None, appendpid=False):
464 runargs=None, appendpid=False):
464 '''Run a command as a service.'''
465 '''Run a command as a service.'''
465
466
466 if opts['daemon'] and not opts['daemon_pipefds']:
467 if opts['daemon'] and not opts['daemon_pipefds']:
467 # Signal child process startup with file removal
468 # Signal child process startup with file removal
468 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
469 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
469 os.close(lockfd)
470 os.close(lockfd)
470 try:
471 try:
471 if not runargs:
472 if not runargs:
472 runargs = util.hgcmd() + sys.argv[1:]
473 runargs = util.hgcmd() + sys.argv[1:]
473 runargs.append('--daemon-pipefds=%s' % lockpath)
474 runargs.append('--daemon-pipefds=%s' % lockpath)
474 # Don't pass --cwd to the child process, because we've already
475 # Don't pass --cwd to the child process, because we've already
475 # changed directory.
476 # changed directory.
476 for i in xrange(1, len(runargs)):
477 for i in xrange(1, len(runargs)):
477 if runargs[i].startswith('--cwd='):
478 if runargs[i].startswith('--cwd='):
478 del runargs[i]
479 del runargs[i]
479 break
480 break
480 elif runargs[i].startswith('--cwd'):
481 elif runargs[i].startswith('--cwd'):
481 del runargs[i:i + 2]
482 del runargs[i:i + 2]
482 break
483 break
483 def condfn():
484 def condfn():
484 return not os.path.exists(lockpath)
485 return not os.path.exists(lockpath)
485 pid = util.rundetached(runargs, condfn)
486 pid = util.rundetached(runargs, condfn)
486 if pid < 0:
487 if pid < 0:
487 raise util.Abort(_('child process failed to start'))
488 raise util.Abort(_('child process failed to start'))
488 finally:
489 finally:
489 try:
490 try:
490 os.unlink(lockpath)
491 os.unlink(lockpath)
491 except OSError, e:
492 except OSError, e:
492 if e.errno != errno.ENOENT:
493 if e.errno != errno.ENOENT:
493 raise
494 raise
494 if parentfn:
495 if parentfn:
495 return parentfn(pid)
496 return parentfn(pid)
496 else:
497 else:
497 return
498 return
498
499
499 if initfn:
500 if initfn:
500 initfn()
501 initfn()
501
502
502 if opts['pid_file']:
503 if opts['pid_file']:
503 mode = appendpid and 'a' or 'w'
504 mode = appendpid and 'a' or 'w'
504 fp = open(opts['pid_file'], mode)
505 fp = open(opts['pid_file'], mode)
505 fp.write(str(os.getpid()) + '\n')
506 fp.write(str(os.getpid()) + '\n')
506 fp.close()
507 fp.close()
507
508
508 if opts['daemon_pipefds']:
509 if opts['daemon_pipefds']:
509 lockpath = opts['daemon_pipefds']
510 lockpath = opts['daemon_pipefds']
510 try:
511 try:
511 os.setsid()
512 os.setsid()
512 except AttributeError:
513 except AttributeError:
513 pass
514 pass
514 os.unlink(lockpath)
515 os.unlink(lockpath)
515 util.hidewindow()
516 util.hidewindow()
516 sys.stdout.flush()
517 sys.stdout.flush()
517 sys.stderr.flush()
518 sys.stderr.flush()
518
519
519 nullfd = os.open(os.devnull, os.O_RDWR)
520 nullfd = os.open(os.devnull, os.O_RDWR)
520 logfilefd = nullfd
521 logfilefd = nullfd
521 if logfile:
522 if logfile:
522 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
523 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
523 os.dup2(nullfd, 0)
524 os.dup2(nullfd, 0)
524 os.dup2(logfilefd, 1)
525 os.dup2(logfilefd, 1)
525 os.dup2(logfilefd, 2)
526 os.dup2(logfilefd, 2)
526 if nullfd not in (0, 1, 2):
527 if nullfd not in (0, 1, 2):
527 os.close(nullfd)
528 os.close(nullfd)
528 if logfile and logfilefd not in (0, 1, 2):
529 if logfile and logfilefd not in (0, 1, 2):
529 os.close(logfilefd)
530 os.close(logfilefd)
530
531
531 if runfn:
532 if runfn:
532 return runfn()
533 return runfn()
533
534
534 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
535 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
535 opts=None):
536 opts=None):
536 '''export changesets as hg patches.'''
537 '''export changesets as hg patches.'''
537
538
538 total = len(revs)
539 total = len(revs)
539 revwidth = max([len(str(rev)) for rev in revs])
540 revwidth = max([len(str(rev)) for rev in revs])
540
541
541 def single(rev, seqno, fp):
542 def single(rev, seqno, fp):
542 ctx = repo[rev]
543 ctx = repo[rev]
543 node = ctx.node()
544 node = ctx.node()
544 parents = [p.node() for p in ctx.parents() if p]
545 parents = [p.node() for p in ctx.parents() if p]
545 branch = ctx.branch()
546 branch = ctx.branch()
546 if switch_parent:
547 if switch_parent:
547 parents.reverse()
548 parents.reverse()
548 prev = (parents and parents[0]) or nullid
549 prev = (parents and parents[0]) or nullid
549
550
550 shouldclose = False
551 shouldclose = False
551 if not fp and len(template) > 0:
552 if not fp and len(template) > 0:
552 desc_lines = ctx.description().rstrip().split('\n')
553 desc_lines = ctx.description().rstrip().split('\n')
553 desc = desc_lines[0] #Commit always has a first line.
554 desc = desc_lines[0] #Commit always has a first line.
554 fp = makefileobj(repo, template, node, desc=desc, total=total,
555 fp = makefileobj(repo, template, node, desc=desc, total=total,
555 seqno=seqno, revwidth=revwidth, mode='ab')
556 seqno=seqno, revwidth=revwidth, mode='ab')
556 if fp != template:
557 if fp != template:
557 shouldclose = True
558 shouldclose = True
558 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
559 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
559 repo.ui.note("%s\n" % fp.name)
560 repo.ui.note("%s\n" % fp.name)
560
561
561 if not fp:
562 if not fp:
562 write = repo.ui.write
563 write = repo.ui.write
563 else:
564 else:
564 def write(s, **kw):
565 def write(s, **kw):
565 fp.write(s)
566 fp.write(s)
566
567
567
568
568 write("# HG changeset patch\n")
569 write("# HG changeset patch\n")
569 write("# User %s\n" % ctx.user())
570 write("# User %s\n" % ctx.user())
570 write("# Date %d %d\n" % ctx.date())
571 write("# Date %d %d\n" % ctx.date())
571 if branch and branch != 'default':
572 if branch and branch != 'default':
572 write("# Branch %s\n" % branch)
573 write("# Branch %s\n" % branch)
573 write("# Node ID %s\n" % hex(node))
574 write("# Node ID %s\n" % hex(node))
574 write("# Parent %s\n" % hex(prev))
575 write("# Parent %s\n" % hex(prev))
575 if len(parents) > 1:
576 if len(parents) > 1:
576 write("# Parent %s\n" % hex(parents[1]))
577 write("# Parent %s\n" % hex(parents[1]))
577 write(ctx.description().rstrip())
578 write(ctx.description().rstrip())
578 write("\n\n")
579 write("\n\n")
579
580
580 for chunk, label in patch.diffui(repo, prev, node, opts=opts):
581 for chunk, label in patch.diffui(repo, prev, node, opts=opts):
581 write(chunk, label=label)
582 write(chunk, label=label)
582
583
583 if shouldclose:
584 if shouldclose:
584 fp.close()
585 fp.close()
585
586
586 for seqno, rev in enumerate(revs):
587 for seqno, rev in enumerate(revs):
587 single(rev, seqno + 1, fp)
588 single(rev, seqno + 1, fp)
588
589
589 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
590 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
590 changes=None, stat=False, fp=None, prefix='',
591 changes=None, stat=False, fp=None, prefix='',
591 listsubrepos=False):
592 listsubrepos=False):
592 '''show diff or diffstat.'''
593 '''show diff or diffstat.'''
593 if fp is None:
594 if fp is None:
594 write = ui.write
595 write = ui.write
595 else:
596 else:
596 def write(s, **kw):
597 def write(s, **kw):
597 fp.write(s)
598 fp.write(s)
598
599
599 if stat:
600 if stat:
600 diffopts = diffopts.copy(context=0)
601 diffopts = diffopts.copy(context=0)
601 width = 80
602 width = 80
602 if not ui.plain():
603 if not ui.plain():
603 width = ui.termwidth()
604 width = ui.termwidth()
604 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
605 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
605 prefix=prefix)
606 prefix=prefix)
606 for chunk, label in patch.diffstatui(util.iterlines(chunks),
607 for chunk, label in patch.diffstatui(util.iterlines(chunks),
607 width=width,
608 width=width,
608 git=diffopts.git):
609 git=diffopts.git):
609 write(chunk, label=label)
610 write(chunk, label=label)
610 else:
611 else:
611 for chunk, label in patch.diffui(repo, node1, node2, match,
612 for chunk, label in patch.diffui(repo, node1, node2, match,
612 changes, diffopts, prefix=prefix):
613 changes, diffopts, prefix=prefix):
613 write(chunk, label=label)
614 write(chunk, label=label)
614
615
615 if listsubrepos:
616 if listsubrepos:
616 ctx1 = repo[node1]
617 ctx1 = repo[node1]
617 ctx2 = repo[node2]
618 ctx2 = repo[node2]
618 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
619 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
619 tempnode2 = node2
620 tempnode2 = node2
620 try:
621 try:
621 if node2 is not None:
622 if node2 is not None:
622 tempnode2 = ctx2.substate[subpath][1]
623 tempnode2 = ctx2.substate[subpath][1]
623 except KeyError:
624 except KeyError:
624 # A subrepo that existed in node1 was deleted between node1 and
625 # A subrepo that existed in node1 was deleted between node1 and
625 # node2 (inclusive). Thus, ctx2's substate won't contain that
626 # node2 (inclusive). Thus, ctx2's substate won't contain that
626 # subpath. The best we can do is to ignore it.
627 # subpath. The best we can do is to ignore it.
627 tempnode2 = None
628 tempnode2 = None
628 submatch = matchmod.narrowmatcher(subpath, match)
629 submatch = matchmod.narrowmatcher(subpath, match)
629 sub.diff(diffopts, tempnode2, submatch, changes=changes,
630 sub.diff(diffopts, tempnode2, submatch, changes=changes,
630 stat=stat, fp=fp, prefix=prefix)
631 stat=stat, fp=fp, prefix=prefix)
631
632
632 class changeset_printer(object):
633 class changeset_printer(object):
633 '''show changeset information when templating not requested.'''
634 '''show changeset information when templating not requested.'''
634
635
635 def __init__(self, ui, repo, patch, diffopts, buffered):
636 def __init__(self, ui, repo, patch, diffopts, buffered):
636 self.ui = ui
637 self.ui = ui
637 self.repo = repo
638 self.repo = repo
638 self.buffered = buffered
639 self.buffered = buffered
639 self.patch = patch
640 self.patch = patch
640 self.diffopts = diffopts
641 self.diffopts = diffopts
641 self.header = {}
642 self.header = {}
642 self.hunk = {}
643 self.hunk = {}
643 self.lastheader = None
644 self.lastheader = None
644 self.footer = None
645 self.footer = None
645
646
646 def flush(self, rev):
647 def flush(self, rev):
647 if rev in self.header:
648 if rev in self.header:
648 h = self.header[rev]
649 h = self.header[rev]
649 if h != self.lastheader:
650 if h != self.lastheader:
650 self.lastheader = h
651 self.lastheader = h
651 self.ui.write(h)
652 self.ui.write(h)
652 del self.header[rev]
653 del self.header[rev]
653 if rev in self.hunk:
654 if rev in self.hunk:
654 self.ui.write(self.hunk[rev])
655 self.ui.write(self.hunk[rev])
655 del self.hunk[rev]
656 del self.hunk[rev]
656 return 1
657 return 1
657 return 0
658 return 0
658
659
659 def close(self):
660 def close(self):
660 if self.footer:
661 if self.footer:
661 self.ui.write(self.footer)
662 self.ui.write(self.footer)
662
663
663 def show(self, ctx, copies=None, matchfn=None, **props):
664 def show(self, ctx, copies=None, matchfn=None, **props):
664 if self.buffered:
665 if self.buffered:
665 self.ui.pushbuffer()
666 self.ui.pushbuffer()
666 self._show(ctx, copies, matchfn, props)
667 self._show(ctx, copies, matchfn, props)
667 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
668 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
668 else:
669 else:
669 self._show(ctx, copies, matchfn, props)
670 self._show(ctx, copies, matchfn, props)
670
671
671 def _show(self, ctx, copies, matchfn, props):
672 def _show(self, ctx, copies, matchfn, props):
672 '''show a single changeset or file revision'''
673 '''show a single changeset or file revision'''
673 changenode = ctx.node()
674 changenode = ctx.node()
674 rev = ctx.rev()
675 rev = ctx.rev()
675
676
676 if self.ui.quiet:
677 if self.ui.quiet:
677 self.ui.write("%d:%s\n" % (rev, short(changenode)),
678 self.ui.write("%d:%s\n" % (rev, short(changenode)),
678 label='log.node')
679 label='log.node')
679 return
680 return
680
681
681 log = self.repo.changelog
682 log = self.repo.changelog
682 date = util.datestr(ctx.date())
683 date = util.datestr(ctx.date())
683
684
684 hexfunc = self.ui.debugflag and hex or short
685 hexfunc = self.ui.debugflag and hex or short
685
686
686 parents = [(p, hexfunc(log.node(p)))
687 parents = [(p, hexfunc(log.node(p)))
687 for p in self._meaningful_parentrevs(log, rev)]
688 for p in self._meaningful_parentrevs(log, rev)]
688
689
689 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
690 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
690 label='log.changeset changeset.%s' % ctx.phasestr())
691 label='log.changeset changeset.%s' % ctx.phasestr())
691
692
692 branch = ctx.branch()
693 branch = ctx.branch()
693 # don't show the default branch name
694 # don't show the default branch name
694 if branch != 'default':
695 if branch != 'default':
695 self.ui.write(_("branch: %s\n") % branch,
696 self.ui.write(_("branch: %s\n") % branch,
696 label='log.branch')
697 label='log.branch')
697 for bookmark in self.repo.nodebookmarks(changenode):
698 for bookmark in self.repo.nodebookmarks(changenode):
698 self.ui.write(_("bookmark: %s\n") % bookmark,
699 self.ui.write(_("bookmark: %s\n") % bookmark,
699 label='log.bookmark')
700 label='log.bookmark')
700 for tag in self.repo.nodetags(changenode):
701 for tag in self.repo.nodetags(changenode):
701 self.ui.write(_("tag: %s\n") % tag,
702 self.ui.write(_("tag: %s\n") % tag,
702 label='log.tag')
703 label='log.tag')
703 if self.ui.debugflag and ctx.phase():
704 if self.ui.debugflag and ctx.phase():
704 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
705 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
705 label='log.phase')
706 label='log.phase')
706 for parent in parents:
707 for parent in parents:
707 self.ui.write(_("parent: %d:%s\n") % parent,
708 self.ui.write(_("parent: %d:%s\n") % parent,
708 label='log.parent changeset.%s' % ctx.phasestr())
709 label='log.parent changeset.%s' % ctx.phasestr())
709
710
710 if self.ui.debugflag:
711 if self.ui.debugflag:
711 mnode = ctx.manifestnode()
712 mnode = ctx.manifestnode()
712 self.ui.write(_("manifest: %d:%s\n") %
713 self.ui.write(_("manifest: %d:%s\n") %
713 (self.repo.manifest.rev(mnode), hex(mnode)),
714 (self.repo.manifest.rev(mnode), hex(mnode)),
714 label='ui.debug log.manifest')
715 label='ui.debug log.manifest')
715 self.ui.write(_("user: %s\n") % ctx.user(),
716 self.ui.write(_("user: %s\n") % ctx.user(),
716 label='log.user')
717 label='log.user')
717 self.ui.write(_("date: %s\n") % date,
718 self.ui.write(_("date: %s\n") % date,
718 label='log.date')
719 label='log.date')
719
720
720 if self.ui.debugflag:
721 if self.ui.debugflag:
721 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
722 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
722 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
723 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
723 files):
724 files):
724 if value:
725 if value:
725 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
726 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
726 label='ui.debug log.files')
727 label='ui.debug log.files')
727 elif ctx.files() and self.ui.verbose:
728 elif ctx.files() and self.ui.verbose:
728 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
729 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
729 label='ui.note log.files')
730 label='ui.note log.files')
730 if copies and self.ui.verbose:
731 if copies and self.ui.verbose:
731 copies = ['%s (%s)' % c for c in copies]
732 copies = ['%s (%s)' % c for c in copies]
732 self.ui.write(_("copies: %s\n") % ' '.join(copies),
733 self.ui.write(_("copies: %s\n") % ' '.join(copies),
733 label='ui.note log.copies')
734 label='ui.note log.copies')
734
735
735 extra = ctx.extra()
736 extra = ctx.extra()
736 if extra and self.ui.debugflag:
737 if extra and self.ui.debugflag:
737 for key, value in sorted(extra.items()):
738 for key, value in sorted(extra.items()):
738 self.ui.write(_("extra: %s=%s\n")
739 self.ui.write(_("extra: %s=%s\n")
739 % (key, value.encode('string_escape')),
740 % (key, value.encode('string_escape')),
740 label='ui.debug log.extra')
741 label='ui.debug log.extra')
741
742
742 description = ctx.description().strip()
743 description = ctx.description().strip()
743 if description:
744 if description:
744 if self.ui.verbose:
745 if self.ui.verbose:
745 self.ui.write(_("description:\n"),
746 self.ui.write(_("description:\n"),
746 label='ui.note log.description')
747 label='ui.note log.description')
747 self.ui.write(description,
748 self.ui.write(description,
748 label='ui.note log.description')
749 label='ui.note log.description')
749 self.ui.write("\n\n")
750 self.ui.write("\n\n")
750 else:
751 else:
751 self.ui.write(_("summary: %s\n") %
752 self.ui.write(_("summary: %s\n") %
752 description.splitlines()[0],
753 description.splitlines()[0],
753 label='log.summary')
754 label='log.summary')
754 self.ui.write("\n")
755 self.ui.write("\n")
755
756
756 self.showpatch(changenode, matchfn)
757 self.showpatch(changenode, matchfn)
757
758
758 def showpatch(self, node, matchfn):
759 def showpatch(self, node, matchfn):
759 if not matchfn:
760 if not matchfn:
760 matchfn = self.patch
761 matchfn = self.patch
761 if matchfn:
762 if matchfn:
762 stat = self.diffopts.get('stat')
763 stat = self.diffopts.get('stat')
763 diff = self.diffopts.get('patch')
764 diff = self.diffopts.get('patch')
764 diffopts = patch.diffopts(self.ui, self.diffopts)
765 diffopts = patch.diffopts(self.ui, self.diffopts)
765 prev = self.repo.changelog.parents(node)[0]
766 prev = self.repo.changelog.parents(node)[0]
766 if stat:
767 if stat:
767 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
768 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
768 match=matchfn, stat=True)
769 match=matchfn, stat=True)
769 if diff:
770 if diff:
770 if stat:
771 if stat:
771 self.ui.write("\n")
772 self.ui.write("\n")
772 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
773 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
773 match=matchfn, stat=False)
774 match=matchfn, stat=False)
774 self.ui.write("\n")
775 self.ui.write("\n")
775
776
776 def _meaningful_parentrevs(self, log, rev):
777 def _meaningful_parentrevs(self, log, rev):
777 """Return list of meaningful (or all if debug) parentrevs for rev.
778 """Return list of meaningful (or all if debug) parentrevs for rev.
778
779
779 For merges (two non-nullrev revisions) both parents are meaningful.
780 For merges (two non-nullrev revisions) both parents are meaningful.
780 Otherwise the first parent revision is considered meaningful if it
781 Otherwise the first parent revision is considered meaningful if it
781 is not the preceding revision.
782 is not the preceding revision.
782 """
783 """
783 parents = log.parentrevs(rev)
784 parents = log.parentrevs(rev)
784 if not self.ui.debugflag and parents[1] == nullrev:
785 if not self.ui.debugflag and parents[1] == nullrev:
785 if parents[0] >= rev - 1:
786 if parents[0] >= rev - 1:
786 parents = []
787 parents = []
787 else:
788 else:
788 parents = [parents[0]]
789 parents = [parents[0]]
789 return parents
790 return parents
790
791
791
792
792 class changeset_templater(changeset_printer):
793 class changeset_templater(changeset_printer):
793 '''format changeset information.'''
794 '''format changeset information.'''
794
795
795 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
796 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
796 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
797 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
797 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
798 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
798 defaulttempl = {
799 defaulttempl = {
799 'parent': '{rev}:{node|formatnode} ',
800 'parent': '{rev}:{node|formatnode} ',
800 'manifest': '{rev}:{node|formatnode}',
801 'manifest': '{rev}:{node|formatnode}',
801 'file_copy': '{name} ({source})',
802 'file_copy': '{name} ({source})',
802 'extra': '{key}={value|stringescape}'
803 'extra': '{key}={value|stringescape}'
803 }
804 }
804 # filecopy is preserved for compatibility reasons
805 # filecopy is preserved for compatibility reasons
805 defaulttempl['filecopy'] = defaulttempl['file_copy']
806 defaulttempl['filecopy'] = defaulttempl['file_copy']
806 self.t = templater.templater(mapfile, {'formatnode': formatnode},
807 self.t = templater.templater(mapfile, {'formatnode': formatnode},
807 cache=defaulttempl)
808 cache=defaulttempl)
808 self.cache = {}
809 self.cache = {}
809
810
810 def use_template(self, t):
811 def use_template(self, t):
811 '''set template string to use'''
812 '''set template string to use'''
812 self.t.cache['changeset'] = t
813 self.t.cache['changeset'] = t
813
814
814 def _meaningful_parentrevs(self, ctx):
815 def _meaningful_parentrevs(self, ctx):
815 """Return list of meaningful (or all if debug) parentrevs for rev.
816 """Return list of meaningful (or all if debug) parentrevs for rev.
816 """
817 """
817 parents = ctx.parents()
818 parents = ctx.parents()
818 if len(parents) > 1:
819 if len(parents) > 1:
819 return parents
820 return parents
820 if self.ui.debugflag:
821 if self.ui.debugflag:
821 return [parents[0], self.repo['null']]
822 return [parents[0], self.repo['null']]
822 if parents[0].rev() >= ctx.rev() - 1:
823 if parents[0].rev() >= ctx.rev() - 1:
823 return []
824 return []
824 return parents
825 return parents
825
826
826 def _show(self, ctx, copies, matchfn, props):
827 def _show(self, ctx, copies, matchfn, props):
827 '''show a single changeset or file revision'''
828 '''show a single changeset or file revision'''
828
829
829 showlist = templatekw.showlist
830 showlist = templatekw.showlist
830
831
831 # showparents() behaviour depends on ui trace level which
832 # showparents() behaviour depends on ui trace level which
832 # causes unexpected behaviours at templating level and makes
833 # causes unexpected behaviours at templating level and makes
833 # it harder to extract it in a standalone function. Its
834 # it harder to extract it in a standalone function. Its
834 # behaviour cannot be changed so leave it here for now.
835 # behaviour cannot be changed so leave it here for now.
835 def showparents(**args):
836 def showparents(**args):
836 ctx = args['ctx']
837 ctx = args['ctx']
837 parents = [[('rev', p.rev()), ('node', p.hex())]
838 parents = [[('rev', p.rev()), ('node', p.hex())]
838 for p in self._meaningful_parentrevs(ctx)]
839 for p in self._meaningful_parentrevs(ctx)]
839 return showlist('parent', parents, **args)
840 return showlist('parent', parents, **args)
840
841
841 props = props.copy()
842 props = props.copy()
842 props.update(templatekw.keywords)
843 props.update(templatekw.keywords)
843 props['parents'] = showparents
844 props['parents'] = showparents
844 props['templ'] = self.t
845 props['templ'] = self.t
845 props['ctx'] = ctx
846 props['ctx'] = ctx
846 props['repo'] = self.repo
847 props['repo'] = self.repo
847 props['revcache'] = {'copies': copies}
848 props['revcache'] = {'copies': copies}
848 props['cache'] = self.cache
849 props['cache'] = self.cache
849
850
850 # find correct templates for current mode
851 # find correct templates for current mode
851
852
852 tmplmodes = [
853 tmplmodes = [
853 (True, None),
854 (True, None),
854 (self.ui.verbose, 'verbose'),
855 (self.ui.verbose, 'verbose'),
855 (self.ui.quiet, 'quiet'),
856 (self.ui.quiet, 'quiet'),
856 (self.ui.debugflag, 'debug'),
857 (self.ui.debugflag, 'debug'),
857 ]
858 ]
858
859
859 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
860 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
860 for mode, postfix in tmplmodes:
861 for mode, postfix in tmplmodes:
861 for type in types:
862 for type in types:
862 cur = postfix and ('%s_%s' % (type, postfix)) or type
863 cur = postfix and ('%s_%s' % (type, postfix)) or type
863 if mode and cur in self.t:
864 if mode and cur in self.t:
864 types[type] = cur
865 types[type] = cur
865
866
866 try:
867 try:
867
868
868 # write header
869 # write header
869 if types['header']:
870 if types['header']:
870 h = templater.stringify(self.t(types['header'], **props))
871 h = templater.stringify(self.t(types['header'], **props))
871 if self.buffered:
872 if self.buffered:
872 self.header[ctx.rev()] = h
873 self.header[ctx.rev()] = h
873 else:
874 else:
874 if self.lastheader != h:
875 if self.lastheader != h:
875 self.lastheader = h
876 self.lastheader = h
876 self.ui.write(h)
877 self.ui.write(h)
877
878
878 # write changeset metadata, then patch if requested
879 # write changeset metadata, then patch if requested
879 key = types['changeset']
880 key = types['changeset']
880 self.ui.write(templater.stringify(self.t(key, **props)))
881 self.ui.write(templater.stringify(self.t(key, **props)))
881 self.showpatch(ctx.node(), matchfn)
882 self.showpatch(ctx.node(), matchfn)
882
883
883 if types['footer']:
884 if types['footer']:
884 if not self.footer:
885 if not self.footer:
885 self.footer = templater.stringify(self.t(types['footer'],
886 self.footer = templater.stringify(self.t(types['footer'],
886 **props))
887 **props))
887
888
888 except KeyError, inst:
889 except KeyError, inst:
889 msg = _("%s: no key named '%s'")
890 msg = _("%s: no key named '%s'")
890 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
891 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
891 except SyntaxError, inst:
892 except SyntaxError, inst:
892 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
893 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
893
894
894 def show_changeset(ui, repo, opts, buffered=False):
895 def show_changeset(ui, repo, opts, buffered=False):
895 """show one changeset using template or regular display.
896 """show one changeset using template or regular display.
896
897
897 Display format will be the first non-empty hit of:
898 Display format will be the first non-empty hit of:
898 1. option 'template'
899 1. option 'template'
899 2. option 'style'
900 2. option 'style'
900 3. [ui] setting 'logtemplate'
901 3. [ui] setting 'logtemplate'
901 4. [ui] setting 'style'
902 4. [ui] setting 'style'
902 If all of these values are either the unset or the empty string,
903 If all of these values are either the unset or the empty string,
903 regular display via changeset_printer() is done.
904 regular display via changeset_printer() is done.
904 """
905 """
905 # options
906 # options
906 patch = False
907 patch = False
907 if opts.get('patch') or opts.get('stat'):
908 if opts.get('patch') or opts.get('stat'):
908 patch = scmutil.matchall(repo)
909 patch = scmutil.matchall(repo)
909
910
910 tmpl = opts.get('template')
911 tmpl = opts.get('template')
911 style = None
912 style = None
912 if tmpl:
913 if tmpl:
913 tmpl = templater.parsestring(tmpl, quoted=False)
914 tmpl = templater.parsestring(tmpl, quoted=False)
914 else:
915 else:
915 style = opts.get('style')
916 style = opts.get('style')
916
917
917 # ui settings
918 # ui settings
918 if not (tmpl or style):
919 if not (tmpl or style):
919 tmpl = ui.config('ui', 'logtemplate')
920 tmpl = ui.config('ui', 'logtemplate')
920 if tmpl:
921 if tmpl:
921 try:
922 try:
922 tmpl = templater.parsestring(tmpl)
923 tmpl = templater.parsestring(tmpl)
923 except SyntaxError:
924 except SyntaxError:
924 tmpl = templater.parsestring(tmpl, quoted=False)
925 tmpl = templater.parsestring(tmpl, quoted=False)
925 else:
926 else:
926 style = util.expandpath(ui.config('ui', 'style', ''))
927 style = util.expandpath(ui.config('ui', 'style', ''))
927
928
928 if not (tmpl or style):
929 if not (tmpl or style):
929 return changeset_printer(ui, repo, patch, opts, buffered)
930 return changeset_printer(ui, repo, patch, opts, buffered)
930
931
931 mapfile = None
932 mapfile = None
932 if style and not tmpl:
933 if style and not tmpl:
933 mapfile = style
934 mapfile = style
934 if not os.path.split(mapfile)[0]:
935 if not os.path.split(mapfile)[0]:
935 mapname = (templater.templatepath('map-cmdline.' + mapfile)
936 mapname = (templater.templatepath('map-cmdline.' + mapfile)
936 or templater.templatepath(mapfile))
937 or templater.templatepath(mapfile))
937 if mapname:
938 if mapname:
938 mapfile = mapname
939 mapfile = mapname
939
940
940 try:
941 try:
941 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
942 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
942 except SyntaxError, inst:
943 except SyntaxError, inst:
943 raise util.Abort(inst.args[0])
944 raise util.Abort(inst.args[0])
944 if tmpl:
945 if tmpl:
945 t.use_template(tmpl)
946 t.use_template(tmpl)
946 return t
947 return t
947
948
948 def finddate(ui, repo, date):
949 def finddate(ui, repo, date):
949 """Find the tipmost changeset that matches the given date spec"""
950 """Find the tipmost changeset that matches the given date spec"""
950
951
951 df = util.matchdate(date)
952 df = util.matchdate(date)
952 m = scmutil.matchall(repo)
953 m = scmutil.matchall(repo)
953 results = {}
954 results = {}
954
955
955 def prep(ctx, fns):
956 def prep(ctx, fns):
956 d = ctx.date()
957 d = ctx.date()
957 if df(d[0]):
958 if df(d[0]):
958 results[ctx.rev()] = d
959 results[ctx.rev()] = d
959
960
960 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
961 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
961 rev = ctx.rev()
962 rev = ctx.rev()
962 if rev in results:
963 if rev in results:
963 ui.status(_("found revision %s from %s\n") %
964 ui.status(_("found revision %s from %s\n") %
964 (rev, util.datestr(results[rev])))
965 (rev, util.datestr(results[rev])))
965 return str(rev)
966 return str(rev)
966
967
967 raise util.Abort(_("revision matching date not found"))
968 raise util.Abort(_("revision matching date not found"))
968
969
969 def increasingwindows(start, end, windowsize=8, sizelimit=512):
970 def increasingwindows(start, end, windowsize=8, sizelimit=512):
970 if start < end:
971 if start < end:
971 while start < end:
972 while start < end:
972 yield start, min(windowsize, end - start)
973 yield start, min(windowsize, end - start)
973 start += windowsize
974 start += windowsize
974 if windowsize < sizelimit:
975 if windowsize < sizelimit:
975 windowsize *= 2
976 windowsize *= 2
976 else:
977 else:
977 while start > end:
978 while start > end:
978 yield start, min(windowsize, start - end - 1)
979 yield start, min(windowsize, start - end - 1)
979 start -= windowsize
980 start -= windowsize
980 if windowsize < sizelimit:
981 if windowsize < sizelimit:
981 windowsize *= 2
982 windowsize *= 2
982
983
983 def walkchangerevs(repo, match, opts, prepare):
984 def walkchangerevs(repo, match, opts, prepare):
984 '''Iterate over files and the revs in which they changed.
985 '''Iterate over files and the revs in which they changed.
985
986
986 Callers most commonly need to iterate backwards over the history
987 Callers most commonly need to iterate backwards over the history
987 in which they are interested. Doing so has awful (quadratic-looking)
988 in which they are interested. Doing so has awful (quadratic-looking)
988 performance, so we use iterators in a "windowed" way.
989 performance, so we use iterators in a "windowed" way.
989
990
990 We walk a window of revisions in the desired order. Within the
991 We walk a window of revisions in the desired order. Within the
991 window, we first walk forwards to gather data, then in the desired
992 window, we first walk forwards to gather data, then in the desired
992 order (usually backwards) to display it.
993 order (usually backwards) to display it.
993
994
994 This function returns an iterator yielding contexts. Before
995 This function returns an iterator yielding contexts. Before
995 yielding each context, the iterator will first call the prepare
996 yielding each context, the iterator will first call the prepare
996 function on each context in the window in forward order.'''
997 function on each context in the window in forward order.'''
997
998
998 follow = opts.get('follow') or opts.get('follow_first')
999 follow = opts.get('follow') or opts.get('follow_first')
999
1000
1000 if not len(repo):
1001 if not len(repo):
1001 return []
1002 return []
1002 if opts.get('rev'):
1003 if opts.get('rev'):
1003 revs = scmutil.revrange(repo, opts.get('rev'))
1004 revs = scmutil.revrange(repo, opts.get('rev'))
1004 elif follow:
1005 elif follow:
1005 revs = repo.revs('reverse(:.)')
1006 revs = repo.revs('reverse(:.)')
1006 else:
1007 else:
1007 revs = list(repo)
1008 revs = list(repo)
1008 revs.reverse()
1009 revs.reverse()
1009 if not revs:
1010 if not revs:
1010 return []
1011 return []
1011 wanted = set()
1012 wanted = set()
1012 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1013 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1013 fncache = {}
1014 fncache = {}
1014 change = repo.changectx
1015 change = repo.changectx
1015
1016
1016 # First step is to fill wanted, the set of revisions that we want to yield.
1017 # First step is to fill wanted, the set of revisions that we want to yield.
1017 # When it does not induce extra cost, we also fill fncache for revisions in
1018 # When it does not induce extra cost, we also fill fncache for revisions in
1018 # wanted: a cache of filenames that were changed (ctx.files()) and that
1019 # wanted: a cache of filenames that were changed (ctx.files()) and that
1019 # match the file filtering conditions.
1020 # match the file filtering conditions.
1020
1021
1021 if not slowpath and not match.files():
1022 if not slowpath and not match.files():
1022 # No files, no patterns. Display all revs.
1023 # No files, no patterns. Display all revs.
1023 wanted = set(revs)
1024 wanted = set(revs)
1024 copies = []
1025 copies = []
1025
1026
1026 if not slowpath and match.files():
1027 if not slowpath and match.files():
1027 # We only have to read through the filelog to find wanted revisions
1028 # We only have to read through the filelog to find wanted revisions
1028
1029
1029 minrev, maxrev = min(revs), max(revs)
1030 minrev, maxrev = min(revs), max(revs)
1030 def filerevgen(filelog, last):
1031 def filerevgen(filelog, last):
1031 """
1032 """
1032 Only files, no patterns. Check the history of each file.
1033 Only files, no patterns. Check the history of each file.
1033
1034
1034 Examines filelog entries within minrev, maxrev linkrev range
1035 Examines filelog entries within minrev, maxrev linkrev range
1035 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1036 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1036 tuples in backwards order
1037 tuples in backwards order
1037 """
1038 """
1038 cl_count = len(repo)
1039 cl_count = len(repo)
1039 revs = []
1040 revs = []
1040 for j in xrange(0, last + 1):
1041 for j in xrange(0, last + 1):
1041 linkrev = filelog.linkrev(j)
1042 linkrev = filelog.linkrev(j)
1042 if linkrev < minrev:
1043 if linkrev < minrev:
1043 continue
1044 continue
1044 # only yield rev for which we have the changelog, it can
1045 # only yield rev for which we have the changelog, it can
1045 # happen while doing "hg log" during a pull or commit
1046 # happen while doing "hg log" during a pull or commit
1046 if linkrev >= cl_count:
1047 if linkrev >= cl_count:
1047 break
1048 break
1048
1049
1049 parentlinkrevs = []
1050 parentlinkrevs = []
1050 for p in filelog.parentrevs(j):
1051 for p in filelog.parentrevs(j):
1051 if p != nullrev:
1052 if p != nullrev:
1052 parentlinkrevs.append(filelog.linkrev(p))
1053 parentlinkrevs.append(filelog.linkrev(p))
1053 n = filelog.node(j)
1054 n = filelog.node(j)
1054 revs.append((linkrev, parentlinkrevs,
1055 revs.append((linkrev, parentlinkrevs,
1055 follow and filelog.renamed(n)))
1056 follow and filelog.renamed(n)))
1056
1057
1057 return reversed(revs)
1058 return reversed(revs)
1058 def iterfiles():
1059 def iterfiles():
1059 pctx = repo['.']
1060 pctx = repo['.']
1060 for filename in match.files():
1061 for filename in match.files():
1061 if follow:
1062 if follow:
1062 if filename not in pctx:
1063 if filename not in pctx:
1063 raise util.Abort(_('cannot follow file not in parent '
1064 raise util.Abort(_('cannot follow file not in parent '
1064 'revision: "%s"') % filename)
1065 'revision: "%s"') % filename)
1065 yield filename, pctx[filename].filenode()
1066 yield filename, pctx[filename].filenode()
1066 else:
1067 else:
1067 yield filename, None
1068 yield filename, None
1068 for filename_node in copies:
1069 for filename_node in copies:
1069 yield filename_node
1070 yield filename_node
1070 for file_, node in iterfiles():
1071 for file_, node in iterfiles():
1071 filelog = repo.file(file_)
1072 filelog = repo.file(file_)
1072 if not len(filelog):
1073 if not len(filelog):
1073 if node is None:
1074 if node is None:
1074 # A zero count may be a directory or deleted file, so
1075 # A zero count may be a directory or deleted file, so
1075 # try to find matching entries on the slow path.
1076 # try to find matching entries on the slow path.
1076 if follow:
1077 if follow:
1077 raise util.Abort(
1078 raise util.Abort(
1078 _('cannot follow nonexistent file: "%s"') % file_)
1079 _('cannot follow nonexistent file: "%s"') % file_)
1079 slowpath = True
1080 slowpath = True
1080 break
1081 break
1081 else:
1082 else:
1082 continue
1083 continue
1083
1084
1084 if node is None:
1085 if node is None:
1085 last = len(filelog) - 1
1086 last = len(filelog) - 1
1086 else:
1087 else:
1087 last = filelog.rev(node)
1088 last = filelog.rev(node)
1088
1089
1089
1090
1090 # keep track of all ancestors of the file
1091 # keep track of all ancestors of the file
1091 ancestors = set([filelog.linkrev(last)])
1092 ancestors = set([filelog.linkrev(last)])
1092
1093
1093 # iterate from latest to oldest revision
1094 # iterate from latest to oldest revision
1094 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1095 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1095 if not follow:
1096 if not follow:
1096 if rev > maxrev:
1097 if rev > maxrev:
1097 continue
1098 continue
1098 else:
1099 else:
1099 # Note that last might not be the first interesting
1100 # Note that last might not be the first interesting
1100 # rev to us:
1101 # rev to us:
1101 # if the file has been changed after maxrev, we'll
1102 # if the file has been changed after maxrev, we'll
1102 # have linkrev(last) > maxrev, and we still need
1103 # have linkrev(last) > maxrev, and we still need
1103 # to explore the file graph
1104 # to explore the file graph
1104 if rev not in ancestors:
1105 if rev not in ancestors:
1105 continue
1106 continue
1106 # XXX insert 1327 fix here
1107 # XXX insert 1327 fix here
1107 if flparentlinkrevs:
1108 if flparentlinkrevs:
1108 ancestors.update(flparentlinkrevs)
1109 ancestors.update(flparentlinkrevs)
1109
1110
1110 fncache.setdefault(rev, []).append(file_)
1111 fncache.setdefault(rev, []).append(file_)
1111 wanted.add(rev)
1112 wanted.add(rev)
1112 if copied:
1113 if copied:
1113 copies.append(copied)
1114 copies.append(copied)
1114
1115
1115 # We decided to fall back to the slowpath because at least one
1116 # We decided to fall back to the slowpath because at least one
1116 # of the paths was not a file. Check to see if at least one of them
1117 # of the paths was not a file. Check to see if at least one of them
1117 # existed in history, otherwise simply return
1118 # existed in history, otherwise simply return
1118 if slowpath:
1119 if slowpath:
1119 for path in match.files():
1120 for path in match.files():
1120 if path == '.' or path in repo.store:
1121 if path == '.' or path in repo.store:
1121 break
1122 break
1122 else:
1123 else:
1123 return []
1124 return []
1124
1125
1125 if slowpath:
1126 if slowpath:
1126 # We have to read the changelog to match filenames against
1127 # We have to read the changelog to match filenames against
1127 # changed files
1128 # changed files
1128
1129
1129 if follow:
1130 if follow:
1130 raise util.Abort(_('can only follow copies/renames for explicit '
1131 raise util.Abort(_('can only follow copies/renames for explicit '
1131 'filenames'))
1132 'filenames'))
1132
1133
1133 # The slow path checks files modified in every changeset.
1134 # The slow path checks files modified in every changeset.
1134 for i in sorted(revs):
1135 for i in sorted(revs):
1135 ctx = change(i)
1136 ctx = change(i)
1136 matches = filter(match, ctx.files())
1137 matches = filter(match, ctx.files())
1137 if matches:
1138 if matches:
1138 fncache[i] = matches
1139 fncache[i] = matches
1139 wanted.add(i)
1140 wanted.add(i)
1140
1141
1141 class followfilter(object):
1142 class followfilter(object):
1142 def __init__(self, onlyfirst=False):
1143 def __init__(self, onlyfirst=False):
1143 self.startrev = nullrev
1144 self.startrev = nullrev
1144 self.roots = set()
1145 self.roots = set()
1145 self.onlyfirst = onlyfirst
1146 self.onlyfirst = onlyfirst
1146
1147
1147 def match(self, rev):
1148 def match(self, rev):
1148 def realparents(rev):
1149 def realparents(rev):
1149 if self.onlyfirst:
1150 if self.onlyfirst:
1150 return repo.changelog.parentrevs(rev)[0:1]
1151 return repo.changelog.parentrevs(rev)[0:1]
1151 else:
1152 else:
1152 return filter(lambda x: x != nullrev,
1153 return filter(lambda x: x != nullrev,
1153 repo.changelog.parentrevs(rev))
1154 repo.changelog.parentrevs(rev))
1154
1155
1155 if self.startrev == nullrev:
1156 if self.startrev == nullrev:
1156 self.startrev = rev
1157 self.startrev = rev
1157 return True
1158 return True
1158
1159
1159 if rev > self.startrev:
1160 if rev > self.startrev:
1160 # forward: all descendants
1161 # forward: all descendants
1161 if not self.roots:
1162 if not self.roots:
1162 self.roots.add(self.startrev)
1163 self.roots.add(self.startrev)
1163 for parent in realparents(rev):
1164 for parent in realparents(rev):
1164 if parent in self.roots:
1165 if parent in self.roots:
1165 self.roots.add(rev)
1166 self.roots.add(rev)
1166 return True
1167 return True
1167 else:
1168 else:
1168 # backwards: all parents
1169 # backwards: all parents
1169 if not self.roots:
1170 if not self.roots:
1170 self.roots.update(realparents(self.startrev))
1171 self.roots.update(realparents(self.startrev))
1171 if rev in self.roots:
1172 if rev in self.roots:
1172 self.roots.remove(rev)
1173 self.roots.remove(rev)
1173 self.roots.update(realparents(rev))
1174 self.roots.update(realparents(rev))
1174 return True
1175 return True
1175
1176
1176 return False
1177 return False
1177
1178
1178 # it might be worthwhile to do this in the iterator if the rev range
1179 # it might be worthwhile to do this in the iterator if the rev range
1179 # is descending and the prune args are all within that range
1180 # is descending and the prune args are all within that range
1180 for rev in opts.get('prune', ()):
1181 for rev in opts.get('prune', ()):
1181 rev = repo[rev].rev()
1182 rev = repo[rev].rev()
1182 ff = followfilter()
1183 ff = followfilter()
1183 stop = min(revs[0], revs[-1])
1184 stop = min(revs[0], revs[-1])
1184 for x in xrange(rev, stop - 1, -1):
1185 for x in xrange(rev, stop - 1, -1):
1185 if ff.match(x):
1186 if ff.match(x):
1186 wanted.discard(x)
1187 wanted.discard(x)
1187
1188
1188 # Now that wanted is correctly initialized, we can iterate over the
1189 # Now that wanted is correctly initialized, we can iterate over the
1189 # revision range, yielding only revisions in wanted.
1190 # revision range, yielding only revisions in wanted.
1190 def iterate():
1191 def iterate():
1191 if follow and not match.files():
1192 if follow and not match.files():
1192 ff = followfilter(onlyfirst=opts.get('follow_first'))
1193 ff = followfilter(onlyfirst=opts.get('follow_first'))
1193 def want(rev):
1194 def want(rev):
1194 return ff.match(rev) and rev in wanted
1195 return ff.match(rev) and rev in wanted
1195 else:
1196 else:
1196 def want(rev):
1197 def want(rev):
1197 return rev in wanted
1198 return rev in wanted
1198
1199
1199 for i, window in increasingwindows(0, len(revs)):
1200 for i, window in increasingwindows(0, len(revs)):
1200 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1201 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1201 for rev in sorted(nrevs):
1202 for rev in sorted(nrevs):
1202 fns = fncache.get(rev)
1203 fns = fncache.get(rev)
1203 ctx = change(rev)
1204 ctx = change(rev)
1204 if not fns:
1205 if not fns:
1205 def fns_generator():
1206 def fns_generator():
1206 for f in ctx.files():
1207 for f in ctx.files():
1207 if match(f):
1208 if match(f):
1208 yield f
1209 yield f
1209 fns = fns_generator()
1210 fns = fns_generator()
1210 prepare(ctx, fns)
1211 prepare(ctx, fns)
1211 for rev in nrevs:
1212 for rev in nrevs:
1212 yield change(rev)
1213 yield change(rev)
1213 return iterate()
1214 return iterate()
1214
1215
1215 def _makegraphfilematcher(repo, pats, followfirst):
1216 def _makegraphfilematcher(repo, pats, followfirst):
1216 # When displaying a revision with --patch --follow FILE, we have
1217 # When displaying a revision with --patch --follow FILE, we have
1217 # to know which file of the revision must be diffed. With
1218 # to know which file of the revision must be diffed. With
1218 # --follow, we want the names of the ancestors of FILE in the
1219 # --follow, we want the names of the ancestors of FILE in the
1219 # revision, stored in "fcache". "fcache" is populated by
1220 # revision, stored in "fcache". "fcache" is populated by
1220 # reproducing the graph traversal already done by --follow revset
1221 # reproducing the graph traversal already done by --follow revset
1221 # and relating linkrevs to file names (which is not "correct" but
1222 # and relating linkrevs to file names (which is not "correct" but
1222 # good enough).
1223 # good enough).
1223 fcache = {}
1224 fcache = {}
1224 fcacheready = [False]
1225 fcacheready = [False]
1225 pctx = repo['.']
1226 pctx = repo['.']
1226 wctx = repo[None]
1227 wctx = repo[None]
1227
1228
1228 def populate():
1229 def populate():
1229 for fn in pats:
1230 for fn in pats:
1230 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1231 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1231 for c in i:
1232 for c in i:
1232 fcache.setdefault(c.linkrev(), set()).add(c.path())
1233 fcache.setdefault(c.linkrev(), set()).add(c.path())
1233
1234
1234 def filematcher(rev):
1235 def filematcher(rev):
1235 if not fcacheready[0]:
1236 if not fcacheready[0]:
1236 # Lazy initialization
1237 # Lazy initialization
1237 fcacheready[0] = True
1238 fcacheready[0] = True
1238 populate()
1239 populate()
1239 return scmutil.match(wctx, fcache.get(rev, []), default='path')
1240 return scmutil.match(wctx, fcache.get(rev, []), default='path')
1240
1241
1241 return filematcher
1242 return filematcher
1242
1243
1243 def _makegraphlogrevset(repo, pats, opts, revs):
1244 def _makegraphlogrevset(repo, pats, opts, revs):
1244 """Return (expr, filematcher) where expr is a revset string built
1245 """Return (expr, filematcher) where expr is a revset string built
1245 from log options and file patterns or None. If --stat or --patch
1246 from log options and file patterns or None. If --stat or --patch
1246 are not passed filematcher is None. Otherwise it is a callable
1247 are not passed filematcher is None. Otherwise it is a callable
1247 taking a revision number and returning a match objects filtering
1248 taking a revision number and returning a match objects filtering
1248 the files to be detailed when displaying the revision.
1249 the files to be detailed when displaying the revision.
1249 """
1250 """
1250 opt2revset = {
1251 opt2revset = {
1251 'no_merges': ('not merge()', None),
1252 'no_merges': ('not merge()', None),
1252 'only_merges': ('merge()', None),
1253 'only_merges': ('merge()', None),
1253 '_ancestors': ('ancestors(%(val)s)', None),
1254 '_ancestors': ('ancestors(%(val)s)', None),
1254 '_fancestors': ('_firstancestors(%(val)s)', None),
1255 '_fancestors': ('_firstancestors(%(val)s)', None),
1255 '_descendants': ('descendants(%(val)s)', None),
1256 '_descendants': ('descendants(%(val)s)', None),
1256 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1257 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1257 '_matchfiles': ('_matchfiles(%(val)s)', None),
1258 '_matchfiles': ('_matchfiles(%(val)s)', None),
1258 'date': ('date(%(val)r)', None),
1259 'date': ('date(%(val)r)', None),
1259 'branch': ('branch(%(val)r)', ' or '),
1260 'branch': ('branch(%(val)r)', ' or '),
1260 '_patslog': ('filelog(%(val)r)', ' or '),
1261 '_patslog': ('filelog(%(val)r)', ' or '),
1261 '_patsfollow': ('follow(%(val)r)', ' or '),
1262 '_patsfollow': ('follow(%(val)r)', ' or '),
1262 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1263 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1263 'keyword': ('keyword(%(val)r)', ' or '),
1264 'keyword': ('keyword(%(val)r)', ' or '),
1264 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1265 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1265 'user': ('user(%(val)r)', ' or '),
1266 'user': ('user(%(val)r)', ' or '),
1266 }
1267 }
1267
1268
1268 opts = dict(opts)
1269 opts = dict(opts)
1269 # follow or not follow?
1270 # follow or not follow?
1270 follow = opts.get('follow') or opts.get('follow_first')
1271 follow = opts.get('follow') or opts.get('follow_first')
1271 followfirst = opts.get('follow_first') and 1 or 0
1272 followfirst = opts.get('follow_first') and 1 or 0
1272 # --follow with FILE behaviour depends on revs...
1273 # --follow with FILE behaviour depends on revs...
1273 startrev = revs[0]
1274 startrev = revs[0]
1274 followdescendants = (len(revs) > 1 and revs[0] < revs[1]) and 1 or 0
1275 followdescendants = (len(revs) > 1 and revs[0] < revs[1]) and 1 or 0
1275
1276
1276 # branch and only_branch are really aliases and must be handled at
1277 # branch and only_branch are really aliases and must be handled at
1277 # the same time
1278 # the same time
1278 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1279 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1279 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1280 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1280 # pats/include/exclude are passed to match.match() directly in
1281 # pats/include/exclude are passed to match.match() directly in
1281 # _matchfiles() revset but walkchangerevs() builds its matcher with
1282 # _matchfiles() revset but walkchangerevs() builds its matcher with
1282 # scmutil.match(). The difference is input pats are globbed on
1283 # scmutil.match(). The difference is input pats are globbed on
1283 # platforms without shell expansion (windows).
1284 # platforms without shell expansion (windows).
1284 pctx = repo[None]
1285 pctx = repo[None]
1285 match, pats = scmutil.matchandpats(pctx, pats, opts)
1286 match, pats = scmutil.matchandpats(pctx, pats, opts)
1286 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1287 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1287 if not slowpath:
1288 if not slowpath:
1288 for f in match.files():
1289 for f in match.files():
1289 if follow and f not in pctx:
1290 if follow and f not in pctx:
1290 raise util.Abort(_('cannot follow file not in parent '
1291 raise util.Abort(_('cannot follow file not in parent '
1291 'revision: "%s"') % f)
1292 'revision: "%s"') % f)
1292 filelog = repo.file(f)
1293 filelog = repo.file(f)
1293 if not len(filelog):
1294 if not len(filelog):
1294 # A zero count may be a directory or deleted file, so
1295 # A zero count may be a directory or deleted file, so
1295 # try to find matching entries on the slow path.
1296 # try to find matching entries on the slow path.
1296 if follow:
1297 if follow:
1297 raise util.Abort(
1298 raise util.Abort(
1298 _('cannot follow nonexistent file: "%s"') % f)
1299 _('cannot follow nonexistent file: "%s"') % f)
1299 slowpath = True
1300 slowpath = True
1300
1301
1301 # We decided to fall back to the slowpath because at least one
1302 # We decided to fall back to the slowpath because at least one
1302 # of the paths was not a file. Check to see if at least one of them
1303 # of the paths was not a file. Check to see if at least one of them
1303 # existed in history - in that case, we'll continue down the
1304 # existed in history - in that case, we'll continue down the
1304 # slowpath; otherwise, we can turn off the slowpath
1305 # slowpath; otherwise, we can turn off the slowpath
1305 if slowpath:
1306 if slowpath:
1306 for path in match.files():
1307 for path in match.files():
1307 if path == '.' or path in repo.store:
1308 if path == '.' or path in repo.store:
1308 break
1309 break
1309 else:
1310 else:
1310 slowpath = False
1311 slowpath = False
1311
1312
1312 if slowpath:
1313 if slowpath:
1313 # See walkchangerevs() slow path.
1314 # See walkchangerevs() slow path.
1314 #
1315 #
1315 if follow:
1316 if follow:
1316 raise util.Abort(_('can only follow copies/renames for explicit '
1317 raise util.Abort(_('can only follow copies/renames for explicit '
1317 'filenames'))
1318 'filenames'))
1318 # pats/include/exclude cannot be represented as separate
1319 # pats/include/exclude cannot be represented as separate
1319 # revset expressions as their filtering logic applies at file
1320 # revset expressions as their filtering logic applies at file
1320 # level. For instance "-I a -X a" matches a revision touching
1321 # level. For instance "-I a -X a" matches a revision touching
1321 # "a" and "b" while "file(a) and not file(b)" does
1322 # "a" and "b" while "file(a) and not file(b)" does
1322 # not. Besides, filesets are evaluated against the working
1323 # not. Besides, filesets are evaluated against the working
1323 # directory.
1324 # directory.
1324 matchargs = ['r:', 'd:relpath']
1325 matchargs = ['r:', 'd:relpath']
1325 for p in pats:
1326 for p in pats:
1326 matchargs.append('p:' + p)
1327 matchargs.append('p:' + p)
1327 for p in opts.get('include', []):
1328 for p in opts.get('include', []):
1328 matchargs.append('i:' + p)
1329 matchargs.append('i:' + p)
1329 for p in opts.get('exclude', []):
1330 for p in opts.get('exclude', []):
1330 matchargs.append('x:' + p)
1331 matchargs.append('x:' + p)
1331 matchargs = ','.join(('%r' % p) for p in matchargs)
1332 matchargs = ','.join(('%r' % p) for p in matchargs)
1332 opts['_matchfiles'] = matchargs
1333 opts['_matchfiles'] = matchargs
1333 else:
1334 else:
1334 if follow:
1335 if follow:
1335 fpats = ('_patsfollow', '_patsfollowfirst')
1336 fpats = ('_patsfollow', '_patsfollowfirst')
1336 fnopats = (('_ancestors', '_fancestors'),
1337 fnopats = (('_ancestors', '_fancestors'),
1337 ('_descendants', '_fdescendants'))
1338 ('_descendants', '_fdescendants'))
1338 if pats:
1339 if pats:
1339 # follow() revset interprets its file argument as a
1340 # follow() revset interprets its file argument as a
1340 # manifest entry, so use match.files(), not pats.
1341 # manifest entry, so use match.files(), not pats.
1341 opts[fpats[followfirst]] = list(match.files())
1342 opts[fpats[followfirst]] = list(match.files())
1342 else:
1343 else:
1343 opts[fnopats[followdescendants][followfirst]] = str(startrev)
1344 opts[fnopats[followdescendants][followfirst]] = str(startrev)
1344 else:
1345 else:
1345 opts['_patslog'] = list(pats)
1346 opts['_patslog'] = list(pats)
1346
1347
1347 filematcher = None
1348 filematcher = None
1348 if opts.get('patch') or opts.get('stat'):
1349 if opts.get('patch') or opts.get('stat'):
1349 if follow:
1350 if follow:
1350 filematcher = _makegraphfilematcher(repo, pats, followfirst)
1351 filematcher = _makegraphfilematcher(repo, pats, followfirst)
1351 else:
1352 else:
1352 filematcher = lambda rev: match
1353 filematcher = lambda rev: match
1353
1354
1354 expr = []
1355 expr = []
1355 for op, val in opts.iteritems():
1356 for op, val in opts.iteritems():
1356 if not val:
1357 if not val:
1357 continue
1358 continue
1358 if op not in opt2revset:
1359 if op not in opt2revset:
1359 continue
1360 continue
1360 revop, andor = opt2revset[op]
1361 revop, andor = opt2revset[op]
1361 if '%(val)' not in revop:
1362 if '%(val)' not in revop:
1362 expr.append(revop)
1363 expr.append(revop)
1363 else:
1364 else:
1364 if not isinstance(val, list):
1365 if not isinstance(val, list):
1365 e = revop % {'val': val}
1366 e = revop % {'val': val}
1366 else:
1367 else:
1367 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
1368 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
1368 expr.append(e)
1369 expr.append(e)
1369
1370
1370 if expr:
1371 if expr:
1371 expr = '(' + ' and '.join(expr) + ')'
1372 expr = '(' + ' and '.join(expr) + ')'
1372 else:
1373 else:
1373 expr = None
1374 expr = None
1374 return expr, filematcher
1375 return expr, filematcher
1375
1376
1376 def getgraphlogrevs(repo, pats, opts):
1377 def getgraphlogrevs(repo, pats, opts):
1377 """Return (revs, expr, filematcher) where revs is an iterable of
1378 """Return (revs, expr, filematcher) where revs is an iterable of
1378 revision numbers, expr is a revset string built from log options
1379 revision numbers, expr is a revset string built from log options
1379 and file patterns or None, and used to filter 'revs'. If --stat or
1380 and file patterns or None, and used to filter 'revs'. If --stat or
1380 --patch are not passed filematcher is None. Otherwise it is a
1381 --patch are not passed filematcher is None. Otherwise it is a
1381 callable taking a revision number and returning a match objects
1382 callable taking a revision number and returning a match objects
1382 filtering the files to be detailed when displaying the revision.
1383 filtering the files to be detailed when displaying the revision.
1383 """
1384 """
1384 def increasingrevs(repo, revs, matcher):
1385 def increasingrevs(repo, revs, matcher):
1385 # The sorted input rev sequence is chopped in sub-sequences
1386 # The sorted input rev sequence is chopped in sub-sequences
1386 # which are sorted in ascending order and passed to the
1387 # which are sorted in ascending order and passed to the
1387 # matcher. The filtered revs are sorted again as they were in
1388 # matcher. The filtered revs are sorted again as they were in
1388 # the original sub-sequence. This achieve several things:
1389 # the original sub-sequence. This achieve several things:
1389 #
1390 #
1390 # - getlogrevs() now returns a generator which behaviour is
1391 # - getlogrevs() now returns a generator which behaviour is
1391 # adapted to log need. First results come fast, last ones
1392 # adapted to log need. First results come fast, last ones
1392 # are batched for performances.
1393 # are batched for performances.
1393 #
1394 #
1394 # - revset matchers often operate faster on revision in
1395 # - revset matchers often operate faster on revision in
1395 # changelog order, because most filters deal with the
1396 # changelog order, because most filters deal with the
1396 # changelog.
1397 # changelog.
1397 #
1398 #
1398 # - revset matchers can reorder revisions. "A or B" typically
1399 # - revset matchers can reorder revisions. "A or B" typically
1399 # returns returns the revision matching A then the revision
1400 # returns returns the revision matching A then the revision
1400 # matching B. We want to hide this internal implementation
1401 # matching B. We want to hide this internal implementation
1401 # detail from the caller, and sorting the filtered revision
1402 # detail from the caller, and sorting the filtered revision
1402 # again achieves this.
1403 # again achieves this.
1403 for i, window in increasingwindows(0, len(revs), windowsize=1):
1404 for i, window in increasingwindows(0, len(revs), windowsize=1):
1404 orevs = revs[i:i + window]
1405 orevs = revs[i:i + window]
1405 nrevs = set(matcher(repo, sorted(orevs)))
1406 nrevs = set(matcher(repo, sorted(orevs)))
1406 for rev in orevs:
1407 for rev in orevs:
1407 if rev in nrevs:
1408 if rev in nrevs:
1408 yield rev
1409 yield rev
1409
1410
1410 if not len(repo):
1411 if not len(repo):
1411 return iter([]), None, None
1412 return iter([]), None, None
1412 # Default --rev value depends on --follow but --follow behaviour
1413 # Default --rev value depends on --follow but --follow behaviour
1413 # depends on revisions resolved from --rev...
1414 # depends on revisions resolved from --rev...
1414 follow = opts.get('follow') or opts.get('follow_first')
1415 follow = opts.get('follow') or opts.get('follow_first')
1415 if opts.get('rev'):
1416 if opts.get('rev'):
1416 revs = scmutil.revrange(repo, opts['rev'])
1417 revs = scmutil.revrange(repo, opts['rev'])
1417 else:
1418 else:
1418 if follow and len(repo) > 0:
1419 if follow and len(repo) > 0:
1419 revs = repo.revs('reverse(:.)')
1420 revs = repo.revs('reverse(:.)')
1420 else:
1421 else:
1421 revs = list(repo.changelog)
1422 revs = list(repo.changelog)
1422 revs.reverse()
1423 revs.reverse()
1423 if not revs:
1424 if not revs:
1424 return iter([]), None, None
1425 return iter([]), None, None
1425 expr, filematcher = _makegraphlogrevset(repo, pats, opts, revs)
1426 expr, filematcher = _makegraphlogrevset(repo, pats, opts, revs)
1426 if expr:
1427 if expr:
1427 matcher = revset.match(repo.ui, expr)
1428 matcher = revset.match(repo.ui, expr)
1428 revs = increasingrevs(repo, revs, matcher)
1429 revs = increasingrevs(repo, revs, matcher)
1429 if not opts.get('hidden'):
1430 if not opts.get('hidden'):
1430 # --hidden is still experimental and not worth a dedicated revset
1431 # --hidden is still experimental and not worth a dedicated revset
1431 # yet. Fortunately, filtering revision number is fast.
1432 # yet. Fortunately, filtering revision number is fast.
1432 revs = (r for r in revs if r not in repo.hiddenrevs)
1433 revs = (r for r in revs if r not in repo.hiddenrevs)
1433 else:
1434 else:
1434 revs = iter(revs)
1435 revs = iter(revs)
1435 return revs, expr, filematcher
1436 return revs, expr, filematcher
1436
1437
1437 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
1438 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
1438 filematcher=None):
1439 filematcher=None):
1439 seen, state = [], graphmod.asciistate()
1440 seen, state = [], graphmod.asciistate()
1440 for rev, type, ctx, parents in dag:
1441 for rev, type, ctx, parents in dag:
1441 char = 'o'
1442 char = 'o'
1442 if ctx.node() in showparents:
1443 if ctx.node() in showparents:
1443 char = '@'
1444 char = '@'
1444 elif ctx.obsolete():
1445 elif ctx.obsolete():
1445 char = 'x'
1446 char = 'x'
1446 copies = None
1447 copies = None
1447 if getrenamed and ctx.rev():
1448 if getrenamed and ctx.rev():
1448 copies = []
1449 copies = []
1449 for fn in ctx.files():
1450 for fn in ctx.files():
1450 rename = getrenamed(fn, ctx.rev())
1451 rename = getrenamed(fn, ctx.rev())
1451 if rename:
1452 if rename:
1452 copies.append((fn, rename[0]))
1453 copies.append((fn, rename[0]))
1453 revmatchfn = None
1454 revmatchfn = None
1454 if filematcher is not None:
1455 if filematcher is not None:
1455 revmatchfn = filematcher(ctx.rev())
1456 revmatchfn = filematcher(ctx.rev())
1456 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
1457 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
1457 lines = displayer.hunk.pop(rev).split('\n')
1458 lines = displayer.hunk.pop(rev).split('\n')
1458 if not lines[-1]:
1459 if not lines[-1]:
1459 del lines[-1]
1460 del lines[-1]
1460 displayer.flush(rev)
1461 displayer.flush(rev)
1461 edges = edgefn(type, char, lines, seen, rev, parents)
1462 edges = edgefn(type, char, lines, seen, rev, parents)
1462 for type, char, lines, coldata in edges:
1463 for type, char, lines, coldata in edges:
1463 graphmod.ascii(ui, state, type, char, lines, coldata)
1464 graphmod.ascii(ui, state, type, char, lines, coldata)
1464 displayer.close()
1465 displayer.close()
1465
1466
1466 def graphlog(ui, repo, *pats, **opts):
1467 def graphlog(ui, repo, *pats, **opts):
1467 # Parameters are identical to log command ones
1468 # Parameters are identical to log command ones
1468 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
1469 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
1469 revs = sorted(revs, reverse=1)
1470 revs = sorted(revs, reverse=1)
1470 limit = loglimit(opts)
1471 limit = loglimit(opts)
1471 if limit is not None:
1472 if limit is not None:
1472 revs = revs[:limit]
1473 revs = revs[:limit]
1473 revdag = graphmod.dagwalker(repo, revs)
1474 revdag = graphmod.dagwalker(repo, revs)
1474
1475
1475 getrenamed = None
1476 getrenamed = None
1476 if opts.get('copies'):
1477 if opts.get('copies'):
1477 endrev = None
1478 endrev = None
1478 if opts.get('rev'):
1479 if opts.get('rev'):
1479 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
1480 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
1480 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
1481 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
1481 displayer = show_changeset(ui, repo, opts, buffered=True)
1482 displayer = show_changeset(ui, repo, opts, buffered=True)
1482 showparents = [ctx.node() for ctx in repo[None].parents()]
1483 showparents = [ctx.node() for ctx in repo[None].parents()]
1483 displaygraph(ui, revdag, displayer, showparents,
1484 displaygraph(ui, revdag, displayer, showparents,
1484 graphmod.asciiedges, getrenamed, filematcher)
1485 graphmod.asciiedges, getrenamed, filematcher)
1485
1486
1486 def checkunsupportedgraphflags(pats, opts):
1487 def checkunsupportedgraphflags(pats, opts):
1487 for op in ["newest_first"]:
1488 for op in ["newest_first"]:
1488 if op in opts and opts[op]:
1489 if op in opts and opts[op]:
1489 raise util.Abort(_("-G/--graph option is incompatible with --%s")
1490 raise util.Abort(_("-G/--graph option is incompatible with --%s")
1490 % op.replace("_", "-"))
1491 % op.replace("_", "-"))
1491
1492
1492 def graphrevs(repo, nodes, opts):
1493 def graphrevs(repo, nodes, opts):
1493 limit = loglimit(opts)
1494 limit = loglimit(opts)
1494 nodes.reverse()
1495 nodes.reverse()
1495 if limit is not None:
1496 if limit is not None:
1496 nodes = nodes[:limit]
1497 nodes = nodes[:limit]
1497 return graphmod.nodes(repo, nodes)
1498 return graphmod.nodes(repo, nodes)
1498
1499
1499 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1500 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1500 join = lambda f: os.path.join(prefix, f)
1501 join = lambda f: os.path.join(prefix, f)
1501 bad = []
1502 bad = []
1502 oldbad = match.bad
1503 oldbad = match.bad
1503 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1504 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1504 names = []
1505 names = []
1505 wctx = repo[None]
1506 wctx = repo[None]
1506 cca = None
1507 cca = None
1507 abort, warn = scmutil.checkportabilityalert(ui)
1508 abort, warn = scmutil.checkportabilityalert(ui)
1508 if abort or warn:
1509 if abort or warn:
1509 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
1510 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
1510 for f in repo.walk(match):
1511 for f in repo.walk(match):
1511 exact = match.exact(f)
1512 exact = match.exact(f)
1512 if exact or not explicitonly and f not in repo.dirstate:
1513 if exact or not explicitonly and f not in repo.dirstate:
1513 if cca:
1514 if cca:
1514 cca(f)
1515 cca(f)
1515 names.append(f)
1516 names.append(f)
1516 if ui.verbose or not exact:
1517 if ui.verbose or not exact:
1517 ui.status(_('adding %s\n') % match.rel(join(f)))
1518 ui.status(_('adding %s\n') % match.rel(join(f)))
1518
1519
1519 for subpath in wctx.substate:
1520 for subpath in wctx.substate:
1520 sub = wctx.sub(subpath)
1521 sub = wctx.sub(subpath)
1521 try:
1522 try:
1522 submatch = matchmod.narrowmatcher(subpath, match)
1523 submatch = matchmod.narrowmatcher(subpath, match)
1523 if listsubrepos:
1524 if listsubrepos:
1524 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1525 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1525 False))
1526 False))
1526 else:
1527 else:
1527 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1528 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1528 True))
1529 True))
1529 except error.LookupError:
1530 except error.LookupError:
1530 ui.status(_("skipping missing subrepository: %s\n")
1531 ui.status(_("skipping missing subrepository: %s\n")
1531 % join(subpath))
1532 % join(subpath))
1532
1533
1533 if not dryrun:
1534 if not dryrun:
1534 rejected = wctx.add(names, prefix)
1535 rejected = wctx.add(names, prefix)
1535 bad.extend(f for f in rejected if f in match.files())
1536 bad.extend(f for f in rejected if f in match.files())
1536 return bad
1537 return bad
1537
1538
1538 def forget(ui, repo, match, prefix, explicitonly):
1539 def forget(ui, repo, match, prefix, explicitonly):
1539 join = lambda f: os.path.join(prefix, f)
1540 join = lambda f: os.path.join(prefix, f)
1540 bad = []
1541 bad = []
1541 oldbad = match.bad
1542 oldbad = match.bad
1542 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1543 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1543 wctx = repo[None]
1544 wctx = repo[None]
1544 forgot = []
1545 forgot = []
1545 s = repo.status(match=match, clean=True)
1546 s = repo.status(match=match, clean=True)
1546 forget = sorted(s[0] + s[1] + s[3] + s[6])
1547 forget = sorted(s[0] + s[1] + s[3] + s[6])
1547 if explicitonly:
1548 if explicitonly:
1548 forget = [f for f in forget if match.exact(f)]
1549 forget = [f for f in forget if match.exact(f)]
1549
1550
1550 for subpath in wctx.substate:
1551 for subpath in wctx.substate:
1551 sub = wctx.sub(subpath)
1552 sub = wctx.sub(subpath)
1552 try:
1553 try:
1553 submatch = matchmod.narrowmatcher(subpath, match)
1554 submatch = matchmod.narrowmatcher(subpath, match)
1554 subbad, subforgot = sub.forget(ui, submatch, prefix)
1555 subbad, subforgot = sub.forget(ui, submatch, prefix)
1555 bad.extend([subpath + '/' + f for f in subbad])
1556 bad.extend([subpath + '/' + f for f in subbad])
1556 forgot.extend([subpath + '/' + f for f in subforgot])
1557 forgot.extend([subpath + '/' + f for f in subforgot])
1557 except error.LookupError:
1558 except error.LookupError:
1558 ui.status(_("skipping missing subrepository: %s\n")
1559 ui.status(_("skipping missing subrepository: %s\n")
1559 % join(subpath))
1560 % join(subpath))
1560
1561
1561 if not explicitonly:
1562 if not explicitonly:
1562 for f in match.files():
1563 for f in match.files():
1563 if f not in repo.dirstate and not os.path.isdir(match.rel(join(f))):
1564 if f not in repo.dirstate and not os.path.isdir(match.rel(join(f))):
1564 if f not in forgot:
1565 if f not in forgot:
1565 if os.path.exists(match.rel(join(f))):
1566 if os.path.exists(match.rel(join(f))):
1566 ui.warn(_('not removing %s: '
1567 ui.warn(_('not removing %s: '
1567 'file is already untracked\n')
1568 'file is already untracked\n')
1568 % match.rel(join(f)))
1569 % match.rel(join(f)))
1569 bad.append(f)
1570 bad.append(f)
1570
1571
1571 for f in forget:
1572 for f in forget:
1572 if ui.verbose or not match.exact(f):
1573 if ui.verbose or not match.exact(f):
1573 ui.status(_('removing %s\n') % match.rel(join(f)))
1574 ui.status(_('removing %s\n') % match.rel(join(f)))
1574
1575
1575 rejected = wctx.forget(forget, prefix)
1576 rejected = wctx.forget(forget, prefix)
1576 bad.extend(f for f in rejected if f in match.files())
1577 bad.extend(f for f in rejected if f in match.files())
1577 forgot.extend(forget)
1578 forgot.extend(forget)
1578 return bad, forgot
1579 return bad, forgot
1579
1580
1580 def duplicatecopies(repo, rev, p1):
1581 def duplicatecopies(repo, rev, p1):
1581 "Reproduce copies found in the source revision in the dirstate for grafts"
1582 "Reproduce copies found in the source revision in the dirstate for grafts"
1582 for dst, src in copies.pathcopies(repo[p1], repo[rev]).iteritems():
1583 for dst, src in copies.pathcopies(repo[p1], repo[rev]).iteritems():
1583 repo.dirstate.copy(src, dst)
1584 repo.dirstate.copy(src, dst)
1584
1585
1585 def commit(ui, repo, commitfunc, pats, opts):
1586 def commit(ui, repo, commitfunc, pats, opts):
1586 '''commit the specified files or all outstanding changes'''
1587 '''commit the specified files or all outstanding changes'''
1587 date = opts.get('date')
1588 date = opts.get('date')
1588 if date:
1589 if date:
1589 opts['date'] = util.parsedate(date)
1590 opts['date'] = util.parsedate(date)
1590 message = logmessage(ui, opts)
1591 message = logmessage(ui, opts)
1591
1592
1592 # extract addremove carefully -- this function can be called from a command
1593 # extract addremove carefully -- this function can be called from a command
1593 # that doesn't support addremove
1594 # that doesn't support addremove
1594 if opts.get('addremove'):
1595 if opts.get('addremove'):
1595 scmutil.addremove(repo, pats, opts)
1596 scmutil.addremove(repo, pats, opts)
1596
1597
1597 return commitfunc(ui, repo, message,
1598 return commitfunc(ui, repo, message,
1598 scmutil.match(repo[None], pats, opts), opts)
1599 scmutil.match(repo[None], pats, opts), opts)
1599
1600
1600 def amend(ui, repo, commitfunc, old, extra, pats, opts):
1601 def amend(ui, repo, commitfunc, old, extra, pats, opts):
1601 ui.note(_('amending changeset %s\n') % old)
1602 ui.note(_('amending changeset %s\n') % old)
1602 base = old.p1()
1603 base = old.p1()
1603
1604
1604 wlock = lock = None
1605 wlock = lock = None
1605 try:
1606 try:
1606 wlock = repo.wlock()
1607 wlock = repo.wlock()
1607 lock = repo.lock()
1608 lock = repo.lock()
1608 tr = repo.transaction('amend')
1609 tr = repo.transaction('amend')
1609 try:
1610 try:
1610 # See if we got a message from -m or -l, if not, open the editor
1611 # See if we got a message from -m or -l, if not, open the editor
1611 # with the message of the changeset to amend
1612 # with the message of the changeset to amend
1612 message = logmessage(ui, opts)
1613 message = logmessage(ui, opts)
1613 # First, do a regular commit to record all changes in the working
1614 # First, do a regular commit to record all changes in the working
1614 # directory (if there are any)
1615 # directory (if there are any)
1615 ui.callhooks = False
1616 ui.callhooks = False
1616 try:
1617 try:
1617 opts['message'] = 'temporary amend commit for %s' % old
1618 opts['message'] = 'temporary amend commit for %s' % old
1618 node = commit(ui, repo, commitfunc, pats, opts)
1619 node = commit(ui, repo, commitfunc, pats, opts)
1619 finally:
1620 finally:
1620 ui.callhooks = True
1621 ui.callhooks = True
1621 ctx = repo[node]
1622 ctx = repo[node]
1622
1623
1623 # Participating changesets:
1624 # Participating changesets:
1624 #
1625 #
1625 # node/ctx o - new (intermediate) commit that contains changes
1626 # node/ctx o - new (intermediate) commit that contains changes
1626 # | from working dir to go into amending commit
1627 # | from working dir to go into amending commit
1627 # | (or a workingctx if there were no changes)
1628 # | (or a workingctx if there were no changes)
1628 # |
1629 # |
1629 # old o - changeset to amend
1630 # old o - changeset to amend
1630 # |
1631 # |
1631 # base o - parent of amending changeset
1632 # base o - parent of amending changeset
1632
1633
1633 # Update extra dict from amended commit (e.g. to preserve graft
1634 # Update extra dict from amended commit (e.g. to preserve graft
1634 # source)
1635 # source)
1635 extra.update(old.extra())
1636 extra.update(old.extra())
1636
1637
1637 # Also update it from the intermediate commit or from the wctx
1638 # Also update it from the intermediate commit or from the wctx
1638 extra.update(ctx.extra())
1639 extra.update(ctx.extra())
1639
1640
1640 files = set(old.files())
1641 files = set(old.files())
1641
1642
1642 # Second, we use either the commit we just did, or if there were no
1643 # Second, we use either the commit we just did, or if there were no
1643 # changes the parent of the working directory as the version of the
1644 # changes the parent of the working directory as the version of the
1644 # files in the final amend commit
1645 # files in the final amend commit
1645 if node:
1646 if node:
1646 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
1647 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
1647
1648
1648 user = ctx.user()
1649 user = ctx.user()
1649 date = ctx.date()
1650 date = ctx.date()
1650 # Recompute copies (avoid recording a -> b -> a)
1651 # Recompute copies (avoid recording a -> b -> a)
1651 copied = copies.pathcopies(base, ctx)
1652 copied = copies.pathcopies(base, ctx)
1652
1653
1653 # Prune files which were reverted by the updates: if old
1654 # Prune files which were reverted by the updates: if old
1654 # introduced file X and our intermediate commit, node,
1655 # introduced file X and our intermediate commit, node,
1655 # renamed that file, then those two files are the same and
1656 # renamed that file, then those two files are the same and
1656 # we can discard X from our list of files. Likewise if X
1657 # we can discard X from our list of files. Likewise if X
1657 # was deleted, it's no longer relevant
1658 # was deleted, it's no longer relevant
1658 files.update(ctx.files())
1659 files.update(ctx.files())
1659
1660
1660 def samefile(f):
1661 def samefile(f):
1661 if f in ctx.manifest():
1662 if f in ctx.manifest():
1662 a = ctx.filectx(f)
1663 a = ctx.filectx(f)
1663 if f in base.manifest():
1664 if f in base.manifest():
1664 b = base.filectx(f)
1665 b = base.filectx(f)
1665 return (not a.cmp(b)
1666 return (not a.cmp(b)
1666 and a.flags() == b.flags())
1667 and a.flags() == b.flags())
1667 else:
1668 else:
1668 return False
1669 return False
1669 else:
1670 else:
1670 return f not in base.manifest()
1671 return f not in base.manifest()
1671 files = [f for f in files if not samefile(f)]
1672 files = [f for f in files if not samefile(f)]
1672
1673
1673 def filectxfn(repo, ctx_, path):
1674 def filectxfn(repo, ctx_, path):
1674 try:
1675 try:
1675 fctx = ctx[path]
1676 fctx = ctx[path]
1676 flags = fctx.flags()
1677 flags = fctx.flags()
1677 mctx = context.memfilectx(fctx.path(), fctx.data(),
1678 mctx = context.memfilectx(fctx.path(), fctx.data(),
1678 islink='l' in flags,
1679 islink='l' in flags,
1679 isexec='x' in flags,
1680 isexec='x' in flags,
1680 copied=copied.get(path))
1681 copied=copied.get(path))
1681 return mctx
1682 return mctx
1682 except KeyError:
1683 except KeyError:
1683 raise IOError
1684 raise IOError
1684 else:
1685 else:
1685 ui.note(_('copying changeset %s to %s\n') % (old, base))
1686 ui.note(_('copying changeset %s to %s\n') % (old, base))
1686
1687
1687 # Use version of files as in the old cset
1688 # Use version of files as in the old cset
1688 def filectxfn(repo, ctx_, path):
1689 def filectxfn(repo, ctx_, path):
1689 try:
1690 try:
1690 return old.filectx(path)
1691 return old.filectx(path)
1691 except KeyError:
1692 except KeyError:
1692 raise IOError
1693 raise IOError
1693
1694
1694 user = opts.get('user') or old.user()
1695 user = opts.get('user') or old.user()
1695 date = opts.get('date') or old.date()
1696 date = opts.get('date') or old.date()
1696 if not message:
1697 if not message:
1697 message = old.description()
1698 message = old.description()
1698
1699
1700 pureextra = extra.copy()
1701 extra['amend_source'] = old.hex()
1702
1699 new = context.memctx(repo,
1703 new = context.memctx(repo,
1700 parents=[base.node(), nullid],
1704 parents=[base.node(), nullid],
1701 text=message,
1705 text=message,
1702 files=files,
1706 files=files,
1703 filectxfn=filectxfn,
1707 filectxfn=filectxfn,
1704 user=user,
1708 user=user,
1705 date=date,
1709 date=date,
1706 extra=extra)
1710 extra=extra)
1707 new._text = commitforceeditor(repo, new, [])
1711 new._text = commitforceeditor(repo, new, [])
1712
1713 newdesc = changelog.stripdesc(new.description())
1714 if ((not node)
1715 and newdesc == old.description()
1716 and user == old.user()
1717 and date == old.date()
1718 and pureextra == old.extra()):
1719 # nothing changed. continuing here would create a new node
1720 # anyway because of the amend_source noise.
1721 #
1722 # This not what we expect from amend.
1723 return old.node()
1724
1708 ph = repo.ui.config('phases', 'new-commit', phases.draft)
1725 ph = repo.ui.config('phases', 'new-commit', phases.draft)
1709 try:
1726 try:
1710 repo.ui.setconfig('phases', 'new-commit', old.phase())
1727 repo.ui.setconfig('phases', 'new-commit', old.phase())
1711 newid = repo.commitctx(new)
1728 newid = repo.commitctx(new)
1712 finally:
1729 finally:
1713 repo.ui.setconfig('phases', 'new-commit', ph)
1730 repo.ui.setconfig('phases', 'new-commit', ph)
1714 if newid != old.node():
1731 if newid != old.node():
1715 # Reroute the working copy parent to the new changeset
1732 # Reroute the working copy parent to the new changeset
1716 repo.setparents(newid, nullid)
1733 repo.setparents(newid, nullid)
1717
1734
1718 # Move bookmarks from old parent to amend commit
1735 # Move bookmarks from old parent to amend commit
1719 bms = repo.nodebookmarks(old.node())
1736 bms = repo.nodebookmarks(old.node())
1720 if bms:
1737 if bms:
1721 for bm in bms:
1738 for bm in bms:
1722 repo._bookmarks[bm] = newid
1739 repo._bookmarks[bm] = newid
1723 bookmarks.write(repo)
1740 bookmarks.write(repo)
1724 #commit the whole amend process
1741 #commit the whole amend process
1725 if obsolete._enabled and newid != old.node():
1742 if obsolete._enabled and newid != old.node():
1726 # mark the new changeset as successor of the rewritten one
1743 # mark the new changeset as successor of the rewritten one
1727 new = repo[newid]
1744 new = repo[newid]
1728 obs = [(old, (new,))]
1745 obs = [(old, (new,))]
1729 if node:
1746 if node:
1730 obs.append((ctx, (new,)))
1747 obs.append((ctx, (new,)))
1731
1748
1732 obsolete.createmarkers(repo, obs)
1749 obsolete.createmarkers(repo, obs)
1733 tr.close()
1750 tr.close()
1734 finally:
1751 finally:
1735 tr.release()
1752 tr.release()
1736 if (not obsolete._enabled) and newid != old.node():
1753 if (not obsolete._enabled) and newid != old.node():
1737 # Strip the intermediate commit (if there was one) and the amended
1754 # Strip the intermediate commit (if there was one) and the amended
1738 # commit
1755 # commit
1739 if node:
1756 if node:
1740 ui.note(_('stripping intermediate changeset %s\n') % ctx)
1757 ui.note(_('stripping intermediate changeset %s\n') % ctx)
1741 ui.note(_('stripping amended changeset %s\n') % old)
1758 ui.note(_('stripping amended changeset %s\n') % old)
1742 repair.strip(ui, repo, old.node(), topic='amend-backup')
1759 repair.strip(ui, repo, old.node(), topic='amend-backup')
1743 finally:
1760 finally:
1744 lockmod.release(wlock, lock)
1761 lockmod.release(wlock, lock)
1745 return newid
1762 return newid
1746
1763
1747 def commiteditor(repo, ctx, subs):
1764 def commiteditor(repo, ctx, subs):
1748 if ctx.description():
1765 if ctx.description():
1749 return ctx.description()
1766 return ctx.description()
1750 return commitforceeditor(repo, ctx, subs)
1767 return commitforceeditor(repo, ctx, subs)
1751
1768
1752 def commitforceeditor(repo, ctx, subs):
1769 def commitforceeditor(repo, ctx, subs):
1753 edittext = []
1770 edittext = []
1754 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1771 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1755 if ctx.description():
1772 if ctx.description():
1756 edittext.append(ctx.description())
1773 edittext.append(ctx.description())
1757 edittext.append("")
1774 edittext.append("")
1758 edittext.append("") # Empty line between message and comments.
1775 edittext.append("") # Empty line between message and comments.
1759 edittext.append(_("HG: Enter commit message."
1776 edittext.append(_("HG: Enter commit message."
1760 " Lines beginning with 'HG:' are removed."))
1777 " Lines beginning with 'HG:' are removed."))
1761 edittext.append(_("HG: Leave message empty to abort commit."))
1778 edittext.append(_("HG: Leave message empty to abort commit."))
1762 edittext.append("HG: --")
1779 edittext.append("HG: --")
1763 edittext.append(_("HG: user: %s") % ctx.user())
1780 edittext.append(_("HG: user: %s") % ctx.user())
1764 if ctx.p2():
1781 if ctx.p2():
1765 edittext.append(_("HG: branch merge"))
1782 edittext.append(_("HG: branch merge"))
1766 if ctx.branch():
1783 if ctx.branch():
1767 edittext.append(_("HG: branch '%s'") % ctx.branch())
1784 edittext.append(_("HG: branch '%s'") % ctx.branch())
1768 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1785 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1769 edittext.extend([_("HG: added %s") % f for f in added])
1786 edittext.extend([_("HG: added %s") % f for f in added])
1770 edittext.extend([_("HG: changed %s") % f for f in modified])
1787 edittext.extend([_("HG: changed %s") % f for f in modified])
1771 edittext.extend([_("HG: removed %s") % f for f in removed])
1788 edittext.extend([_("HG: removed %s") % f for f in removed])
1772 if not added and not modified and not removed:
1789 if not added and not modified and not removed:
1773 edittext.append(_("HG: no files changed"))
1790 edittext.append(_("HG: no files changed"))
1774 edittext.append("")
1791 edittext.append("")
1775 # run editor in the repository root
1792 # run editor in the repository root
1776 olddir = os.getcwd()
1793 olddir = os.getcwd()
1777 os.chdir(repo.root)
1794 os.chdir(repo.root)
1778 text = repo.ui.edit("\n".join(edittext), ctx.user())
1795 text = repo.ui.edit("\n".join(edittext), ctx.user())
1779 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
1796 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
1780 os.chdir(olddir)
1797 os.chdir(olddir)
1781
1798
1782 if not text.strip():
1799 if not text.strip():
1783 raise util.Abort(_("empty commit message"))
1800 raise util.Abort(_("empty commit message"))
1784
1801
1785 return text
1802 return text
1786
1803
1787 def revert(ui, repo, ctx, parents, *pats, **opts):
1804 def revert(ui, repo, ctx, parents, *pats, **opts):
1788 parent, p2 = parents
1805 parent, p2 = parents
1789 node = ctx.node()
1806 node = ctx.node()
1790
1807
1791 mf = ctx.manifest()
1808 mf = ctx.manifest()
1792 if node == parent:
1809 if node == parent:
1793 pmf = mf
1810 pmf = mf
1794 else:
1811 else:
1795 pmf = None
1812 pmf = None
1796
1813
1797 # need all matching names in dirstate and manifest of target rev,
1814 # need all matching names in dirstate and manifest of target rev,
1798 # so have to walk both. do not print errors if files exist in one
1815 # so have to walk both. do not print errors if files exist in one
1799 # but not other.
1816 # but not other.
1800
1817
1801 names = {}
1818 names = {}
1802
1819
1803 wlock = repo.wlock()
1820 wlock = repo.wlock()
1804 try:
1821 try:
1805 # walk dirstate.
1822 # walk dirstate.
1806
1823
1807 m = scmutil.match(repo[None], pats, opts)
1824 m = scmutil.match(repo[None], pats, opts)
1808 m.bad = lambda x, y: False
1825 m.bad = lambda x, y: False
1809 for abs in repo.walk(m):
1826 for abs in repo.walk(m):
1810 names[abs] = m.rel(abs), m.exact(abs)
1827 names[abs] = m.rel(abs), m.exact(abs)
1811
1828
1812 # walk target manifest.
1829 # walk target manifest.
1813
1830
1814 def badfn(path, msg):
1831 def badfn(path, msg):
1815 if path in names:
1832 if path in names:
1816 return
1833 return
1817 if path in ctx.substate:
1834 if path in ctx.substate:
1818 return
1835 return
1819 path_ = path + '/'
1836 path_ = path + '/'
1820 for f in names:
1837 for f in names:
1821 if f.startswith(path_):
1838 if f.startswith(path_):
1822 return
1839 return
1823 ui.warn("%s: %s\n" % (m.rel(path), msg))
1840 ui.warn("%s: %s\n" % (m.rel(path), msg))
1824
1841
1825 m = scmutil.match(ctx, pats, opts)
1842 m = scmutil.match(ctx, pats, opts)
1826 m.bad = badfn
1843 m.bad = badfn
1827 for abs in ctx.walk(m):
1844 for abs in ctx.walk(m):
1828 if abs not in names:
1845 if abs not in names:
1829 names[abs] = m.rel(abs), m.exact(abs)
1846 names[abs] = m.rel(abs), m.exact(abs)
1830
1847
1831 # get the list of subrepos that must be reverted
1848 # get the list of subrepos that must be reverted
1832 targetsubs = [s for s in ctx.substate if m(s)]
1849 targetsubs = [s for s in ctx.substate if m(s)]
1833 m = scmutil.matchfiles(repo, names)
1850 m = scmutil.matchfiles(repo, names)
1834 changes = repo.status(match=m)[:4]
1851 changes = repo.status(match=m)[:4]
1835 modified, added, removed, deleted = map(set, changes)
1852 modified, added, removed, deleted = map(set, changes)
1836
1853
1837 # if f is a rename, also revert the source
1854 # if f is a rename, also revert the source
1838 cwd = repo.getcwd()
1855 cwd = repo.getcwd()
1839 for f in added:
1856 for f in added:
1840 src = repo.dirstate.copied(f)
1857 src = repo.dirstate.copied(f)
1841 if src and src not in names and repo.dirstate[src] == 'r':
1858 if src and src not in names and repo.dirstate[src] == 'r':
1842 removed.add(src)
1859 removed.add(src)
1843 names[src] = (repo.pathto(src, cwd), True)
1860 names[src] = (repo.pathto(src, cwd), True)
1844
1861
1845 def removeforget(abs):
1862 def removeforget(abs):
1846 if repo.dirstate[abs] == 'a':
1863 if repo.dirstate[abs] == 'a':
1847 return _('forgetting %s\n')
1864 return _('forgetting %s\n')
1848 return _('removing %s\n')
1865 return _('removing %s\n')
1849
1866
1850 revert = ([], _('reverting %s\n'))
1867 revert = ([], _('reverting %s\n'))
1851 add = ([], _('adding %s\n'))
1868 add = ([], _('adding %s\n'))
1852 remove = ([], removeforget)
1869 remove = ([], removeforget)
1853 undelete = ([], _('undeleting %s\n'))
1870 undelete = ([], _('undeleting %s\n'))
1854
1871
1855 disptable = (
1872 disptable = (
1856 # dispatch table:
1873 # dispatch table:
1857 # file state
1874 # file state
1858 # action if in target manifest
1875 # action if in target manifest
1859 # action if not in target manifest
1876 # action if not in target manifest
1860 # make backup if in target manifest
1877 # make backup if in target manifest
1861 # make backup if not in target manifest
1878 # make backup if not in target manifest
1862 (modified, revert, remove, True, True),
1879 (modified, revert, remove, True, True),
1863 (added, revert, remove, True, False),
1880 (added, revert, remove, True, False),
1864 (removed, undelete, None, False, False),
1881 (removed, undelete, None, False, False),
1865 (deleted, revert, remove, False, False),
1882 (deleted, revert, remove, False, False),
1866 )
1883 )
1867
1884
1868 for abs, (rel, exact) in sorted(names.items()):
1885 for abs, (rel, exact) in sorted(names.items()):
1869 mfentry = mf.get(abs)
1886 mfentry = mf.get(abs)
1870 target = repo.wjoin(abs)
1887 target = repo.wjoin(abs)
1871 def handle(xlist, dobackup):
1888 def handle(xlist, dobackup):
1872 xlist[0].append(abs)
1889 xlist[0].append(abs)
1873 if (dobackup and not opts.get('no_backup') and
1890 if (dobackup and not opts.get('no_backup') and
1874 os.path.lexists(target)):
1891 os.path.lexists(target)):
1875 bakname = "%s.orig" % rel
1892 bakname = "%s.orig" % rel
1876 ui.note(_('saving current version of %s as %s\n') %
1893 ui.note(_('saving current version of %s as %s\n') %
1877 (rel, bakname))
1894 (rel, bakname))
1878 if not opts.get('dry_run'):
1895 if not opts.get('dry_run'):
1879 util.rename(target, bakname)
1896 util.rename(target, bakname)
1880 if ui.verbose or not exact:
1897 if ui.verbose or not exact:
1881 msg = xlist[1]
1898 msg = xlist[1]
1882 if not isinstance(msg, basestring):
1899 if not isinstance(msg, basestring):
1883 msg = msg(abs)
1900 msg = msg(abs)
1884 ui.status(msg % rel)
1901 ui.status(msg % rel)
1885 for table, hitlist, misslist, backuphit, backupmiss in disptable:
1902 for table, hitlist, misslist, backuphit, backupmiss in disptable:
1886 if abs not in table:
1903 if abs not in table:
1887 continue
1904 continue
1888 # file has changed in dirstate
1905 # file has changed in dirstate
1889 if mfentry:
1906 if mfentry:
1890 handle(hitlist, backuphit)
1907 handle(hitlist, backuphit)
1891 elif misslist is not None:
1908 elif misslist is not None:
1892 handle(misslist, backupmiss)
1909 handle(misslist, backupmiss)
1893 break
1910 break
1894 else:
1911 else:
1895 if abs not in repo.dirstate:
1912 if abs not in repo.dirstate:
1896 if mfentry:
1913 if mfentry:
1897 handle(add, True)
1914 handle(add, True)
1898 elif exact:
1915 elif exact:
1899 ui.warn(_('file not managed: %s\n') % rel)
1916 ui.warn(_('file not managed: %s\n') % rel)
1900 continue
1917 continue
1901 # file has not changed in dirstate
1918 # file has not changed in dirstate
1902 if node == parent:
1919 if node == parent:
1903 if exact:
1920 if exact:
1904 ui.warn(_('no changes needed to %s\n') % rel)
1921 ui.warn(_('no changes needed to %s\n') % rel)
1905 continue
1922 continue
1906 if pmf is None:
1923 if pmf is None:
1907 # only need parent manifest in this unlikely case,
1924 # only need parent manifest in this unlikely case,
1908 # so do not read by default
1925 # so do not read by default
1909 pmf = repo[parent].manifest()
1926 pmf = repo[parent].manifest()
1910 if abs in pmf and mfentry:
1927 if abs in pmf and mfentry:
1911 # if version of file is same in parent and target
1928 # if version of file is same in parent and target
1912 # manifests, do nothing
1929 # manifests, do nothing
1913 if (pmf[abs] != mfentry or
1930 if (pmf[abs] != mfentry or
1914 pmf.flags(abs) != mf.flags(abs)):
1931 pmf.flags(abs) != mf.flags(abs)):
1915 handle(revert, False)
1932 handle(revert, False)
1916 else:
1933 else:
1917 handle(remove, False)
1934 handle(remove, False)
1918
1935
1919 if not opts.get('dry_run'):
1936 if not opts.get('dry_run'):
1920 def checkout(f):
1937 def checkout(f):
1921 fc = ctx[f]
1938 fc = ctx[f]
1922 repo.wwrite(f, fc.data(), fc.flags())
1939 repo.wwrite(f, fc.data(), fc.flags())
1923
1940
1924 audit_path = scmutil.pathauditor(repo.root)
1941 audit_path = scmutil.pathauditor(repo.root)
1925 for f in remove[0]:
1942 for f in remove[0]:
1926 if repo.dirstate[f] == 'a':
1943 if repo.dirstate[f] == 'a':
1927 repo.dirstate.drop(f)
1944 repo.dirstate.drop(f)
1928 continue
1945 continue
1929 audit_path(f)
1946 audit_path(f)
1930 try:
1947 try:
1931 util.unlinkpath(repo.wjoin(f))
1948 util.unlinkpath(repo.wjoin(f))
1932 except OSError:
1949 except OSError:
1933 pass
1950 pass
1934 repo.dirstate.remove(f)
1951 repo.dirstate.remove(f)
1935
1952
1936 normal = None
1953 normal = None
1937 if node == parent:
1954 if node == parent:
1938 # We're reverting to our parent. If possible, we'd like status
1955 # We're reverting to our parent. If possible, we'd like status
1939 # to report the file as clean. We have to use normallookup for
1956 # to report the file as clean. We have to use normallookup for
1940 # merges to avoid losing information about merged/dirty files.
1957 # merges to avoid losing information about merged/dirty files.
1941 if p2 != nullid:
1958 if p2 != nullid:
1942 normal = repo.dirstate.normallookup
1959 normal = repo.dirstate.normallookup
1943 else:
1960 else:
1944 normal = repo.dirstate.normal
1961 normal = repo.dirstate.normal
1945 for f in revert[0]:
1962 for f in revert[0]:
1946 checkout(f)
1963 checkout(f)
1947 if normal:
1964 if normal:
1948 normal(f)
1965 normal(f)
1949
1966
1950 for f in add[0]:
1967 for f in add[0]:
1951 checkout(f)
1968 checkout(f)
1952 repo.dirstate.add(f)
1969 repo.dirstate.add(f)
1953
1970
1954 normal = repo.dirstate.normallookup
1971 normal = repo.dirstate.normallookup
1955 if node == parent and p2 == nullid:
1972 if node == parent and p2 == nullid:
1956 normal = repo.dirstate.normal
1973 normal = repo.dirstate.normal
1957 for f in undelete[0]:
1974 for f in undelete[0]:
1958 checkout(f)
1975 checkout(f)
1959 normal(f)
1976 normal(f)
1960
1977
1961 if targetsubs:
1978 if targetsubs:
1962 # Revert the subrepos on the revert list
1979 # Revert the subrepos on the revert list
1963 for sub in targetsubs:
1980 for sub in targetsubs:
1964 ctx.sub(sub).revert(ui, ctx.substate[sub], *pats, **opts)
1981 ctx.sub(sub).revert(ui, ctx.substate[sub], *pats, **opts)
1965 finally:
1982 finally:
1966 wlock.release()
1983 wlock.release()
1967
1984
1968 def command(table):
1985 def command(table):
1969 '''returns a function object bound to table which can be used as
1986 '''returns a function object bound to table which can be used as
1970 a decorator for populating table as a command table'''
1987 a decorator for populating table as a command table'''
1971
1988
1972 def cmd(name, options, synopsis=None):
1989 def cmd(name, options, synopsis=None):
1973 def decorator(func):
1990 def decorator(func):
1974 if synopsis:
1991 if synopsis:
1975 table[name] = func, options[:], synopsis
1992 table[name] = func, options[:], synopsis
1976 else:
1993 else:
1977 table[name] = func, options[:]
1994 table[name] = func, options[:]
1978 return func
1995 return func
1979 return decorator
1996 return decorator
1980
1997
1981 return cmd
1998 return cmd
@@ -1,444 +1,456 b''
1 $ hg init
1 $ hg init
2
2
3 Setup:
3 Setup:
4
4
5 $ echo a >> a
5 $ echo a >> a
6 $ hg ci -Am 'base'
6 $ hg ci -Am 'base'
7 adding a
7 adding a
8
8
9 Refuse to amend public csets:
9 Refuse to amend public csets:
10
10
11 $ hg phase -r . -p
11 $ hg phase -r . -p
12 $ hg ci --amend
12 $ hg ci --amend
13 abort: cannot amend public changesets
13 abort: cannot amend public changesets
14 [255]
14 [255]
15 $ hg phase -r . -f -d
15 $ hg phase -r . -f -d
16
16
17 $ echo a >> a
17 $ echo a >> a
18 $ hg ci -Am 'base1'
18 $ hg ci -Am 'base1'
19
19
20 Nothing to amend:
20 Nothing to amend:
21
21
22 $ hg ci --amend
22 $ hg ci --amend
23 nothing changed
23 nothing changed
24 [1]
24 [1]
25
25
26 $ cat >> $HGRCPATH <<EOF
26 $ cat >> $HGRCPATH <<EOF
27 > [hooks]
27 > [hooks]
28 > pretxncommit.foo = sh -c "echo \\"pretxncommit \$HG_NODE\\"; hg id -r \$HG_NODE"
28 > pretxncommit.foo = sh -c "echo \\"pretxncommit \$HG_NODE\\"; hg id -r \$HG_NODE"
29 > EOF
29 > EOF
30
30
31 Amending changeset with changes in working dir:
31 Amending changeset with changes in working dir:
32
32
33 $ echo a >> a
33 $ echo a >> a
34 $ hg ci --amend -m 'amend base1'
34 $ hg ci --amend -m 'amend base1'
35 pretxncommit 9cd25b479c51be2f4ed2c38e7abdf7ce67d8e0dc
35 pretxncommit 43f1ba15f28a50abf0aae529cf8a16bfced7b149
36 9cd25b479c51 tip
36 43f1ba15f28a tip
37 saved backup bundle to $TESTTMP/.hg/strip-backup/489edb5b847d-amend-backup.hg (glob)
37 saved backup bundle to $TESTTMP/.hg/strip-backup/489edb5b847d-amend-backup.hg (glob)
38 $ echo 'pretxncommit.foo = ' >> $HGRCPATH
38 $ echo 'pretxncommit.foo = ' >> $HGRCPATH
39 $ hg diff -c .
39 $ hg diff -c .
40 diff -r ad120869acf0 -r 9cd25b479c51 a
40 diff -r ad120869acf0 -r 43f1ba15f28a a
41 --- a/a Thu Jan 01 00:00:00 1970 +0000
41 --- a/a Thu Jan 01 00:00:00 1970 +0000
42 +++ b/a Thu Jan 01 00:00:00 1970 +0000
42 +++ b/a Thu Jan 01 00:00:00 1970 +0000
43 @@ -1,1 +1,3 @@
43 @@ -1,1 +1,3 @@
44 a
44 a
45 +a
45 +a
46 +a
46 +a
47 $ hg log
47 $ hg log
48 changeset: 1:9cd25b479c51
48 changeset: 1:43f1ba15f28a
49 tag: tip
49 tag: tip
50 user: test
50 user: test
51 date: Thu Jan 01 00:00:00 1970 +0000
51 date: Thu Jan 01 00:00:00 1970 +0000
52 summary: amend base1
52 summary: amend base1
53
53
54 changeset: 0:ad120869acf0
54 changeset: 0:ad120869acf0
55 user: test
55 user: test
56 date: Thu Jan 01 00:00:00 1970 +0000
56 date: Thu Jan 01 00:00:00 1970 +0000
57 summary: base
57 summary: base
58
58
59
59
60 Add new file:
60 Add new file:
61
61
62 $ echo b > b
62 $ echo b > b
63 $ hg ci --amend -Am 'amend base1 new file'
63 $ hg ci --amend -Am 'amend base1 new file'
64 adding b
64 adding b
65 saved backup bundle to $TESTTMP/.hg/strip-backup/9cd25b479c51-amend-backup.hg (glob)
65 saved backup bundle to $TESTTMP/.hg/strip-backup/43f1ba15f28a-amend-backup.hg (glob)
66
66
67 Remove file that was added in amended commit:
67 Remove file that was added in amended commit:
68
68
69 $ hg rm b
69 $ hg rm b
70 $ hg ci --amend -m 'amend base1 remove new file'
70 $ hg ci --amend -m 'amend base1 remove new file'
71 saved backup bundle to $TESTTMP/.hg/strip-backup/e2bb3ecffd2f-amend-backup.hg (glob)
71 saved backup bundle to $TESTTMP/.hg/strip-backup/b8e3cb2b3882-amend-backup.hg (glob)
72
72
73 $ hg cat b
73 $ hg cat b
74 b: no such file in rev 664a9b2d60cd
74 b: no such file in rev 74609c7f506e
75 [1]
75 [1]
76
76
77 No changes, just a different message:
77 No changes, just a different message:
78
78
79 $ hg ci -v --amend -m 'no changes, new message'
79 $ hg ci -v --amend -m 'no changes, new message'
80 amending changeset 664a9b2d60cd
80 amending changeset 74609c7f506e
81 copying changeset 664a9b2d60cd to ad120869acf0
81 copying changeset 74609c7f506e to ad120869acf0
82 a
82 a
83 stripping amended changeset 664a9b2d60cd
83 stripping amended changeset 74609c7f506e
84 1 changesets found
84 1 changesets found
85 saved backup bundle to $TESTTMP/.hg/strip-backup/664a9b2d60cd-amend-backup.hg (glob)
85 saved backup bundle to $TESTTMP/.hg/strip-backup/74609c7f506e-amend-backup.hg (glob)
86 1 changesets found
86 1 changesets found
87 adding branch
87 adding branch
88 adding changesets
88 adding changesets
89 adding manifests
89 adding manifests
90 adding file changes
90 adding file changes
91 added 1 changesets with 1 changes to 1 files
91 added 1 changesets with 1 changes to 1 files
92 committed changeset 1:ea6e356ff2ad
92 committed changeset 1:1cd866679df8
93 $ hg diff -c .
93 $ hg diff -c .
94 diff -r ad120869acf0 -r ea6e356ff2ad a
94 diff -r ad120869acf0 -r 1cd866679df8 a
95 --- a/a Thu Jan 01 00:00:00 1970 +0000
95 --- a/a Thu Jan 01 00:00:00 1970 +0000
96 +++ b/a Thu Jan 01 00:00:00 1970 +0000
96 +++ b/a Thu Jan 01 00:00:00 1970 +0000
97 @@ -1,1 +1,3 @@
97 @@ -1,1 +1,3 @@
98 a
98 a
99 +a
99 +a
100 +a
100 +a
101 $ hg log
101 $ hg log
102 changeset: 1:ea6e356ff2ad
102 changeset: 1:1cd866679df8
103 tag: tip
103 tag: tip
104 user: test
104 user: test
105 date: Thu Jan 01 00:00:00 1970 +0000
105 date: Thu Jan 01 00:00:00 1970 +0000
106 summary: no changes, new message
106 summary: no changes, new message
107
107
108 changeset: 0:ad120869acf0
108 changeset: 0:ad120869acf0
109 user: test
109 user: test
110 date: Thu Jan 01 00:00:00 1970 +0000
110 date: Thu Jan 01 00:00:00 1970 +0000
111 summary: base
111 summary: base
112
112
113
113
114 Disable default date on commit so when -d isn't given, the old date is preserved:
114 Disable default date on commit so when -d isn't given, the old date is preserved:
115
115
116 $ echo '[defaults]' >> $HGRCPATH
116 $ echo '[defaults]' >> $HGRCPATH
117 $ echo 'commit=' >> $HGRCPATH
117 $ echo 'commit=' >> $HGRCPATH
118
118
119 Test -u/-d:
119 Test -u/-d:
120
120
121 $ hg ci --amend -u foo -d '1 0'
121 $ hg ci --amend -u foo -d '1 0'
122 saved backup bundle to $TESTTMP/.hg/strip-backup/ea6e356ff2ad-amend-backup.hg (glob)
122 saved backup bundle to $TESTTMP/.hg/strip-backup/1cd866679df8-amend-backup.hg (glob)
123 $ echo a >> a
123 $ echo a >> a
124 $ hg ci --amend -u foo -d '1 0'
124 $ hg ci --amend -u foo -d '1 0'
125 saved backup bundle to $TESTTMP/.hg/strip-backup/377b91ce8b56-amend-backup.hg (glob)
125 saved backup bundle to $TESTTMP/.hg/strip-backup/780e6f23e03d-amend-backup.hg (glob)
126 $ hg log -r .
126 $ hg log -r .
127 changeset: 1:2c94e4a5756f
127 changeset: 1:5f357c7560ab
128 tag: tip
128 tag: tip
129 user: foo
129 user: foo
130 date: Thu Jan 01 00:00:01 1970 +0000
130 date: Thu Jan 01 00:00:01 1970 +0000
131 summary: no changes, new message
131 summary: no changes, new message
132
132
133
133
134 Open editor with old commit message if a message isn't given otherwise:
134 Open editor with old commit message if a message isn't given otherwise:
135
135
136 $ cat > editor.sh << '__EOF__'
136 $ cat > editor.sh << '__EOF__'
137 > #!/bin/sh
137 > #!/bin/sh
138 > cat $1
138 > cat $1
139 > echo "another precious commit message" > "$1"
139 > echo "another precious commit message" > "$1"
140 > __EOF__
140 > __EOF__
141 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v
141 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v
142 amending changeset 2c94e4a5756f
142 amending changeset 5f357c7560ab
143 copying changeset 2c94e4a5756f to ad120869acf0
143 copying changeset 5f357c7560ab to ad120869acf0
144 no changes, new message
144 no changes, new message
145
145
146
146
147 HG: Enter commit message. Lines beginning with 'HG:' are removed.
147 HG: Enter commit message. Lines beginning with 'HG:' are removed.
148 HG: Leave message empty to abort commit.
148 HG: Leave message empty to abort commit.
149 HG: --
149 HG: --
150 HG: user: foo
150 HG: user: foo
151 HG: branch 'default'
151 HG: branch 'default'
152 HG: changed a
152 HG: changed a
153 a
153 a
154 stripping amended changeset 2c94e4a5756f
154 stripping amended changeset 5f357c7560ab
155 1 changesets found
155 1 changesets found
156 saved backup bundle to $TESTTMP/.hg/strip-backup/2c94e4a5756f-amend-backup.hg (glob)
156 saved backup bundle to $TESTTMP/.hg/strip-backup/5f357c7560ab-amend-backup.hg (glob)
157 1 changesets found
157 1 changesets found
158 adding branch
158 adding branch
159 adding changesets
159 adding changesets
160 adding manifests
160 adding manifests
161 adding file changes
161 adding file changes
162 added 1 changesets with 1 changes to 1 files
162 added 1 changesets with 1 changes to 1 files
163 committed changeset 1:ffb49186f961
163 committed changeset 1:7ab3bf440b54
164
164
165 Same, but with changes in working dir (different code path):
165 Same, but with changes in working dir (different code path):
166
166
167 $ echo a >> a
167 $ echo a >> a
168 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v
168 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v
169 amending changeset ffb49186f961
169 amending changeset 7ab3bf440b54
170 a
170 a
171 copying changeset a4f8a65b7c6a to ad120869acf0
171 copying changeset a0ea9b1a4c8c to ad120869acf0
172 another precious commit message
172 another precious commit message
173
173
174
174
175 HG: Enter commit message. Lines beginning with 'HG:' are removed.
175 HG: Enter commit message. Lines beginning with 'HG:' are removed.
176 HG: Leave message empty to abort commit.
176 HG: Leave message empty to abort commit.
177 HG: --
177 HG: --
178 HG: user: foo
178 HG: user: foo
179 HG: branch 'default'
179 HG: branch 'default'
180 HG: changed a
180 HG: changed a
181 a
181 a
182 stripping intermediate changeset a4f8a65b7c6a
182 stripping intermediate changeset a0ea9b1a4c8c
183 stripping amended changeset ffb49186f961
183 stripping amended changeset 7ab3bf440b54
184 2 changesets found
184 2 changesets found
185 saved backup bundle to $TESTTMP/.hg/strip-backup/ffb49186f961-amend-backup.hg (glob)
185 saved backup bundle to $TESTTMP/.hg/strip-backup/7ab3bf440b54-amend-backup.hg (glob)
186 1 changesets found
186 1 changesets found
187 adding branch
187 adding branch
188 adding changesets
188 adding changesets
189 adding manifests
189 adding manifests
190 adding file changes
190 adding file changes
191 added 1 changesets with 1 changes to 1 files
191 added 1 changesets with 1 changes to 1 files
192 committed changeset 1:fb6cca43446f
192 committed changeset 1:ea22a388757c
193
193
194 $ rm editor.sh
194 $ rm editor.sh
195 $ hg log -r .
195 $ hg log -r .
196 changeset: 1:fb6cca43446f
196 changeset: 1:ea22a388757c
197 tag: tip
197 tag: tip
198 user: foo
198 user: foo
199 date: Thu Jan 01 00:00:01 1970 +0000
199 date: Thu Jan 01 00:00:01 1970 +0000
200 summary: another precious commit message
200 summary: another precious commit message
201
201
202
202
203 Moving bookmarks, preserve active bookmark:
203 Moving bookmarks, preserve active bookmark:
204
204
205 $ hg book book1
205 $ hg book book1
206 $ hg book book2
206 $ hg book book2
207 $ hg ci --amend -m 'move bookmarks'
207 $ hg ci --amend -m 'move bookmarks'
208 saved backup bundle to $TESTTMP/.hg/strip-backup/fb6cca43446f-amend-backup.hg (glob)
208 saved backup bundle to $TESTTMP/.hg/strip-backup/ea22a388757c-amend-backup.hg (glob)
209 $ hg book
209 $ hg book
210 book1 1:0cf1c7a51bcf
210 book1 1:6cec5aa930e2
211 * book2 1:0cf1c7a51bcf
211 * book2 1:6cec5aa930e2
212 $ echo a >> a
212 $ echo a >> a
213 $ hg ci --amend -m 'move bookmarks'
213 $ hg ci --amend -m 'move bookmarks'
214 saved backup bundle to $TESTTMP/.hg/strip-backup/0cf1c7a51bcf-amend-backup.hg (glob)
214 saved backup bundle to $TESTTMP/.hg/strip-backup/6cec5aa930e2-amend-backup.hg (glob)
215 $ hg book
215 $ hg book
216 book1 1:7344472bd951
216 book1 1:48bb6e53a15f
217 * book2 1:7344472bd951
217 * book2 1:48bb6e53a15f
218
218
219 $ echo '[defaults]' >> $HGRCPATH
219 $ echo '[defaults]' >> $HGRCPATH
220 $ echo "commit=-d '0 0'" >> $HGRCPATH
220 $ echo "commit=-d '0 0'" >> $HGRCPATH
221
221
222 Moving branches:
222 Moving branches:
223
223
224 $ hg branch foo
224 $ hg branch foo
225 marked working directory as branch foo
225 marked working directory as branch foo
226 (branches are permanent and global, did you want a bookmark?)
226 (branches are permanent and global, did you want a bookmark?)
227 $ echo a >> a
227 $ echo a >> a
228 $ hg ci -m 'branch foo'
228 $ hg ci -m 'branch foo'
229 $ hg branch default -f
229 $ hg branch default -f
230 marked working directory as branch default
230 marked working directory as branch default
231 (branches are permanent and global, did you want a bookmark?)
231 (branches are permanent and global, did you want a bookmark?)
232 $ hg ci --amend -m 'back to default'
232 $ hg ci --amend -m 'back to default'
233 saved backup bundle to $TESTTMP/.hg/strip-backup/1661ca36a2db-amend-backup.hg (glob)
233 saved backup bundle to $TESTTMP/.hg/strip-backup/8ac881fbf49d-amend-backup.hg (glob)
234 $ hg branches
234 $ hg branches
235 default 2:f24ee5961967
235 default 2:ce12b0b57d46
236
236
237 Close branch:
237 Close branch:
238
238
239 $ hg up -q 0
239 $ hg up -q 0
240 $ echo b >> b
240 $ echo b >> b
241 $ hg branch foo
241 $ hg branch foo
242 marked working directory as branch foo
242 marked working directory as branch foo
243 (branches are permanent and global, did you want a bookmark?)
243 (branches are permanent and global, did you want a bookmark?)
244 $ hg ci -Am 'fork'
244 $ hg ci -Am 'fork'
245 adding b
245 adding b
246 $ echo b >> b
246 $ echo b >> b
247 $ hg ci -mb
247 $ hg ci -mb
248 $ hg ci --amend --close-branch -m 'closing branch foo'
248 $ hg ci --amend --close-branch -m 'closing branch foo'
249 saved backup bundle to $TESTTMP/.hg/strip-backup/c962248fa264-amend-backup.hg (glob)
249 saved backup bundle to $TESTTMP/.hg/strip-backup/c962248fa264-amend-backup.hg (glob)
250
250
251 Same thing, different code path:
251 Same thing, different code path:
252
252
253 $ echo b >> b
253 $ echo b >> b
254 $ hg ci -m 'reopen branch'
254 $ hg ci -m 'reopen branch'
255 reopening closed branch head 4
255 reopening closed branch head 4
256 $ echo b >> b
256 $ echo b >> b
257 $ hg ci --amend --close-branch
257 $ hg ci --amend --close-branch
258 saved backup bundle to $TESTTMP/.hg/strip-backup/5e302dcc12b8-amend-backup.hg (glob)
258 saved backup bundle to $TESTTMP/.hg/strip-backup/027371728205-amend-backup.hg (glob)
259 $ hg branches
259 $ hg branches
260 default 2:f24ee5961967
260 default 2:ce12b0b57d46
261
261
262 Refuse to amend merges:
262 Refuse to amend merges:
263
263
264 $ hg up -q default
264 $ hg up -q default
265 $ hg merge foo
265 $ hg merge foo
266 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
266 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
267 (branch merge, don't forget to commit)
267 (branch merge, don't forget to commit)
268 $ hg ci --amend
268 $ hg ci --amend
269 abort: cannot amend while merging
269 abort: cannot amend while merging
270 [255]
270 [255]
271 $ hg ci -m 'merge'
271 $ hg ci -m 'merge'
272 $ hg ci --amend
272 $ hg ci --amend
273 abort: cannot amend merge changesets
273 abort: cannot amend merge changesets
274 [255]
274 [255]
275
275
276 Follow copies/renames:
276 Follow copies/renames:
277
277
278 $ hg mv b c
278 $ hg mv b c
279 $ hg ci -m 'b -> c'
279 $ hg ci -m 'b -> c'
280 $ hg mv c d
280 $ hg mv c d
281 $ hg ci --amend -m 'b -> d'
281 $ hg ci --amend -m 'b -> d'
282 saved backup bundle to $TESTTMP/.hg/strip-backup/9c207120aa98-amend-backup.hg (glob)
282 saved backup bundle to $TESTTMP/.hg/strip-backup/b8c6eac7f12e-amend-backup.hg (glob)
283 $ hg st --rev '.^' --copies d
283 $ hg st --rev '.^' --copies d
284 A d
284 A d
285 b
285 b
286 $ hg cp d e
286 $ hg cp d e
287 $ hg ci -m 'e = d'
287 $ hg ci -m 'e = d'
288 $ hg cp e f
288 $ hg cp e f
289 $ hg ci --amend -m 'f = d'
289 $ hg ci --amend -m 'f = d'
290 saved backup bundle to $TESTTMP/.hg/strip-backup/fda2b3b27b22-amend-backup.hg (glob)
290 saved backup bundle to $TESTTMP/.hg/strip-backup/7f9761d65613-amend-backup.hg (glob)
291 $ hg st --rev '.^' --copies f
291 $ hg st --rev '.^' --copies f
292 A f
292 A f
293 d
293 d
294
294
295 $ mv f f.orig
295 $ mv f f.orig
296 $ hg rm -A f
296 $ hg rm -A f
297 $ hg ci -m removef
297 $ hg ci -m removef
298 $ hg cp a f
298 $ hg cp a f
299 $ mv f.orig f
299 $ mv f.orig f
300 $ hg ci --amend -m replacef
300 $ hg ci --amend -m replacef
301 saved backup bundle to $TESTTMP/.hg/strip-backup/20a7413547f9-amend-backup.hg (glob)
301 saved backup bundle to $TESTTMP/.hg/strip-backup/9e8c5f7e3d95-amend-backup.hg (glob)
302 $ hg st --change . --copies
302 $ hg st --change . --copies
303 $ hg log -r . --template "{file_copies}\n"
303 $ hg log -r . --template "{file_copies}\n"
304
304
305
305
306 Move added file (issue3410):
306 Move added file (issue3410):
307
307
308 $ echo g >> g
308 $ echo g >> g
309 $ hg ci -Am g
309 $ hg ci -Am g
310 adding g
310 adding g
311 $ hg mv g h
311 $ hg mv g h
312 $ hg ci --amend
312 $ hg ci --amend
313 saved backup bundle to $TESTTMP/.hg/strip-backup/5daa77a5d616-amend-backup.hg (glob)
313 saved backup bundle to $TESTTMP/.hg/strip-backup/24aa8eacce2b-amend-backup.hg (glob)
314 $ hg st --change . --copies h
314 $ hg st --change . --copies h
315 A h
315 A h
316 $ hg log -r . --template "{file_copies}\n"
316 $ hg log -r . --template "{file_copies}\n"
317
317
318
318
319 Can't rollback an amend:
319 Can't rollback an amend:
320
320
321 $ hg rollback
321 $ hg rollback
322 no rollback information available
322 no rollback information available
323 [1]
323 [1]
324
324
325 Preserve extra dict (issue3430):
325 Preserve extra dict (issue3430):
326
326
327 $ hg branch a
327 $ hg branch a
328 marked working directory as branch a
328 marked working directory as branch a
329 (branches are permanent and global, did you want a bookmark?)
329 (branches are permanent and global, did you want a bookmark?)
330 $ echo a >> a
330 $ echo a >> a
331 $ hg ci -ma
331 $ hg ci -ma
332 $ hg ci --amend -m "a'"
332 $ hg ci --amend -m "a'"
333 saved backup bundle to $TESTTMP/.hg/strip-backup/167f8e3031df-amend-backup.hg (glob)
333 saved backup bundle to $TESTTMP/.hg/strip-backup/3837aa2a2fdb-amend-backup.hg (glob)
334 $ hg log -r . --template "{branch}\n"
334 $ hg log -r . --template "{branch}\n"
335 a
335 a
336 $ hg ci --amend -m "a''"
336 $ hg ci --amend -m "a''"
337 saved backup bundle to $TESTTMP/.hg/strip-backup/ceac1a44c806-amend-backup.hg (glob)
337 saved backup bundle to $TESTTMP/.hg/strip-backup/c05c06be7514-amend-backup.hg (glob)
338 $ hg log -r . --template "{branch}\n"
338 $ hg log -r . --template "{branch}\n"
339 a
339 a
340
340
341 Also preserve other entries in the dict that are in the old commit,
341 Also preserve other entries in the dict that are in the old commit,
342 first graft something so there's an additional entry:
342 first graft something so there's an additional entry:
343
343
344 $ hg up 0 -q
344 $ hg up 0 -q
345 $ echo z > z
345 $ echo z > z
346 $ hg ci -Am 'fork'
346 $ hg ci -Am 'fork'
347 adding z
347 adding z
348 created new head
348 created new head
349 $ hg up 11
349 $ hg up 11
350 5 files updated, 0 files merged, 1 files removed, 0 files unresolved
350 5 files updated, 0 files merged, 1 files removed, 0 files unresolved
351 $ hg graft 12
351 $ hg graft 12
352 grafting revision 12
352 grafting revision 12
353 $ hg ci --amend -m 'graft amend'
353 $ hg ci --amend -m 'graft amend'
354 saved backup bundle to $TESTTMP/.hg/strip-backup/18a5124daf7a-amend-backup.hg (glob)
354 saved backup bundle to $TESTTMP/.hg/strip-backup/bd010aea3f39-amend-backup.hg (glob)
355 $ hg log -r . --debug | grep extra
355 $ hg log -r . --debug | grep extra
356 extra: amend_source=bd010aea3f39f3fb2a2f884b9ccb0471cd77398e
356 extra: branch=a
357 extra: branch=a
357 extra: source=2647734878ef0236dda712fae9c1651cf694ea8a
358 extra: source=2647734878ef0236dda712fae9c1651cf694ea8a
358
359
359 Preserve phase
360 Preserve phase
360
361
361 $ hg phase '.^::.'
362 $ hg phase '.^::.'
362 11: draft
363 11: draft
363 13: draft
364 13: draft
364 $ hg phase --secret --force .
365 $ hg phase --secret --force .
365 $ hg phase '.^::.'
366 $ hg phase '.^::.'
366 11: draft
367 11: draft
367 13: secret
368 13: secret
368 $ hg commit --amend -m 'amend for phase' -q
369 $ hg commit --amend -m 'amend for phase' -q
369 $ hg phase '.^::.'
370 $ hg phase '.^::.'
370 11: draft
371 11: draft
371 13: secret
372 13: secret
372
373
373 Test amend with obsolete
374 Test amend with obsolete
374 ---------------------------
375 ---------------------------
375
376
376 Enable obsolete
377 Enable obsolete
377
378
378 $ cat > ${TESTTMP}/obs.py << EOF
379 $ cat > ${TESTTMP}/obs.py << EOF
379 > import mercurial.obsolete
380 > import mercurial.obsolete
380 > mercurial.obsolete._enabled = True
381 > mercurial.obsolete._enabled = True
381 > EOF
382 > EOF
382 $ echo '[extensions]' >> $HGRCPATH
383 $ echo '[extensions]' >> $HGRCPATH
383 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
384 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
384
385
385
386
386 Amend with no files changes
387 Amend with no files changes
387
388
388 $ hg id -n
389 $ hg id -n
389 13
390 13
390 $ hg ci --amend -m 'babar'
391 $ hg ci --amend -m 'babar'
391 $ hg id -n
392 $ hg id -n
392 14
393 14
393 $ hg log -Gl 3 --style=compact
394 $ hg log -Gl 3 --style=compact
394 @ 14[tip]:11 43df5a5434ad 1970-01-01 00:00 +0000 test
395 @ 14[tip]:11 b650e6ee8614 1970-01-01 00:00 +0000 test
395 | babar
396 | babar
396 |
397 |
397 | o 12:0 2647734878ef 1970-01-01 00:00 +0000 test
398 | o 12:0 2647734878ef 1970-01-01 00:00 +0000 test
398 | | fork
399 | | fork
399 | |
400 | |
400 o | 11 7e09f708a0e9 1970-01-01 00:00 +0000 test
401 o | 11 3334b7925910 1970-01-01 00:00 +0000 test
401 | | a''
402 | | a''
402 | |
403 | |
403 $ hg log -Gl 4 --hidden --style=compact
404 $ hg log -Gl 4 --hidden --style=compact
404 @ 14[tip]:11 43df5a5434ad 1970-01-01 00:00 +0000 test
405 @ 14[tip]:11 b650e6ee8614 1970-01-01 00:00 +0000 test
405 | babar
406 | babar
406 |
407 |
407 | x 13:11 175fafee6f44 1970-01-01 00:00 +0000 test
408 | x 13:11 68ff8ff97044 1970-01-01 00:00 +0000 test
408 |/ amend for phase
409 |/ amend for phase
409 |
410 |
410 | o 12:0 2647734878ef 1970-01-01 00:00 +0000 test
411 | o 12:0 2647734878ef 1970-01-01 00:00 +0000 test
411 | | fork
412 | | fork
412 | |
413 | |
413 o | 11 7e09f708a0e9 1970-01-01 00:00 +0000 test
414 o | 11 3334b7925910 1970-01-01 00:00 +0000 test
414 | | a''
415 | | a''
415 | |
416 | |
416
417
417 Amend with files changes
418 Amend with files changes
418
419
419 (note: the extra commit over 15 is a temporary junk I would be happy to get
420 (note: the extra commit over 15 is a temporary junk I would be happy to get
420 ride of)
421 ride of)
421
422
422 $ echo 'babar' >> a
423 $ echo 'babar' >> a
423 $ hg commit --amend
424 $ hg commit --amend
424 $ hg log -Gl 6 --hidden --style=compact
425 $ hg log -Gl 6 --hidden --style=compact
425 @ 16[tip]:11 31e0a4a1b04a 1970-01-01 00:00 +0000 test
426 @ 16[tip]:11 9f9e9bccf56c 1970-01-01 00:00 +0000 test
426 | babar
427 | babar
427 |
428 |
428 | x 15 053c696ada75 1970-01-01 00:00 +0000 test
429 | x 15 90fef497c56f 1970-01-01 00:00 +0000 test
429 | | temporary amend commit for 43df5a5434ad
430 | | temporary amend commit for b650e6ee8614
430 | |
431 | |
431 | x 14:11 43df5a5434ad 1970-01-01 00:00 +0000 test
432 | x 14:11 b650e6ee8614 1970-01-01 00:00 +0000 test
432 |/ babar
433 |/ babar
433 |
434 |
434 | x 13:11 175fafee6f44 1970-01-01 00:00 +0000 test
435 | x 13:11 68ff8ff97044 1970-01-01 00:00 +0000 test
435 |/ amend for phase
436 |/ amend for phase
436 |
437 |
437 | o 12:0 2647734878ef 1970-01-01 00:00 +0000 test
438 | o 12:0 2647734878ef 1970-01-01 00:00 +0000 test
438 | | fork
439 | | fork
439 | |
440 | |
440 o | 11 7e09f708a0e9 1970-01-01 00:00 +0000 test
441 o | 11 3334b7925910 1970-01-01 00:00 +0000 test
441 | | a''
442 | | a''
442 | |
443 | |
443
444
444
445
446 Test that amend does not make it easy to create obsoletescence cycle
447 ---------------------------------------------------------------------
448
449
450 $ hg id -r 14
451 b650e6ee8614 (a)
452 $ hg revert -ar 14
453 reverting a
454 $ hg commit --amend
455 $ hg id
456 b99e5df575f7 (a) tip
@@ -1,1139 +1,1139 b''
1 $ cat <<EOF >> $HGRCPATH
1 $ cat <<EOF >> $HGRCPATH
2 > [extensions]
2 > [extensions]
3 > keyword =
3 > keyword =
4 > mq =
4 > mq =
5 > notify =
5 > notify =
6 > record =
6 > record =
7 > transplant =
7 > transplant =
8 > [ui]
8 > [ui]
9 > interactive = true
9 > interactive = true
10 > EOF
10 > EOF
11
11
12 hide outer repo
12 hide outer repo
13 $ hg init
13 $ hg init
14
14
15 Run kwdemo before [keyword] files are set up
15 Run kwdemo before [keyword] files are set up
16 as it would succeed without uisetup otherwise
16 as it would succeed without uisetup otherwise
17
17
18 $ hg --quiet kwdemo
18 $ hg --quiet kwdemo
19 [extensions]
19 [extensions]
20 keyword =
20 keyword =
21 [keyword]
21 [keyword]
22 demo.txt =
22 demo.txt =
23 [keywordset]
23 [keywordset]
24 svn = False
24 svn = False
25 [keywordmaps]
25 [keywordmaps]
26 Author = {author|user}
26 Author = {author|user}
27 Date = {date|utcdate}
27 Date = {date|utcdate}
28 Header = {root}/{file},v {node|short} {date|utcdate} {author|user}
28 Header = {root}/{file},v {node|short} {date|utcdate} {author|user}
29 Id = {file|basename},v {node|short} {date|utcdate} {author|user}
29 Id = {file|basename},v {node|short} {date|utcdate} {author|user}
30 RCSFile = {file|basename},v
30 RCSFile = {file|basename},v
31 RCSfile = {file|basename},v
31 RCSfile = {file|basename},v
32 Revision = {node|short}
32 Revision = {node|short}
33 Source = {root}/{file},v
33 Source = {root}/{file},v
34 $Author: test $
34 $Author: test $
35 $Date: ????/??/?? ??:??:?? $ (glob)
35 $Date: ????/??/?? ??:??:?? $ (glob)
36 $Header: */demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
36 $Header: */demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
37 $Id: demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
37 $Id: demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
38 $RCSFile: demo.txt,v $
38 $RCSFile: demo.txt,v $
39 $RCSfile: demo.txt,v $
39 $RCSfile: demo.txt,v $
40 $Revision: ???????????? $ (glob)
40 $Revision: ???????????? $ (glob)
41 $Source: */demo.txt,v $ (glob)
41 $Source: */demo.txt,v $ (glob)
42
42
43 $ hg --quiet kwdemo "Branch = {branches}"
43 $ hg --quiet kwdemo "Branch = {branches}"
44 [extensions]
44 [extensions]
45 keyword =
45 keyword =
46 [keyword]
46 [keyword]
47 demo.txt =
47 demo.txt =
48 [keywordset]
48 [keywordset]
49 svn = False
49 svn = False
50 [keywordmaps]
50 [keywordmaps]
51 Branch = {branches}
51 Branch = {branches}
52 $Branch: demobranch $
52 $Branch: demobranch $
53
53
54 $ cat <<EOF >> $HGRCPATH
54 $ cat <<EOF >> $HGRCPATH
55 > [keyword]
55 > [keyword]
56 > ** =
56 > ** =
57 > b = ignore
57 > b = ignore
58 > i = ignore
58 > i = ignore
59 > [hooks]
59 > [hooks]
60 > EOF
60 > EOF
61 $ cp $HGRCPATH $HGRCPATH.nohooks
61 $ cp $HGRCPATH $HGRCPATH.nohooks
62 > cat <<EOF >> $HGRCPATH
62 > cat <<EOF >> $HGRCPATH
63 > commit=
63 > commit=
64 > commit.test=cp a hooktest
64 > commit.test=cp a hooktest
65 > EOF
65 > EOF
66
66
67 $ hg init Test-bndl
67 $ hg init Test-bndl
68 $ cd Test-bndl
68 $ cd Test-bndl
69
69
70 kwshrink should exit silently in empty/invalid repo
70 kwshrink should exit silently in empty/invalid repo
71
71
72 $ hg kwshrink
72 $ hg kwshrink
73
73
74 Symlinks cannot be created on Windows.
74 Symlinks cannot be created on Windows.
75 A bundle to test this was made with:
75 A bundle to test this was made with:
76 hg init t
76 hg init t
77 cd t
77 cd t
78 echo a > a
78 echo a > a
79 ln -s a sym
79 ln -s a sym
80 hg add sym
80 hg add sym
81 hg ci -m addsym -u mercurial
81 hg ci -m addsym -u mercurial
82 hg bundle --base null ../test-keyword.hg
82 hg bundle --base null ../test-keyword.hg
83
83
84 $ hg pull -u "$TESTDIR"/bundles/test-keyword.hg
84 $ hg pull -u "$TESTDIR"/bundles/test-keyword.hg
85 pulling from *test-keyword.hg (glob)
85 pulling from *test-keyword.hg (glob)
86 requesting all changes
86 requesting all changes
87 adding changesets
87 adding changesets
88 adding manifests
88 adding manifests
89 adding file changes
89 adding file changes
90 added 1 changesets with 1 changes to 1 files
90 added 1 changesets with 1 changes to 1 files
91 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
91 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
92
92
93 $ echo 'expand $Id$' > a
93 $ echo 'expand $Id$' > a
94 $ echo 'do not process $Id:' >> a
94 $ echo 'do not process $Id:' >> a
95 $ echo 'xxx $' >> a
95 $ echo 'xxx $' >> a
96 $ echo 'ignore $Id$' > b
96 $ echo 'ignore $Id$' > b
97
97
98 Output files as they were created
98 Output files as they were created
99
99
100 $ cat a b
100 $ cat a b
101 expand $Id$
101 expand $Id$
102 do not process $Id:
102 do not process $Id:
103 xxx $
103 xxx $
104 ignore $Id$
104 ignore $Id$
105
105
106 no kwfiles
106 no kwfiles
107
107
108 $ hg kwfiles
108 $ hg kwfiles
109
109
110 untracked candidates
110 untracked candidates
111
111
112 $ hg -v kwfiles --unknown
112 $ hg -v kwfiles --unknown
113 k a
113 k a
114
114
115 Add files and check status
115 Add files and check status
116
116
117 $ hg addremove
117 $ hg addremove
118 adding a
118 adding a
119 adding b
119 adding b
120 $ hg status
120 $ hg status
121 A a
121 A a
122 A b
122 A b
123
123
124
124
125 Default keyword expansion including commit hook
125 Default keyword expansion including commit hook
126 Interrupted commit should not change state or run commit hook
126 Interrupted commit should not change state or run commit hook
127
127
128 $ hg --debug commit
128 $ hg --debug commit
129 abort: empty commit message
129 abort: empty commit message
130 [255]
130 [255]
131 $ hg status
131 $ hg status
132 A a
132 A a
133 A b
133 A b
134
134
135 Commit with several checks
135 Commit with several checks
136
136
137 $ hg --debug commit -mabsym -u 'User Name <user@example.com>'
137 $ hg --debug commit -mabsym -u 'User Name <user@example.com>'
138 a
138 a
139 b
139 b
140 overwriting a expanding keywords
140 overwriting a expanding keywords
141 running hook commit.test: cp a hooktest
141 running hook commit.test: cp a hooktest
142 committed changeset 1:ef63ca68695bc9495032c6fda1350c71e6d256e9
142 committed changeset 1:ef63ca68695bc9495032c6fda1350c71e6d256e9
143 $ hg status
143 $ hg status
144 ? hooktest
144 ? hooktest
145 $ hg debugrebuildstate
145 $ hg debugrebuildstate
146 $ hg --quiet identify
146 $ hg --quiet identify
147 ef63ca68695b
147 ef63ca68695b
148
148
149 cat files in working directory with keywords expanded
149 cat files in working directory with keywords expanded
150
150
151 $ cat a b
151 $ cat a b
152 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
152 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
153 do not process $Id:
153 do not process $Id:
154 xxx $
154 xxx $
155 ignore $Id$
155 ignore $Id$
156
156
157 hg cat files and symlink, no expansion
157 hg cat files and symlink, no expansion
158
158
159 $ hg cat sym a b && echo
159 $ hg cat sym a b && echo
160 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
160 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
161 do not process $Id:
161 do not process $Id:
162 xxx $
162 xxx $
163 ignore $Id$
163 ignore $Id$
164 a
164 a
165
165
166 $ diff a hooktest
166 $ diff a hooktest
167
167
168 $ cp $HGRCPATH.nohooks $HGRCPATH
168 $ cp $HGRCPATH.nohooks $HGRCPATH
169 $ rm hooktest
169 $ rm hooktest
170
170
171 hg status of kw-ignored binary file starting with '\1\n'
171 hg status of kw-ignored binary file starting with '\1\n'
172
172
173 >>> open("i", "wb").write("\1\nfoo")
173 >>> open("i", "wb").write("\1\nfoo")
174 $ hg -q commit -Am metasep i
174 $ hg -q commit -Am metasep i
175 $ hg status
175 $ hg status
176 >>> open("i", "wb").write("\1\nbar")
176 >>> open("i", "wb").write("\1\nbar")
177 $ hg status
177 $ hg status
178 M i
178 M i
179 $ hg -q commit -m "modify metasep" i
179 $ hg -q commit -m "modify metasep" i
180 $ hg status --rev 2:3
180 $ hg status --rev 2:3
181 M i
181 M i
182 $ touch empty
182 $ touch empty
183 $ hg -q commit -A -m "another file"
183 $ hg -q commit -A -m "another file"
184 $ hg status -A --rev 3:4 i
184 $ hg status -A --rev 3:4 i
185 C i
185 C i
186
186
187 $ hg -q strip -n 2
187 $ hg -q strip -n 2
188
188
189 Test hook execution
189 Test hook execution
190
190
191 bundle
191 bundle
192
192
193 $ hg bundle --base null ../kw.hg
193 $ hg bundle --base null ../kw.hg
194 2 changesets found
194 2 changesets found
195 $ cd ..
195 $ cd ..
196 $ hg init Test
196 $ hg init Test
197 $ cd Test
197 $ cd Test
198
198
199 Notify on pull to check whether keywords stay as is in email
199 Notify on pull to check whether keywords stay as is in email
200 ie. if patch.diff wrapper acts as it should
200 ie. if patch.diff wrapper acts as it should
201
201
202 $ cat <<EOF >> $HGRCPATH
202 $ cat <<EOF >> $HGRCPATH
203 > [hooks]
203 > [hooks]
204 > incoming.notify = python:hgext.notify.hook
204 > incoming.notify = python:hgext.notify.hook
205 > [notify]
205 > [notify]
206 > sources = pull
206 > sources = pull
207 > diffstat = False
207 > diffstat = False
208 > maxsubject = 15
208 > maxsubject = 15
209 > [reposubs]
209 > [reposubs]
210 > * = Test
210 > * = Test
211 > EOF
211 > EOF
212
212
213 Pull from bundle and trigger notify
213 Pull from bundle and trigger notify
214
214
215 $ hg pull -u ../kw.hg
215 $ hg pull -u ../kw.hg
216 pulling from ../kw.hg
216 pulling from ../kw.hg
217 requesting all changes
217 requesting all changes
218 adding changesets
218 adding changesets
219 adding manifests
219 adding manifests
220 adding file changes
220 adding file changes
221 added 2 changesets with 3 changes to 3 files
221 added 2 changesets with 3 changes to 3 files
222 Content-Type: text/plain; charset="us-ascii"
222 Content-Type: text/plain; charset="us-ascii"
223 MIME-Version: 1.0
223 MIME-Version: 1.0
224 Content-Transfer-Encoding: 7bit
224 Content-Transfer-Encoding: 7bit
225 Date: * (glob)
225 Date: * (glob)
226 Subject: changeset in...
226 Subject: changeset in...
227 From: mercurial
227 From: mercurial
228 X-Hg-Notification: changeset a2392c293916
228 X-Hg-Notification: changeset a2392c293916
229 Message-Id: <hg.a2392c293916*> (glob)
229 Message-Id: <hg.a2392c293916*> (glob)
230 To: Test
230 To: Test
231
231
232 changeset a2392c293916 in $TESTTMP/Test (glob)
232 changeset a2392c293916 in $TESTTMP/Test (glob)
233 details: $TESTTMP/Test?cmd=changeset;node=a2392c293916
233 details: $TESTTMP/Test?cmd=changeset;node=a2392c293916
234 description:
234 description:
235 addsym
235 addsym
236
236
237 diffs (6 lines):
237 diffs (6 lines):
238
238
239 diff -r 000000000000 -r a2392c293916 sym
239 diff -r 000000000000 -r a2392c293916 sym
240 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
240 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
241 +++ b/sym Sat Feb 09 20:25:47 2008 +0100
241 +++ b/sym Sat Feb 09 20:25:47 2008 +0100
242 @@ -0,0 +1,1 @@
242 @@ -0,0 +1,1 @@
243 +a
243 +a
244 \ No newline at end of file
244 \ No newline at end of file
245 Content-Type: text/plain; charset="us-ascii"
245 Content-Type: text/plain; charset="us-ascii"
246 MIME-Version: 1.0
246 MIME-Version: 1.0
247 Content-Transfer-Encoding: 7bit
247 Content-Transfer-Encoding: 7bit
248 Date:* (glob)
248 Date:* (glob)
249 Subject: changeset in...
249 Subject: changeset in...
250 From: User Name <user@example.com>
250 From: User Name <user@example.com>
251 X-Hg-Notification: changeset ef63ca68695b
251 X-Hg-Notification: changeset ef63ca68695b
252 Message-Id: <hg.ef63ca68695b*> (glob)
252 Message-Id: <hg.ef63ca68695b*> (glob)
253 To: Test
253 To: Test
254
254
255 changeset ef63ca68695b in $TESTTMP/Test (glob)
255 changeset ef63ca68695b in $TESTTMP/Test (glob)
256 details: $TESTTMP/Test?cmd=changeset;node=ef63ca68695b
256 details: $TESTTMP/Test?cmd=changeset;node=ef63ca68695b
257 description:
257 description:
258 absym
258 absym
259
259
260 diffs (12 lines):
260 diffs (12 lines):
261
261
262 diff -r a2392c293916 -r ef63ca68695b a
262 diff -r a2392c293916 -r ef63ca68695b a
263 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
263 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
264 +++ b/a Thu Jan 01 00:00:00 1970 +0000
264 +++ b/a Thu Jan 01 00:00:00 1970 +0000
265 @@ -0,0 +1,3 @@
265 @@ -0,0 +1,3 @@
266 +expand $Id$
266 +expand $Id$
267 +do not process $Id:
267 +do not process $Id:
268 +xxx $
268 +xxx $
269 diff -r a2392c293916 -r ef63ca68695b b
269 diff -r a2392c293916 -r ef63ca68695b b
270 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
270 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
271 +++ b/b Thu Jan 01 00:00:00 1970 +0000
271 +++ b/b Thu Jan 01 00:00:00 1970 +0000
272 @@ -0,0 +1,1 @@
272 @@ -0,0 +1,1 @@
273 +ignore $Id$
273 +ignore $Id$
274 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
274 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
275
275
276 $ cp $HGRCPATH.nohooks $HGRCPATH
276 $ cp $HGRCPATH.nohooks $HGRCPATH
277
277
278 Touch files and check with status
278 Touch files and check with status
279
279
280 $ touch a b
280 $ touch a b
281 $ hg status
281 $ hg status
282
282
283 Update and expand
283 Update and expand
284
284
285 $ rm sym a b
285 $ rm sym a b
286 $ hg update -C
286 $ hg update -C
287 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
287 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
288 $ cat a b
288 $ cat a b
289 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
289 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
290 do not process $Id:
290 do not process $Id:
291 xxx $
291 xxx $
292 ignore $Id$
292 ignore $Id$
293
293
294 Check whether expansion is filewise and file mode is preserved
294 Check whether expansion is filewise and file mode is preserved
295
295
296 $ echo '$Id$' > c
296 $ echo '$Id$' > c
297 $ echo 'tests for different changenodes' >> c
297 $ echo 'tests for different changenodes' >> c
298 #if unix-permissions
298 #if unix-permissions
299 $ chmod 600 c
299 $ chmod 600 c
300 $ ls -l c | cut -b 1-10
300 $ ls -l c | cut -b 1-10
301 -rw-------
301 -rw-------
302 #endif
302 #endif
303
303
304 commit file c
304 commit file c
305
305
306 $ hg commit -A -mcndiff -d '1 0' -u 'User Name <user@example.com>'
306 $ hg commit -A -mcndiff -d '1 0' -u 'User Name <user@example.com>'
307 adding c
307 adding c
308 #if unix-permissions
308 #if unix-permissions
309 $ ls -l c | cut -b 1-10
309 $ ls -l c | cut -b 1-10
310 -rw-------
310 -rw-------
311 #endif
311 #endif
312
312
313 force expansion
313 force expansion
314
314
315 $ hg -v kwexpand
315 $ hg -v kwexpand
316 overwriting a expanding keywords
316 overwriting a expanding keywords
317 overwriting c expanding keywords
317 overwriting c expanding keywords
318
318
319 compare changenodes in a and c
319 compare changenodes in a and c
320
320
321 $ cat a c
321 $ cat a c
322 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
322 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
323 do not process $Id:
323 do not process $Id:
324 xxx $
324 xxx $
325 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
325 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
326 tests for different changenodes
326 tests for different changenodes
327
327
328 record
328 record
329
329
330 $ echo '$Id$' > r
330 $ echo '$Id$' > r
331 $ hg add r
331 $ hg add r
332
332
333 record chunk
333 record chunk
334
334
335 >>> lines = open('a', 'rb').readlines()
335 >>> lines = open('a', 'rb').readlines()
336 >>> lines.insert(1, 'foo\n')
336 >>> lines.insert(1, 'foo\n')
337 >>> lines.append('bar\n')
337 >>> lines.append('bar\n')
338 >>> open('a', 'wb').writelines(lines)
338 >>> open('a', 'wb').writelines(lines)
339 $ hg record -d '10 1' -m rectest a<<EOF
339 $ hg record -d '10 1' -m rectest a<<EOF
340 > y
340 > y
341 > y
341 > y
342 > n
342 > n
343 > EOF
343 > EOF
344 diff --git a/a b/a
344 diff --git a/a b/a
345 2 hunks, 2 lines changed
345 2 hunks, 2 lines changed
346 examine changes to 'a'? [Ynesfdaq?]
346 examine changes to 'a'? [Ynesfdaq?]
347 @@ -1,3 +1,4 @@
347 @@ -1,3 +1,4 @@
348 expand $Id$
348 expand $Id$
349 +foo
349 +foo
350 do not process $Id:
350 do not process $Id:
351 xxx $
351 xxx $
352 record change 1/2 to 'a'? [Ynesfdaq?]
352 record change 1/2 to 'a'? [Ynesfdaq?]
353 @@ -2,2 +3,3 @@
353 @@ -2,2 +3,3 @@
354 do not process $Id:
354 do not process $Id:
355 xxx $
355 xxx $
356 +bar
356 +bar
357 record change 2/2 to 'a'? [Ynesfdaq?]
357 record change 2/2 to 'a'? [Ynesfdaq?]
358
358
359 $ hg identify
359 $ hg identify
360 5f5eb23505c3+ tip
360 5f5eb23505c3+ tip
361 $ hg status
361 $ hg status
362 M a
362 M a
363 A r
363 A r
364
364
365 Cat modified file a
365 Cat modified file a
366
366
367 $ cat a
367 $ cat a
368 expand $Id: a,v 5f5eb23505c3 1970/01/01 00:00:10 test $
368 expand $Id: a,v 5f5eb23505c3 1970/01/01 00:00:10 test $
369 foo
369 foo
370 do not process $Id:
370 do not process $Id:
371 xxx $
371 xxx $
372 bar
372 bar
373
373
374 Diff remaining chunk
374 Diff remaining chunk
375
375
376 $ hg diff a
376 $ hg diff a
377 diff -r 5f5eb23505c3 a
377 diff -r 5f5eb23505c3 a
378 --- a/a Thu Jan 01 00:00:09 1970 -0000
378 --- a/a Thu Jan 01 00:00:09 1970 -0000
379 +++ b/a * (glob)
379 +++ b/a * (glob)
380 @@ -2,3 +2,4 @@
380 @@ -2,3 +2,4 @@
381 foo
381 foo
382 do not process $Id:
382 do not process $Id:
383 xxx $
383 xxx $
384 +bar
384 +bar
385
385
386 $ hg rollback
386 $ hg rollback
387 repository tip rolled back to revision 2 (undo commit)
387 repository tip rolled back to revision 2 (undo commit)
388 working directory now based on revision 2
388 working directory now based on revision 2
389
389
390 Record all chunks in file a
390 Record all chunks in file a
391
391
392 $ echo foo > msg
392 $ echo foo > msg
393
393
394 - do not use "hg record -m" here!
394 - do not use "hg record -m" here!
395
395
396 $ hg record -l msg -d '11 1' a<<EOF
396 $ hg record -l msg -d '11 1' a<<EOF
397 > y
397 > y
398 > y
398 > y
399 > y
399 > y
400 > EOF
400 > EOF
401 diff --git a/a b/a
401 diff --git a/a b/a
402 2 hunks, 2 lines changed
402 2 hunks, 2 lines changed
403 examine changes to 'a'? [Ynesfdaq?]
403 examine changes to 'a'? [Ynesfdaq?]
404 @@ -1,3 +1,4 @@
404 @@ -1,3 +1,4 @@
405 expand $Id$
405 expand $Id$
406 +foo
406 +foo
407 do not process $Id:
407 do not process $Id:
408 xxx $
408 xxx $
409 record change 1/2 to 'a'? [Ynesfdaq?]
409 record change 1/2 to 'a'? [Ynesfdaq?]
410 @@ -2,2 +3,3 @@
410 @@ -2,2 +3,3 @@
411 do not process $Id:
411 do not process $Id:
412 xxx $
412 xxx $
413 +bar
413 +bar
414 record change 2/2 to 'a'? [Ynesfdaq?]
414 record change 2/2 to 'a'? [Ynesfdaq?]
415
415
416 File a should be clean
416 File a should be clean
417
417
418 $ hg status -A a
418 $ hg status -A a
419 C a
419 C a
420
420
421 rollback and revert expansion
421 rollback and revert expansion
422
422
423 $ cat a
423 $ cat a
424 expand $Id: a,v 78e0a02d76aa 1970/01/01 00:00:11 test $
424 expand $Id: a,v 78e0a02d76aa 1970/01/01 00:00:11 test $
425 foo
425 foo
426 do not process $Id:
426 do not process $Id:
427 xxx $
427 xxx $
428 bar
428 bar
429 $ hg --verbose rollback
429 $ hg --verbose rollback
430 repository tip rolled back to revision 2 (undo commit)
430 repository tip rolled back to revision 2 (undo commit)
431 working directory now based on revision 2
431 working directory now based on revision 2
432 overwriting a expanding keywords
432 overwriting a expanding keywords
433 $ hg status a
433 $ hg status a
434 M a
434 M a
435 $ cat a
435 $ cat a
436 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
436 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
437 foo
437 foo
438 do not process $Id:
438 do not process $Id:
439 xxx $
439 xxx $
440 bar
440 bar
441 $ echo '$Id$' > y
441 $ echo '$Id$' > y
442 $ echo '$Id$' > z
442 $ echo '$Id$' > z
443 $ hg add y
443 $ hg add y
444 $ hg commit -Am "rollback only" z
444 $ hg commit -Am "rollback only" z
445 $ cat z
445 $ cat z
446 $Id: z,v 45a5d3adce53 1970/01/01 00:00:00 test $
446 $Id: z,v 45a5d3adce53 1970/01/01 00:00:00 test $
447 $ hg --verbose rollback
447 $ hg --verbose rollback
448 repository tip rolled back to revision 2 (undo commit)
448 repository tip rolled back to revision 2 (undo commit)
449 working directory now based on revision 2
449 working directory now based on revision 2
450 overwriting z shrinking keywords
450 overwriting z shrinking keywords
451
451
452 Only z should be overwritten
452 Only z should be overwritten
453
453
454 $ hg status a y z
454 $ hg status a y z
455 M a
455 M a
456 A y
456 A y
457 A z
457 A z
458 $ cat z
458 $ cat z
459 $Id$
459 $Id$
460 $ hg forget y z
460 $ hg forget y z
461 $ rm y z
461 $ rm y z
462
462
463 record added file alone
463 record added file alone
464
464
465 $ hg -v record -l msg -d '12 2' r<<EOF
465 $ hg -v record -l msg -d '12 2' r<<EOF
466 > y
466 > y
467 > EOF
467 > EOF
468 diff --git a/r b/r
468 diff --git a/r b/r
469 new file mode 100644
469 new file mode 100644
470 examine changes to 'r'? [Ynesfdaq?]
470 examine changes to 'r'? [Ynesfdaq?]
471 r
471 r
472 committed changeset 3:82a2f715724d
472 committed changeset 3:82a2f715724d
473 overwriting r expanding keywords
473 overwriting r expanding keywords
474 - status call required for dirstate.normallookup() check
474 - status call required for dirstate.normallookup() check
475 $ hg status r
475 $ hg status r
476 $ hg --verbose rollback
476 $ hg --verbose rollback
477 repository tip rolled back to revision 2 (undo commit)
477 repository tip rolled back to revision 2 (undo commit)
478 working directory now based on revision 2
478 working directory now based on revision 2
479 overwriting r shrinking keywords
479 overwriting r shrinking keywords
480 $ hg forget r
480 $ hg forget r
481 $ rm msg r
481 $ rm msg r
482 $ hg update -C
482 $ hg update -C
483 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
483 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
484
484
485 record added keyword ignored file
485 record added keyword ignored file
486
486
487 $ echo '$Id$' > i
487 $ echo '$Id$' > i
488 $ hg add i
488 $ hg add i
489 $ hg --verbose record -d '13 1' -m recignored<<EOF
489 $ hg --verbose record -d '13 1' -m recignored<<EOF
490 > y
490 > y
491 > EOF
491 > EOF
492 diff --git a/i b/i
492 diff --git a/i b/i
493 new file mode 100644
493 new file mode 100644
494 examine changes to 'i'? [Ynesfdaq?]
494 examine changes to 'i'? [Ynesfdaq?]
495 i
495 i
496 committed changeset 3:9f40ceb5a072
496 committed changeset 3:9f40ceb5a072
497 $ cat i
497 $ cat i
498 $Id$
498 $Id$
499 $ hg -q rollback
499 $ hg -q rollback
500 $ hg forget i
500 $ hg forget i
501 $ rm i
501 $ rm i
502
502
503 amend
503 amend
504
504
505 $ echo amend >> a
505 $ echo amend >> a
506 $ echo amend >> b
506 $ echo amend >> b
507 $ hg -q commit -d '14 1' -m 'prepare amend'
507 $ hg -q commit -d '14 1' -m 'prepare amend'
508
508
509 $ hg --debug commit --amend -d '15 1' -m 'amend without changes' | grep keywords
509 $ hg --debug commit --amend -d '15 1' -m 'amend without changes' | grep keywords
510 overwriting a expanding keywords
510 overwriting a expanding keywords
511 $ hg -q id
511 $ hg -q id
512 577e60613a88
512 67d8c481a6be
513 $ head -1 a
513 $ head -1 a
514 expand $Id: a,v 577e60613a88 1970/01/01 00:00:15 test $
514 expand $Id: a,v 67d8c481a6be 1970/01/01 00:00:15 test $
515
515
516 $ hg -q strip -n tip
516 $ hg -q strip -n tip
517
517
518 Test patch queue repo
518 Test patch queue repo
519
519
520 $ hg init --mq
520 $ hg init --mq
521 $ hg qimport -r tip -n mqtest.diff
521 $ hg qimport -r tip -n mqtest.diff
522 $ hg commit --mq -m mqtest
522 $ hg commit --mq -m mqtest
523
523
524 Keywords should not be expanded in patch
524 Keywords should not be expanded in patch
525
525
526 $ cat .hg/patches/mqtest.diff
526 $ cat .hg/patches/mqtest.diff
527 # HG changeset patch
527 # HG changeset patch
528 # User User Name <user@example.com>
528 # User User Name <user@example.com>
529 # Date 1 0
529 # Date 1 0
530 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad
530 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad
531 # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9
531 # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9
532 cndiff
532 cndiff
533
533
534 diff -r ef63ca68695b -r 40a904bbbe4c c
534 diff -r ef63ca68695b -r 40a904bbbe4c c
535 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
535 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
536 +++ b/c Thu Jan 01 00:00:01 1970 +0000
536 +++ b/c Thu Jan 01 00:00:01 1970 +0000
537 @@ -0,0 +1,2 @@
537 @@ -0,0 +1,2 @@
538 +$Id$
538 +$Id$
539 +tests for different changenodes
539 +tests for different changenodes
540
540
541 $ hg qpop
541 $ hg qpop
542 popping mqtest.diff
542 popping mqtest.diff
543 patch queue now empty
543 patch queue now empty
544
544
545 qgoto, implying qpush, should expand
545 qgoto, implying qpush, should expand
546
546
547 $ hg qgoto mqtest.diff
547 $ hg qgoto mqtest.diff
548 applying mqtest.diff
548 applying mqtest.diff
549 now at: mqtest.diff
549 now at: mqtest.diff
550 $ cat c
550 $ cat c
551 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
551 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
552 tests for different changenodes
552 tests for different changenodes
553 $ hg cat c
553 $ hg cat c
554 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
554 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
555 tests for different changenodes
555 tests for different changenodes
556
556
557 Keywords should not be expanded in filelog
557 Keywords should not be expanded in filelog
558
558
559 $ hg --config 'extensions.keyword=!' cat c
559 $ hg --config 'extensions.keyword=!' cat c
560 $Id$
560 $Id$
561 tests for different changenodes
561 tests for different changenodes
562
562
563 qpop and move on
563 qpop and move on
564
564
565 $ hg qpop
565 $ hg qpop
566 popping mqtest.diff
566 popping mqtest.diff
567 patch queue now empty
567 patch queue now empty
568
568
569 Copy and show added kwfiles
569 Copy and show added kwfiles
570
570
571 $ hg cp a c
571 $ hg cp a c
572 $ hg kwfiles
572 $ hg kwfiles
573 a
573 a
574 c
574 c
575
575
576 Commit and show expansion in original and copy
576 Commit and show expansion in original and copy
577
577
578 $ hg --debug commit -ma2c -d '1 0' -u 'User Name <user@example.com>'
578 $ hg --debug commit -ma2c -d '1 0' -u 'User Name <user@example.com>'
579 c
579 c
580 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
580 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
581 removing unknown node 40a904bbbe4c from 1-phase boundary
581 removing unknown node 40a904bbbe4c from 1-phase boundary
582 overwriting c expanding keywords
582 overwriting c expanding keywords
583 committed changeset 2:25736cf2f5cbe41f6be4e6784ef6ecf9f3bbcc7d
583 committed changeset 2:25736cf2f5cbe41f6be4e6784ef6ecf9f3bbcc7d
584 $ cat a c
584 $ cat a c
585 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
585 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
586 do not process $Id:
586 do not process $Id:
587 xxx $
587 xxx $
588 expand $Id: c,v 25736cf2f5cb 1970/01/01 00:00:01 user $
588 expand $Id: c,v 25736cf2f5cb 1970/01/01 00:00:01 user $
589 do not process $Id:
589 do not process $Id:
590 xxx $
590 xxx $
591
591
592 Touch copied c and check its status
592 Touch copied c and check its status
593
593
594 $ touch c
594 $ touch c
595 $ hg status
595 $ hg status
596
596
597 Copy kwfile to keyword ignored file unexpanding keywords
597 Copy kwfile to keyword ignored file unexpanding keywords
598
598
599 $ hg --verbose copy a i
599 $ hg --verbose copy a i
600 copying a to i
600 copying a to i
601 overwriting i shrinking keywords
601 overwriting i shrinking keywords
602 $ head -n 1 i
602 $ head -n 1 i
603 expand $Id$
603 expand $Id$
604 $ hg forget i
604 $ hg forget i
605 $ rm i
605 $ rm i
606
606
607 Copy ignored file to ignored file: no overwriting
607 Copy ignored file to ignored file: no overwriting
608
608
609 $ hg --verbose copy b i
609 $ hg --verbose copy b i
610 copying b to i
610 copying b to i
611 $ hg forget i
611 $ hg forget i
612 $ rm i
612 $ rm i
613
613
614 cp symlink file; hg cp -A symlink file (part1)
614 cp symlink file; hg cp -A symlink file (part1)
615 - copied symlink points to kwfile: overwrite
615 - copied symlink points to kwfile: overwrite
616
616
617 #if symlink
617 #if symlink
618 $ cp sym i
618 $ cp sym i
619 $ ls -l i
619 $ ls -l i
620 -rw-r--r--* (glob)
620 -rw-r--r--* (glob)
621 $ head -1 i
621 $ head -1 i
622 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
622 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
623 $ hg copy --after --verbose sym i
623 $ hg copy --after --verbose sym i
624 copying sym to i
624 copying sym to i
625 overwriting i shrinking keywords
625 overwriting i shrinking keywords
626 $ head -1 i
626 $ head -1 i
627 expand $Id$
627 expand $Id$
628 $ hg forget i
628 $ hg forget i
629 $ rm i
629 $ rm i
630 #endif
630 #endif
631
631
632 Test different options of hg kwfiles
632 Test different options of hg kwfiles
633
633
634 $ hg kwfiles
634 $ hg kwfiles
635 a
635 a
636 c
636 c
637 $ hg -v kwfiles --ignore
637 $ hg -v kwfiles --ignore
638 I b
638 I b
639 I sym
639 I sym
640 $ hg kwfiles --all
640 $ hg kwfiles --all
641 K a
641 K a
642 K c
642 K c
643 I b
643 I b
644 I sym
644 I sym
645
645
646 Diff specific revision
646 Diff specific revision
647
647
648 $ hg diff --rev 1
648 $ hg diff --rev 1
649 diff -r ef63ca68695b c
649 diff -r ef63ca68695b c
650 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
650 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
651 +++ b/c * (glob)
651 +++ b/c * (glob)
652 @@ -0,0 +1,3 @@
652 @@ -0,0 +1,3 @@
653 +expand $Id$
653 +expand $Id$
654 +do not process $Id:
654 +do not process $Id:
655 +xxx $
655 +xxx $
656
656
657 Status after rollback:
657 Status after rollback:
658
658
659 $ hg rollback
659 $ hg rollback
660 repository tip rolled back to revision 1 (undo commit)
660 repository tip rolled back to revision 1 (undo commit)
661 working directory now based on revision 1
661 working directory now based on revision 1
662 $ hg status
662 $ hg status
663 A c
663 A c
664 $ hg update --clean
664 $ hg update --clean
665 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
665 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
666
666
667 #if symlink
667 #if symlink
668
668
669 cp symlink file; hg cp -A symlink file (part2)
669 cp symlink file; hg cp -A symlink file (part2)
670 - copied symlink points to kw ignored file: do not overwrite
670 - copied symlink points to kw ignored file: do not overwrite
671
671
672 $ cat a > i
672 $ cat a > i
673 $ ln -s i symignored
673 $ ln -s i symignored
674 $ hg commit -Am 'fake expansion in ignored and symlink' i symignored
674 $ hg commit -Am 'fake expansion in ignored and symlink' i symignored
675 $ cp symignored x
675 $ cp symignored x
676 $ hg copy --after --verbose symignored x
676 $ hg copy --after --verbose symignored x
677 copying symignored to x
677 copying symignored to x
678 $ head -n 1 x
678 $ head -n 1 x
679 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
679 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
680 $ hg forget x
680 $ hg forget x
681 $ rm x
681 $ rm x
682
682
683 $ hg rollback
683 $ hg rollback
684 repository tip rolled back to revision 1 (undo commit)
684 repository tip rolled back to revision 1 (undo commit)
685 working directory now based on revision 1
685 working directory now based on revision 1
686 $ hg update --clean
686 $ hg update --clean
687 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
687 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
688 $ rm i symignored
688 $ rm i symignored
689
689
690 #endif
690 #endif
691
691
692 Custom keywordmaps as argument to kwdemo
692 Custom keywordmaps as argument to kwdemo
693
693
694 $ hg --quiet kwdemo "Xinfo = {author}: {desc}"
694 $ hg --quiet kwdemo "Xinfo = {author}: {desc}"
695 [extensions]
695 [extensions]
696 keyword =
696 keyword =
697 [keyword]
697 [keyword]
698 ** =
698 ** =
699 b = ignore
699 b = ignore
700 demo.txt =
700 demo.txt =
701 i = ignore
701 i = ignore
702 [keywordset]
702 [keywordset]
703 svn = False
703 svn = False
704 [keywordmaps]
704 [keywordmaps]
705 Xinfo = {author}: {desc}
705 Xinfo = {author}: {desc}
706 $Xinfo: test: hg keyword configuration and expansion example $
706 $Xinfo: test: hg keyword configuration and expansion example $
707
707
708 Configure custom keywordmaps
708 Configure custom keywordmaps
709
709
710 $ cat <<EOF >>$HGRCPATH
710 $ cat <<EOF >>$HGRCPATH
711 > [keywordmaps]
711 > [keywordmaps]
712 > Id = {file} {node|short} {date|rfc822date} {author|user}
712 > Id = {file} {node|short} {date|rfc822date} {author|user}
713 > Xinfo = {author}: {desc}
713 > Xinfo = {author}: {desc}
714 > EOF
714 > EOF
715
715
716 Cat and hg cat files before custom expansion
716 Cat and hg cat files before custom expansion
717
717
718 $ cat a b
718 $ cat a b
719 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
719 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
720 do not process $Id:
720 do not process $Id:
721 xxx $
721 xxx $
722 ignore $Id$
722 ignore $Id$
723 $ hg cat sym a b && echo
723 $ hg cat sym a b && echo
724 expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $
724 expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $
725 do not process $Id:
725 do not process $Id:
726 xxx $
726 xxx $
727 ignore $Id$
727 ignore $Id$
728 a
728 a
729
729
730 Write custom keyword and prepare multi-line commit message
730 Write custom keyword and prepare multi-line commit message
731
731
732 $ echo '$Xinfo$' >> a
732 $ echo '$Xinfo$' >> a
733 $ cat <<EOF >> log
733 $ cat <<EOF >> log
734 > firstline
734 > firstline
735 > secondline
735 > secondline
736 > EOF
736 > EOF
737
737
738 Interrupted commit should not change state
738 Interrupted commit should not change state
739
739
740 $ hg commit
740 $ hg commit
741 abort: empty commit message
741 abort: empty commit message
742 [255]
742 [255]
743 $ hg status
743 $ hg status
744 M a
744 M a
745 ? c
745 ? c
746 ? log
746 ? log
747
747
748 Commit with multi-line message and custom expansion
748 Commit with multi-line message and custom expansion
749
749
750 $ hg --debug commit -l log -d '2 0' -u 'User Name <user@example.com>'
750 $ hg --debug commit -l log -d '2 0' -u 'User Name <user@example.com>'
751 a
751 a
752 removing unknown node 40a904bbbe4c from 1-phase boundary
752 removing unknown node 40a904bbbe4c from 1-phase boundary
753 overwriting a expanding keywords
753 overwriting a expanding keywords
754 committed changeset 2:bb948857c743469b22bbf51f7ec8112279ca5d83
754 committed changeset 2:bb948857c743469b22bbf51f7ec8112279ca5d83
755 $ rm log
755 $ rm log
756
756
757 Stat, verify and show custom expansion (firstline)
757 Stat, verify and show custom expansion (firstline)
758
758
759 $ hg status
759 $ hg status
760 ? c
760 ? c
761 $ hg verify
761 $ hg verify
762 checking changesets
762 checking changesets
763 checking manifests
763 checking manifests
764 crosschecking files in changesets and manifests
764 crosschecking files in changesets and manifests
765 checking files
765 checking files
766 3 files, 3 changesets, 4 total revisions
766 3 files, 3 changesets, 4 total revisions
767 $ cat a b
767 $ cat a b
768 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
768 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
769 do not process $Id:
769 do not process $Id:
770 xxx $
770 xxx $
771 $Xinfo: User Name <user@example.com>: firstline $
771 $Xinfo: User Name <user@example.com>: firstline $
772 ignore $Id$
772 ignore $Id$
773 $ hg cat sym a b && echo
773 $ hg cat sym a b && echo
774 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
774 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
775 do not process $Id:
775 do not process $Id:
776 xxx $
776 xxx $
777 $Xinfo: User Name <user@example.com>: firstline $
777 $Xinfo: User Name <user@example.com>: firstline $
778 ignore $Id$
778 ignore $Id$
779 a
779 a
780
780
781 annotate
781 annotate
782
782
783 $ hg annotate a
783 $ hg annotate a
784 1: expand $Id$
784 1: expand $Id$
785 1: do not process $Id:
785 1: do not process $Id:
786 1: xxx $
786 1: xxx $
787 2: $Xinfo$
787 2: $Xinfo$
788
788
789 remove with status checks
789 remove with status checks
790
790
791 $ hg debugrebuildstate
791 $ hg debugrebuildstate
792 $ hg remove a
792 $ hg remove a
793 $ hg --debug commit -m rma
793 $ hg --debug commit -m rma
794 committed changeset 3:d14c712653769de926994cf7fbb06c8fbd68f012
794 committed changeset 3:d14c712653769de926994cf7fbb06c8fbd68f012
795 $ hg status
795 $ hg status
796 ? c
796 ? c
797
797
798 Rollback, revert, and check expansion
798 Rollback, revert, and check expansion
799
799
800 $ hg rollback
800 $ hg rollback
801 repository tip rolled back to revision 2 (undo commit)
801 repository tip rolled back to revision 2 (undo commit)
802 working directory now based on revision 2
802 working directory now based on revision 2
803 $ hg status
803 $ hg status
804 R a
804 R a
805 ? c
805 ? c
806 $ hg revert --no-backup --rev tip a
806 $ hg revert --no-backup --rev tip a
807 $ cat a
807 $ cat a
808 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
808 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
809 do not process $Id:
809 do not process $Id:
810 xxx $
810 xxx $
811 $Xinfo: User Name <user@example.com>: firstline $
811 $Xinfo: User Name <user@example.com>: firstline $
812
812
813 Clone to test global and local configurations
813 Clone to test global and local configurations
814
814
815 $ cd ..
815 $ cd ..
816
816
817 Expansion in destination with global configuration
817 Expansion in destination with global configuration
818
818
819 $ hg --quiet clone Test globalconf
819 $ hg --quiet clone Test globalconf
820 $ cat globalconf/a
820 $ cat globalconf/a
821 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
821 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
822 do not process $Id:
822 do not process $Id:
823 xxx $
823 xxx $
824 $Xinfo: User Name <user@example.com>: firstline $
824 $Xinfo: User Name <user@example.com>: firstline $
825
825
826 No expansion in destination with local configuration in origin only
826 No expansion in destination with local configuration in origin only
827
827
828 $ hg --quiet --config 'keyword.**=ignore' clone Test localconf
828 $ hg --quiet --config 'keyword.**=ignore' clone Test localconf
829 $ cat localconf/a
829 $ cat localconf/a
830 expand $Id$
830 expand $Id$
831 do not process $Id:
831 do not process $Id:
832 xxx $
832 xxx $
833 $Xinfo$
833 $Xinfo$
834
834
835 Clone to test incoming
835 Clone to test incoming
836
836
837 $ hg clone -r1 Test Test-a
837 $ hg clone -r1 Test Test-a
838 adding changesets
838 adding changesets
839 adding manifests
839 adding manifests
840 adding file changes
840 adding file changes
841 added 2 changesets with 3 changes to 3 files
841 added 2 changesets with 3 changes to 3 files
842 updating to branch default
842 updating to branch default
843 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
843 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
844 $ cd Test-a
844 $ cd Test-a
845 $ cat <<EOF >> .hg/hgrc
845 $ cat <<EOF >> .hg/hgrc
846 > [paths]
846 > [paths]
847 > default = ../Test
847 > default = ../Test
848 > EOF
848 > EOF
849 $ hg incoming
849 $ hg incoming
850 comparing with $TESTTMP/Test (glob)
850 comparing with $TESTTMP/Test (glob)
851 searching for changes
851 searching for changes
852 changeset: 2:bb948857c743
852 changeset: 2:bb948857c743
853 tag: tip
853 tag: tip
854 user: User Name <user@example.com>
854 user: User Name <user@example.com>
855 date: Thu Jan 01 00:00:02 1970 +0000
855 date: Thu Jan 01 00:00:02 1970 +0000
856 summary: firstline
856 summary: firstline
857
857
858 Imported patch should not be rejected
858 Imported patch should not be rejected
859
859
860 >>> import re
860 >>> import re
861 >>> text = re.sub(r'(Id.*)', r'\1 rejecttest', open('a').read())
861 >>> text = re.sub(r'(Id.*)', r'\1 rejecttest', open('a').read())
862 >>> open('a', 'wb').write(text)
862 >>> open('a', 'wb').write(text)
863 $ hg --debug commit -m'rejects?' -d '3 0' -u 'User Name <user@example.com>'
863 $ hg --debug commit -m'rejects?' -d '3 0' -u 'User Name <user@example.com>'
864 a
864 a
865 overwriting a expanding keywords
865 overwriting a expanding keywords
866 committed changeset 2:85e279d709ffc28c9fdd1b868570985fc3d87082
866 committed changeset 2:85e279d709ffc28c9fdd1b868570985fc3d87082
867 $ hg export -o ../rejecttest.diff tip
867 $ hg export -o ../rejecttest.diff tip
868 $ cd ../Test
868 $ cd ../Test
869 $ hg import ../rejecttest.diff
869 $ hg import ../rejecttest.diff
870 applying ../rejecttest.diff
870 applying ../rejecttest.diff
871 $ cat a b
871 $ cat a b
872 expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
872 expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
873 do not process $Id: rejecttest
873 do not process $Id: rejecttest
874 xxx $
874 xxx $
875 $Xinfo: User Name <user@example.com>: rejects? $
875 $Xinfo: User Name <user@example.com>: rejects? $
876 ignore $Id$
876 ignore $Id$
877
877
878 $ hg rollback
878 $ hg rollback
879 repository tip rolled back to revision 2 (undo import)
879 repository tip rolled back to revision 2 (undo import)
880 working directory now based on revision 2
880 working directory now based on revision 2
881 $ hg update --clean
881 $ hg update --clean
882 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
882 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
883
883
884 kwexpand/kwshrink on selected files
884 kwexpand/kwshrink on selected files
885
885
886 $ mkdir x
886 $ mkdir x
887 $ hg copy a x/a
887 $ hg copy a x/a
888 $ hg --verbose kwshrink a
888 $ hg --verbose kwshrink a
889 overwriting a shrinking keywords
889 overwriting a shrinking keywords
890 - sleep required for dirstate.normal() check
890 - sleep required for dirstate.normal() check
891 $ sleep 1
891 $ sleep 1
892 $ hg status a
892 $ hg status a
893 $ hg --verbose kwexpand a
893 $ hg --verbose kwexpand a
894 overwriting a expanding keywords
894 overwriting a expanding keywords
895 $ hg status a
895 $ hg status a
896
896
897 kwexpand x/a should abort
897 kwexpand x/a should abort
898
898
899 $ hg --verbose kwexpand x/a
899 $ hg --verbose kwexpand x/a
900 abort: outstanding uncommitted changes
900 abort: outstanding uncommitted changes
901 [255]
901 [255]
902 $ cd x
902 $ cd x
903 $ hg --debug commit -m xa -d '3 0' -u 'User Name <user@example.com>'
903 $ hg --debug commit -m xa -d '3 0' -u 'User Name <user@example.com>'
904 x/a
904 x/a
905 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
905 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
906 overwriting x/a expanding keywords
906 overwriting x/a expanding keywords
907 committed changeset 3:b4560182a3f9a358179fd2d835c15e9da379c1e4
907 committed changeset 3:b4560182a3f9a358179fd2d835c15e9da379c1e4
908 $ cat a
908 $ cat a
909 expand $Id: x/a b4560182a3f9 Thu, 01 Jan 1970 00:00:03 +0000 user $
909 expand $Id: x/a b4560182a3f9 Thu, 01 Jan 1970 00:00:03 +0000 user $
910 do not process $Id:
910 do not process $Id:
911 xxx $
911 xxx $
912 $Xinfo: User Name <user@example.com>: xa $
912 $Xinfo: User Name <user@example.com>: xa $
913
913
914 kwshrink a inside directory x
914 kwshrink a inside directory x
915
915
916 $ hg --verbose kwshrink a
916 $ hg --verbose kwshrink a
917 overwriting x/a shrinking keywords
917 overwriting x/a shrinking keywords
918 $ cat a
918 $ cat a
919 expand $Id$
919 expand $Id$
920 do not process $Id:
920 do not process $Id:
921 xxx $
921 xxx $
922 $Xinfo$
922 $Xinfo$
923 $ cd ..
923 $ cd ..
924
924
925 kwexpand nonexistent
925 kwexpand nonexistent
926
926
927 $ hg kwexpand nonexistent
927 $ hg kwexpand nonexistent
928 nonexistent:* (glob)
928 nonexistent:* (glob)
929
929
930
930
931 #if serve
931 #if serve
932 hg serve
932 hg serve
933 - expand with hgweb file
933 - expand with hgweb file
934 - no expansion with hgweb annotate/changeset/filediff
934 - no expansion with hgweb annotate/changeset/filediff
935 - check errors
935 - check errors
936
936
937 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
937 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
938 $ cat hg.pid >> $DAEMON_PIDS
938 $ cat hg.pid >> $DAEMON_PIDS
939 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/a/?style=raw'
939 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/a/?style=raw'
940 200 Script output follows
940 200 Script output follows
941
941
942 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
942 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
943 do not process $Id:
943 do not process $Id:
944 xxx $
944 xxx $
945 $Xinfo: User Name <user@example.com>: firstline $
945 $Xinfo: User Name <user@example.com>: firstline $
946 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'annotate/tip/a/?style=raw'
946 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'annotate/tip/a/?style=raw'
947 200 Script output follows
947 200 Script output follows
948
948
949
949
950 user@1: expand $Id$
950 user@1: expand $Id$
951 user@1: do not process $Id:
951 user@1: do not process $Id:
952 user@1: xxx $
952 user@1: xxx $
953 user@2: $Xinfo$
953 user@2: $Xinfo$
954
954
955
955
956
956
957
957
958 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'rev/tip/?style=raw'
958 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'rev/tip/?style=raw'
959 200 Script output follows
959 200 Script output follows
960
960
961
961
962 # HG changeset patch
962 # HG changeset patch
963 # User User Name <user@example.com>
963 # User User Name <user@example.com>
964 # Date 3 0
964 # Date 3 0
965 # Node ID b4560182a3f9a358179fd2d835c15e9da379c1e4
965 # Node ID b4560182a3f9a358179fd2d835c15e9da379c1e4
966 # Parent bb948857c743469b22bbf51f7ec8112279ca5d83
966 # Parent bb948857c743469b22bbf51f7ec8112279ca5d83
967 xa
967 xa
968
968
969 diff -r bb948857c743 -r b4560182a3f9 x/a
969 diff -r bb948857c743 -r b4560182a3f9 x/a
970 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
970 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
971 +++ b/x/a Thu Jan 01 00:00:03 1970 +0000
971 +++ b/x/a Thu Jan 01 00:00:03 1970 +0000
972 @@ -0,0 +1,4 @@
972 @@ -0,0 +1,4 @@
973 +expand $Id$
973 +expand $Id$
974 +do not process $Id:
974 +do not process $Id:
975 +xxx $
975 +xxx $
976 +$Xinfo$
976 +$Xinfo$
977
977
978 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'diff/bb948857c743/a?style=raw'
978 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'diff/bb948857c743/a?style=raw'
979 200 Script output follows
979 200 Script output follows
980
980
981
981
982 diff -r ef63ca68695b -r bb948857c743 a
982 diff -r ef63ca68695b -r bb948857c743 a
983 --- a/a Thu Jan 01 00:00:00 1970 +0000
983 --- a/a Thu Jan 01 00:00:00 1970 +0000
984 +++ b/a Thu Jan 01 00:00:02 1970 +0000
984 +++ b/a Thu Jan 01 00:00:02 1970 +0000
985 @@ -1,3 +1,4 @@
985 @@ -1,3 +1,4 @@
986 expand $Id$
986 expand $Id$
987 do not process $Id:
987 do not process $Id:
988 xxx $
988 xxx $
989 +$Xinfo$
989 +$Xinfo$
990
990
991
991
992
992
993
993
994 $ cat errors.log
994 $ cat errors.log
995 #endif
995 #endif
996
996
997 Prepare merge and resolve tests
997 Prepare merge and resolve tests
998
998
999 $ echo '$Id$' > m
999 $ echo '$Id$' > m
1000 $ hg add m
1000 $ hg add m
1001 $ hg commit -m 4kw
1001 $ hg commit -m 4kw
1002 $ echo foo >> m
1002 $ echo foo >> m
1003 $ hg commit -m 5foo
1003 $ hg commit -m 5foo
1004
1004
1005 simplemerge
1005 simplemerge
1006
1006
1007 $ hg update 4
1007 $ hg update 4
1008 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1008 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1009 $ echo foo >> m
1009 $ echo foo >> m
1010 $ hg commit -m 6foo
1010 $ hg commit -m 6foo
1011 created new head
1011 created new head
1012 $ hg merge
1012 $ hg merge
1013 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1013 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1014 (branch merge, don't forget to commit)
1014 (branch merge, don't forget to commit)
1015 $ hg commit -m simplemerge
1015 $ hg commit -m simplemerge
1016 $ cat m
1016 $ cat m
1017 $Id: m 27d48ee14f67 Thu, 01 Jan 1970 00:00:00 +0000 test $
1017 $Id: m 27d48ee14f67 Thu, 01 Jan 1970 00:00:00 +0000 test $
1018 foo
1018 foo
1019
1019
1020 conflict: keyword should stay outside conflict zone
1020 conflict: keyword should stay outside conflict zone
1021
1021
1022 $ hg update 4
1022 $ hg update 4
1023 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1023 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1024 $ echo bar >> m
1024 $ echo bar >> m
1025 $ hg commit -m 8bar
1025 $ hg commit -m 8bar
1026 created new head
1026 created new head
1027 $ hg merge
1027 $ hg merge
1028 merging m
1028 merging m
1029 warning: conflicts during merge.
1029 warning: conflicts during merge.
1030 merging m incomplete! (edit conflicts, then use 'hg resolve --mark')
1030 merging m incomplete! (edit conflicts, then use 'hg resolve --mark')
1031 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1031 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1032 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
1032 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
1033 [1]
1033 [1]
1034 $ cat m
1034 $ cat m
1035 $Id$
1035 $Id$
1036 <<<<<<< local
1036 <<<<<<< local
1037 bar
1037 bar
1038 =======
1038 =======
1039 foo
1039 foo
1040 >>>>>>> other
1040 >>>>>>> other
1041
1041
1042 resolve to local
1042 resolve to local
1043
1043
1044 $ HGMERGE=internal:local hg resolve -a
1044 $ HGMERGE=internal:local hg resolve -a
1045 $ hg commit -m localresolve
1045 $ hg commit -m localresolve
1046 $ cat m
1046 $ cat m
1047 $Id: m 800511b3a22d Thu, 01 Jan 1970 00:00:00 +0000 test $
1047 $Id: m 800511b3a22d Thu, 01 Jan 1970 00:00:00 +0000 test $
1048 bar
1048 bar
1049
1049
1050 Test restricted mode with transplant -b
1050 Test restricted mode with transplant -b
1051
1051
1052 $ hg update 6
1052 $ hg update 6
1053 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1053 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1054 $ hg branch foo
1054 $ hg branch foo
1055 marked working directory as branch foo
1055 marked working directory as branch foo
1056 (branches are permanent and global, did you want a bookmark?)
1056 (branches are permanent and global, did you want a bookmark?)
1057 $ mv a a.bak
1057 $ mv a a.bak
1058 $ echo foobranch > a
1058 $ echo foobranch > a
1059 $ cat a.bak >> a
1059 $ cat a.bak >> a
1060 $ rm a.bak
1060 $ rm a.bak
1061 $ hg commit -m 9foobranch
1061 $ hg commit -m 9foobranch
1062 $ hg update default
1062 $ hg update default
1063 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1063 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1064 $ hg -y transplant -b foo tip
1064 $ hg -y transplant -b foo tip
1065 applying 4aa30d025d50
1065 applying 4aa30d025d50
1066 4aa30d025d50 transplanted to e00abbf63521
1066 4aa30d025d50 transplanted to e00abbf63521
1067
1067
1068 Expansion in changeset but not in file
1068 Expansion in changeset but not in file
1069
1069
1070 $ hg tip -p
1070 $ hg tip -p
1071 changeset: 11:e00abbf63521
1071 changeset: 11:e00abbf63521
1072 tag: tip
1072 tag: tip
1073 parent: 9:800511b3a22d
1073 parent: 9:800511b3a22d
1074 user: test
1074 user: test
1075 date: Thu Jan 01 00:00:00 1970 +0000
1075 date: Thu Jan 01 00:00:00 1970 +0000
1076 summary: 9foobranch
1076 summary: 9foobranch
1077
1077
1078 diff -r 800511b3a22d -r e00abbf63521 a
1078 diff -r 800511b3a22d -r e00abbf63521 a
1079 --- a/a Thu Jan 01 00:00:00 1970 +0000
1079 --- a/a Thu Jan 01 00:00:00 1970 +0000
1080 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1080 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1081 @@ -1,3 +1,4 @@
1081 @@ -1,3 +1,4 @@
1082 +foobranch
1082 +foobranch
1083 expand $Id$
1083 expand $Id$
1084 do not process $Id:
1084 do not process $Id:
1085 xxx $
1085 xxx $
1086
1086
1087 $ head -n 2 a
1087 $ head -n 2 a
1088 foobranch
1088 foobranch
1089 expand $Id: a e00abbf63521 Thu, 01 Jan 1970 00:00:00 +0000 test $
1089 expand $Id: a e00abbf63521 Thu, 01 Jan 1970 00:00:00 +0000 test $
1090
1090
1091 Turn off expansion
1091 Turn off expansion
1092
1092
1093 $ hg -q rollback
1093 $ hg -q rollback
1094 $ hg -q update -C
1094 $ hg -q update -C
1095
1095
1096 kwshrink with unknown file u
1096 kwshrink with unknown file u
1097
1097
1098 $ cp a u
1098 $ cp a u
1099 $ hg --verbose kwshrink
1099 $ hg --verbose kwshrink
1100 overwriting a shrinking keywords
1100 overwriting a shrinking keywords
1101 overwriting m shrinking keywords
1101 overwriting m shrinking keywords
1102 overwriting x/a shrinking keywords
1102 overwriting x/a shrinking keywords
1103
1103
1104 Keywords shrunk in working directory, but not yet disabled
1104 Keywords shrunk in working directory, but not yet disabled
1105 - cat shows unexpanded keywords
1105 - cat shows unexpanded keywords
1106 - hg cat shows expanded keywords
1106 - hg cat shows expanded keywords
1107
1107
1108 $ cat a b
1108 $ cat a b
1109 expand $Id$
1109 expand $Id$
1110 do not process $Id:
1110 do not process $Id:
1111 xxx $
1111 xxx $
1112 $Xinfo$
1112 $Xinfo$
1113 ignore $Id$
1113 ignore $Id$
1114 $ hg cat sym a b && echo
1114 $ hg cat sym a b && echo
1115 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
1115 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
1116 do not process $Id:
1116 do not process $Id:
1117 xxx $
1117 xxx $
1118 $Xinfo: User Name <user@example.com>: firstline $
1118 $Xinfo: User Name <user@example.com>: firstline $
1119 ignore $Id$
1119 ignore $Id$
1120 a
1120 a
1121
1121
1122 Now disable keyword expansion
1122 Now disable keyword expansion
1123
1123
1124 $ rm "$HGRCPATH"
1124 $ rm "$HGRCPATH"
1125 $ cat a b
1125 $ cat a b
1126 expand $Id$
1126 expand $Id$
1127 do not process $Id:
1127 do not process $Id:
1128 xxx $
1128 xxx $
1129 $Xinfo$
1129 $Xinfo$
1130 ignore $Id$
1130 ignore $Id$
1131 $ hg cat sym a b && echo
1131 $ hg cat sym a b && echo
1132 expand $Id$
1132 expand $Id$
1133 do not process $Id:
1133 do not process $Id:
1134 xxx $
1134 xxx $
1135 $Xinfo$
1135 $Xinfo$
1136 ignore $Id$
1136 ignore $Id$
1137 a
1137 a
1138
1138
1139 $ cd ..
1139 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now