##// END OF EJS Templates
changeset_printer: display changeset phase on debug level...
Pierre-Yves David -
r15907:51fc4325 default
parent child Browse files
Show More
@@ -1,1274 +1,1277 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
13 import subrepo
14
14
15 def parsealiases(cmd):
15 def parsealiases(cmd):
16 return cmd.lstrip("^").split("|")
16 return cmd.lstrip("^").split("|")
17
17
18 def findpossible(cmd, table, strict=False):
18 def findpossible(cmd, table, strict=False):
19 """
19 """
20 Return cmd -> (aliases, command table entry)
20 Return cmd -> (aliases, command table entry)
21 for each matching command.
21 for each matching command.
22 Return debug commands (or their aliases) only if no normal command matches.
22 Return debug commands (or their aliases) only if no normal command matches.
23 """
23 """
24 choice = {}
24 choice = {}
25 debugchoice = {}
25 debugchoice = {}
26
26
27 if cmd in table:
27 if cmd in table:
28 # short-circuit exact matches, "log" alias beats "^log|history"
28 # short-circuit exact matches, "log" alias beats "^log|history"
29 keys = [cmd]
29 keys = [cmd]
30 else:
30 else:
31 keys = table.keys()
31 keys = table.keys()
32
32
33 for e in keys:
33 for e in keys:
34 aliases = parsealiases(e)
34 aliases = parsealiases(e)
35 found = None
35 found = None
36 if cmd in aliases:
36 if cmd in aliases:
37 found = cmd
37 found = cmd
38 elif not strict:
38 elif not strict:
39 for a in aliases:
39 for a in aliases:
40 if a.startswith(cmd):
40 if a.startswith(cmd):
41 found = a
41 found = a
42 break
42 break
43 if found is not None:
43 if found is not None:
44 if aliases[0].startswith("debug") or found.startswith("debug"):
44 if aliases[0].startswith("debug") or found.startswith("debug"):
45 debugchoice[found] = (aliases, table[e])
45 debugchoice[found] = (aliases, table[e])
46 else:
46 else:
47 choice[found] = (aliases, table[e])
47 choice[found] = (aliases, table[e])
48
48
49 if not choice and debugchoice:
49 if not choice and debugchoice:
50 choice = debugchoice
50 choice = debugchoice
51
51
52 return choice
52 return choice
53
53
54 def findcmd(cmd, table, strict=True):
54 def findcmd(cmd, table, strict=True):
55 """Return (aliases, command table entry) for command string."""
55 """Return (aliases, command table entry) for command string."""
56 choice = findpossible(cmd, table, strict)
56 choice = findpossible(cmd, table, strict)
57
57
58 if cmd in choice:
58 if cmd in choice:
59 return choice[cmd]
59 return choice[cmd]
60
60
61 if len(choice) > 1:
61 if len(choice) > 1:
62 clist = choice.keys()
62 clist = choice.keys()
63 clist.sort()
63 clist.sort()
64 raise error.AmbiguousCommand(cmd, clist)
64 raise error.AmbiguousCommand(cmd, clist)
65
65
66 if choice:
66 if choice:
67 return choice.values()[0]
67 return choice.values()[0]
68
68
69 raise error.UnknownCommand(cmd)
69 raise error.UnknownCommand(cmd)
70
70
71 def findrepo(p):
71 def findrepo(p):
72 while not os.path.isdir(os.path.join(p, ".hg")):
72 while not os.path.isdir(os.path.join(p, ".hg")):
73 oldp, p = p, os.path.dirname(p)
73 oldp, p = p, os.path.dirname(p)
74 if p == oldp:
74 if p == oldp:
75 return None
75 return None
76
76
77 return p
77 return p
78
78
79 def bailifchanged(repo):
79 def bailifchanged(repo):
80 if repo.dirstate.p2() != nullid:
80 if repo.dirstate.p2() != nullid:
81 raise util.Abort(_('outstanding uncommitted merge'))
81 raise util.Abort(_('outstanding uncommitted merge'))
82 modified, added, removed, deleted = repo.status()[:4]
82 modified, added, removed, deleted = repo.status()[:4]
83 if modified or added or removed or deleted:
83 if modified or added or removed or deleted:
84 raise util.Abort(_("outstanding uncommitted changes"))
84 raise util.Abort(_("outstanding uncommitted changes"))
85 ctx = repo[None]
85 ctx = repo[None]
86 for s in ctx.substate:
86 for s in ctx.substate:
87 if ctx.sub(s).dirty():
87 if ctx.sub(s).dirty():
88 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
88 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
89
89
90 def logmessage(ui, opts):
90 def logmessage(ui, opts):
91 """ get the log message according to -m and -l option """
91 """ get the log message according to -m and -l option """
92 message = opts.get('message')
92 message = opts.get('message')
93 logfile = opts.get('logfile')
93 logfile = opts.get('logfile')
94
94
95 if message and logfile:
95 if message and logfile:
96 raise util.Abort(_('options --message and --logfile are mutually '
96 raise util.Abort(_('options --message and --logfile are mutually '
97 'exclusive'))
97 'exclusive'))
98 if not message and logfile:
98 if not message and logfile:
99 try:
99 try:
100 if logfile == '-':
100 if logfile == '-':
101 message = ui.fin.read()
101 message = ui.fin.read()
102 else:
102 else:
103 message = '\n'.join(util.readfile(logfile).splitlines())
103 message = '\n'.join(util.readfile(logfile).splitlines())
104 except IOError, inst:
104 except IOError, inst:
105 raise util.Abort(_("can't read commit message '%s': %s") %
105 raise util.Abort(_("can't read commit message '%s': %s") %
106 (logfile, inst.strerror))
106 (logfile, inst.strerror))
107 return message
107 return message
108
108
109 def loglimit(opts):
109 def loglimit(opts):
110 """get the log limit according to option -l/--limit"""
110 """get the log limit according to option -l/--limit"""
111 limit = opts.get('limit')
111 limit = opts.get('limit')
112 if limit:
112 if limit:
113 try:
113 try:
114 limit = int(limit)
114 limit = int(limit)
115 except ValueError:
115 except ValueError:
116 raise util.Abort(_('limit must be a positive integer'))
116 raise util.Abort(_('limit must be a positive integer'))
117 if limit <= 0:
117 if limit <= 0:
118 raise util.Abort(_('limit must be positive'))
118 raise util.Abort(_('limit must be positive'))
119 else:
119 else:
120 limit = None
120 limit = None
121 return limit
121 return limit
122
122
123 def makefilename(repo, pat, node, desc=None,
123 def makefilename(repo, pat, node, desc=None,
124 total=None, seqno=None, revwidth=None, pathname=None):
124 total=None, seqno=None, revwidth=None, pathname=None):
125 node_expander = {
125 node_expander = {
126 'H': lambda: hex(node),
126 'H': lambda: hex(node),
127 'R': lambda: str(repo.changelog.rev(node)),
127 'R': lambda: str(repo.changelog.rev(node)),
128 'h': lambda: short(node),
128 'h': lambda: short(node),
129 'm': lambda: re.sub('[^\w]', '_', str(desc))
129 'm': lambda: re.sub('[^\w]', '_', str(desc))
130 }
130 }
131 expander = {
131 expander = {
132 '%': lambda: '%',
132 '%': lambda: '%',
133 'b': lambda: os.path.basename(repo.root),
133 'b': lambda: os.path.basename(repo.root),
134 }
134 }
135
135
136 try:
136 try:
137 if node:
137 if node:
138 expander.update(node_expander)
138 expander.update(node_expander)
139 if node:
139 if node:
140 expander['r'] = (lambda:
140 expander['r'] = (lambda:
141 str(repo.changelog.rev(node)).zfill(revwidth or 0))
141 str(repo.changelog.rev(node)).zfill(revwidth or 0))
142 if total is not None:
142 if total is not None:
143 expander['N'] = lambda: str(total)
143 expander['N'] = lambda: str(total)
144 if seqno is not None:
144 if seqno is not None:
145 expander['n'] = lambda: str(seqno)
145 expander['n'] = lambda: str(seqno)
146 if total is not None and seqno is not None:
146 if total is not None and seqno is not None:
147 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
147 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
148 if pathname is not None:
148 if pathname is not None:
149 expander['s'] = lambda: os.path.basename(pathname)
149 expander['s'] = lambda: os.path.basename(pathname)
150 expander['d'] = lambda: os.path.dirname(pathname) or '.'
150 expander['d'] = lambda: os.path.dirname(pathname) or '.'
151 expander['p'] = lambda: pathname
151 expander['p'] = lambda: pathname
152
152
153 newname = []
153 newname = []
154 patlen = len(pat)
154 patlen = len(pat)
155 i = 0
155 i = 0
156 while i < patlen:
156 while i < patlen:
157 c = pat[i]
157 c = pat[i]
158 if c == '%':
158 if c == '%':
159 i += 1
159 i += 1
160 c = pat[i]
160 c = pat[i]
161 c = expander[c]()
161 c = expander[c]()
162 newname.append(c)
162 newname.append(c)
163 i += 1
163 i += 1
164 return ''.join(newname)
164 return ''.join(newname)
165 except KeyError, inst:
165 except KeyError, inst:
166 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
166 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
167 inst.args[0])
167 inst.args[0])
168
168
169 def makefileobj(repo, pat, node=None, desc=None, total=None,
169 def makefileobj(repo, pat, node=None, desc=None, total=None,
170 seqno=None, revwidth=None, mode='wb', pathname=None):
170 seqno=None, revwidth=None, mode='wb', pathname=None):
171
171
172 writable = mode not in ('r', 'rb')
172 writable = mode not in ('r', 'rb')
173
173
174 if not pat or pat == '-':
174 if not pat or pat == '-':
175 fp = writable and repo.ui.fout or repo.ui.fin
175 fp = writable and repo.ui.fout or repo.ui.fin
176 if util.safehasattr(fp, 'fileno'):
176 if util.safehasattr(fp, 'fileno'):
177 return os.fdopen(os.dup(fp.fileno()), mode)
177 return os.fdopen(os.dup(fp.fileno()), mode)
178 else:
178 else:
179 # if this fp can't be duped properly, return
179 # if this fp can't be duped properly, return
180 # a dummy object that can be closed
180 # a dummy object that can be closed
181 class wrappedfileobj(object):
181 class wrappedfileobj(object):
182 noop = lambda x: None
182 noop = lambda x: None
183 def __init__(self, f):
183 def __init__(self, f):
184 self.f = f
184 self.f = f
185 def __getattr__(self, attr):
185 def __getattr__(self, attr):
186 if attr == 'close':
186 if attr == 'close':
187 return self.noop
187 return self.noop
188 else:
188 else:
189 return getattr(self.f, attr)
189 return getattr(self.f, attr)
190
190
191 return wrappedfileobj(fp)
191 return wrappedfileobj(fp)
192 if util.safehasattr(pat, 'write') and writable:
192 if util.safehasattr(pat, 'write') and writable:
193 return pat
193 return pat
194 if util.safehasattr(pat, 'read') and 'r' in mode:
194 if util.safehasattr(pat, 'read') and 'r' in mode:
195 return pat
195 return pat
196 return open(makefilename(repo, pat, node, desc, total, seqno, revwidth,
196 return open(makefilename(repo, pat, node, desc, total, seqno, revwidth,
197 pathname),
197 pathname),
198 mode)
198 mode)
199
199
200 def openrevlog(repo, cmd, file_, opts):
200 def openrevlog(repo, cmd, file_, opts):
201 """opens the changelog, manifest, a filelog or a given revlog"""
201 """opens the changelog, manifest, a filelog or a given revlog"""
202 cl = opts['changelog']
202 cl = opts['changelog']
203 mf = opts['manifest']
203 mf = opts['manifest']
204 msg = None
204 msg = None
205 if cl and mf:
205 if cl and mf:
206 msg = _('cannot specify --changelog and --manifest at the same time')
206 msg = _('cannot specify --changelog and --manifest at the same time')
207 elif cl or mf:
207 elif cl or mf:
208 if file_:
208 if file_:
209 msg = _('cannot specify filename with --changelog or --manifest')
209 msg = _('cannot specify filename with --changelog or --manifest')
210 elif not repo:
210 elif not repo:
211 msg = _('cannot specify --changelog or --manifest '
211 msg = _('cannot specify --changelog or --manifest '
212 'without a repository')
212 'without a repository')
213 if msg:
213 if msg:
214 raise util.Abort(msg)
214 raise util.Abort(msg)
215
215
216 r = None
216 r = None
217 if repo:
217 if repo:
218 if cl:
218 if cl:
219 r = repo.changelog
219 r = repo.changelog
220 elif mf:
220 elif mf:
221 r = repo.manifest
221 r = repo.manifest
222 elif file_:
222 elif file_:
223 filelog = repo.file(file_)
223 filelog = repo.file(file_)
224 if len(filelog):
224 if len(filelog):
225 r = filelog
225 r = filelog
226 if not r:
226 if not r:
227 if not file_:
227 if not file_:
228 raise error.CommandError(cmd, _('invalid arguments'))
228 raise error.CommandError(cmd, _('invalid arguments'))
229 if not os.path.isfile(file_):
229 if not os.path.isfile(file_):
230 raise util.Abort(_("revlog '%s' not found") % file_)
230 raise util.Abort(_("revlog '%s' not found") % file_)
231 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
231 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
232 file_[:-2] + ".i")
232 file_[:-2] + ".i")
233 return r
233 return r
234
234
235 def copy(ui, repo, pats, opts, rename=False):
235 def copy(ui, repo, pats, opts, rename=False):
236 # called with the repo lock held
236 # called with the repo lock held
237 #
237 #
238 # hgsep => pathname that uses "/" to separate directories
238 # hgsep => pathname that uses "/" to separate directories
239 # ossep => pathname that uses os.sep to separate directories
239 # ossep => pathname that uses os.sep to separate directories
240 cwd = repo.getcwd()
240 cwd = repo.getcwd()
241 targets = {}
241 targets = {}
242 after = opts.get("after")
242 after = opts.get("after")
243 dryrun = opts.get("dry_run")
243 dryrun = opts.get("dry_run")
244 wctx = repo[None]
244 wctx = repo[None]
245
245
246 def walkpat(pat):
246 def walkpat(pat):
247 srcs = []
247 srcs = []
248 badstates = after and '?' or '?r'
248 badstates = after and '?' or '?r'
249 m = scmutil.match(repo[None], [pat], opts, globbed=True)
249 m = scmutil.match(repo[None], [pat], opts, globbed=True)
250 for abs in repo.walk(m):
250 for abs in repo.walk(m):
251 state = repo.dirstate[abs]
251 state = repo.dirstate[abs]
252 rel = m.rel(abs)
252 rel = m.rel(abs)
253 exact = m.exact(abs)
253 exact = m.exact(abs)
254 if state in badstates:
254 if state in badstates:
255 if exact and state == '?':
255 if exact and state == '?':
256 ui.warn(_('%s: not copying - file is not managed\n') % rel)
256 ui.warn(_('%s: not copying - file is not managed\n') % rel)
257 if exact and state == 'r':
257 if exact and state == 'r':
258 ui.warn(_('%s: not copying - file has been marked for'
258 ui.warn(_('%s: not copying - file has been marked for'
259 ' remove\n') % rel)
259 ' remove\n') % rel)
260 continue
260 continue
261 # abs: hgsep
261 # abs: hgsep
262 # rel: ossep
262 # rel: ossep
263 srcs.append((abs, rel, exact))
263 srcs.append((abs, rel, exact))
264 return srcs
264 return srcs
265
265
266 # abssrc: hgsep
266 # abssrc: hgsep
267 # relsrc: ossep
267 # relsrc: ossep
268 # otarget: ossep
268 # otarget: ossep
269 def copyfile(abssrc, relsrc, otarget, exact):
269 def copyfile(abssrc, relsrc, otarget, exact):
270 abstarget = scmutil.canonpath(repo.root, cwd, otarget)
270 abstarget = scmutil.canonpath(repo.root, cwd, otarget)
271 reltarget = repo.pathto(abstarget, cwd)
271 reltarget = repo.pathto(abstarget, cwd)
272 target = repo.wjoin(abstarget)
272 target = repo.wjoin(abstarget)
273 src = repo.wjoin(abssrc)
273 src = repo.wjoin(abssrc)
274 state = repo.dirstate[abstarget]
274 state = repo.dirstate[abstarget]
275
275
276 scmutil.checkportable(ui, abstarget)
276 scmutil.checkportable(ui, abstarget)
277
277
278 # check for collisions
278 # check for collisions
279 prevsrc = targets.get(abstarget)
279 prevsrc = targets.get(abstarget)
280 if prevsrc is not None:
280 if prevsrc is not None:
281 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
281 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
282 (reltarget, repo.pathto(abssrc, cwd),
282 (reltarget, repo.pathto(abssrc, cwd),
283 repo.pathto(prevsrc, cwd)))
283 repo.pathto(prevsrc, cwd)))
284 return
284 return
285
285
286 # check for overwrites
286 # check for overwrites
287 exists = os.path.lexists(target)
287 exists = os.path.lexists(target)
288 if not after and exists or after and state in 'mn':
288 if not after and exists or after and state in 'mn':
289 if not opts['force']:
289 if not opts['force']:
290 ui.warn(_('%s: not overwriting - file exists\n') %
290 ui.warn(_('%s: not overwriting - file exists\n') %
291 reltarget)
291 reltarget)
292 return
292 return
293
293
294 if after:
294 if after:
295 if not exists:
295 if not exists:
296 if rename:
296 if rename:
297 ui.warn(_('%s: not recording move - %s does not exist\n') %
297 ui.warn(_('%s: not recording move - %s does not exist\n') %
298 (relsrc, reltarget))
298 (relsrc, reltarget))
299 else:
299 else:
300 ui.warn(_('%s: not recording copy - %s does not exist\n') %
300 ui.warn(_('%s: not recording copy - %s does not exist\n') %
301 (relsrc, reltarget))
301 (relsrc, reltarget))
302 return
302 return
303 elif not dryrun:
303 elif not dryrun:
304 try:
304 try:
305 if exists:
305 if exists:
306 os.unlink(target)
306 os.unlink(target)
307 targetdir = os.path.dirname(target) or '.'
307 targetdir = os.path.dirname(target) or '.'
308 if not os.path.isdir(targetdir):
308 if not os.path.isdir(targetdir):
309 os.makedirs(targetdir)
309 os.makedirs(targetdir)
310 util.copyfile(src, target)
310 util.copyfile(src, target)
311 srcexists = True
311 srcexists = True
312 except IOError, inst:
312 except IOError, inst:
313 if inst.errno == errno.ENOENT:
313 if inst.errno == errno.ENOENT:
314 ui.warn(_('%s: deleted in working copy\n') % relsrc)
314 ui.warn(_('%s: deleted in working copy\n') % relsrc)
315 srcexists = False
315 srcexists = False
316 else:
316 else:
317 ui.warn(_('%s: cannot copy - %s\n') %
317 ui.warn(_('%s: cannot copy - %s\n') %
318 (relsrc, inst.strerror))
318 (relsrc, inst.strerror))
319 return True # report a failure
319 return True # report a failure
320
320
321 if ui.verbose or not exact:
321 if ui.verbose or not exact:
322 if rename:
322 if rename:
323 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
323 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
324 else:
324 else:
325 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
325 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
326
326
327 targets[abstarget] = abssrc
327 targets[abstarget] = abssrc
328
328
329 # fix up dirstate
329 # fix up dirstate
330 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
330 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
331 dryrun=dryrun, cwd=cwd)
331 dryrun=dryrun, cwd=cwd)
332 if rename and not dryrun:
332 if rename and not dryrun:
333 if not after and srcexists:
333 if not after and srcexists:
334 util.unlinkpath(repo.wjoin(abssrc))
334 util.unlinkpath(repo.wjoin(abssrc))
335 wctx.forget([abssrc])
335 wctx.forget([abssrc])
336
336
337 # pat: ossep
337 # pat: ossep
338 # dest ossep
338 # dest ossep
339 # srcs: list of (hgsep, hgsep, ossep, bool)
339 # srcs: list of (hgsep, hgsep, ossep, bool)
340 # return: function that takes hgsep and returns ossep
340 # return: function that takes hgsep and returns ossep
341 def targetpathfn(pat, dest, srcs):
341 def targetpathfn(pat, dest, srcs):
342 if os.path.isdir(pat):
342 if os.path.isdir(pat):
343 abspfx = scmutil.canonpath(repo.root, cwd, pat)
343 abspfx = scmutil.canonpath(repo.root, cwd, pat)
344 abspfx = util.localpath(abspfx)
344 abspfx = util.localpath(abspfx)
345 if destdirexists:
345 if destdirexists:
346 striplen = len(os.path.split(abspfx)[0])
346 striplen = len(os.path.split(abspfx)[0])
347 else:
347 else:
348 striplen = len(abspfx)
348 striplen = len(abspfx)
349 if striplen:
349 if striplen:
350 striplen += len(os.sep)
350 striplen += len(os.sep)
351 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
351 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
352 elif destdirexists:
352 elif destdirexists:
353 res = lambda p: os.path.join(dest,
353 res = lambda p: os.path.join(dest,
354 os.path.basename(util.localpath(p)))
354 os.path.basename(util.localpath(p)))
355 else:
355 else:
356 res = lambda p: dest
356 res = lambda p: dest
357 return res
357 return res
358
358
359 # pat: ossep
359 # pat: ossep
360 # dest ossep
360 # dest ossep
361 # srcs: list of (hgsep, hgsep, ossep, bool)
361 # srcs: list of (hgsep, hgsep, ossep, bool)
362 # return: function that takes hgsep and returns ossep
362 # return: function that takes hgsep and returns ossep
363 def targetpathafterfn(pat, dest, srcs):
363 def targetpathafterfn(pat, dest, srcs):
364 if matchmod.patkind(pat):
364 if matchmod.patkind(pat):
365 # a mercurial pattern
365 # a mercurial pattern
366 res = lambda p: os.path.join(dest,
366 res = lambda p: os.path.join(dest,
367 os.path.basename(util.localpath(p)))
367 os.path.basename(util.localpath(p)))
368 else:
368 else:
369 abspfx = scmutil.canonpath(repo.root, cwd, pat)
369 abspfx = scmutil.canonpath(repo.root, cwd, pat)
370 if len(abspfx) < len(srcs[0][0]):
370 if len(abspfx) < len(srcs[0][0]):
371 # A directory. Either the target path contains the last
371 # A directory. Either the target path contains the last
372 # component of the source path or it does not.
372 # component of the source path or it does not.
373 def evalpath(striplen):
373 def evalpath(striplen):
374 score = 0
374 score = 0
375 for s in srcs:
375 for s in srcs:
376 t = os.path.join(dest, util.localpath(s[0])[striplen:])
376 t = os.path.join(dest, util.localpath(s[0])[striplen:])
377 if os.path.lexists(t):
377 if os.path.lexists(t):
378 score += 1
378 score += 1
379 return score
379 return score
380
380
381 abspfx = util.localpath(abspfx)
381 abspfx = util.localpath(abspfx)
382 striplen = len(abspfx)
382 striplen = len(abspfx)
383 if striplen:
383 if striplen:
384 striplen += len(os.sep)
384 striplen += len(os.sep)
385 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
385 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
386 score = evalpath(striplen)
386 score = evalpath(striplen)
387 striplen1 = len(os.path.split(abspfx)[0])
387 striplen1 = len(os.path.split(abspfx)[0])
388 if striplen1:
388 if striplen1:
389 striplen1 += len(os.sep)
389 striplen1 += len(os.sep)
390 if evalpath(striplen1) > score:
390 if evalpath(striplen1) > score:
391 striplen = striplen1
391 striplen = striplen1
392 res = lambda p: os.path.join(dest,
392 res = lambda p: os.path.join(dest,
393 util.localpath(p)[striplen:])
393 util.localpath(p)[striplen:])
394 else:
394 else:
395 # a file
395 # a file
396 if destdirexists:
396 if destdirexists:
397 res = lambda p: os.path.join(dest,
397 res = lambda p: os.path.join(dest,
398 os.path.basename(util.localpath(p)))
398 os.path.basename(util.localpath(p)))
399 else:
399 else:
400 res = lambda p: dest
400 res = lambda p: dest
401 return res
401 return res
402
402
403
403
404 pats = scmutil.expandpats(pats)
404 pats = scmutil.expandpats(pats)
405 if not pats:
405 if not pats:
406 raise util.Abort(_('no source or destination specified'))
406 raise util.Abort(_('no source or destination specified'))
407 if len(pats) == 1:
407 if len(pats) == 1:
408 raise util.Abort(_('no destination specified'))
408 raise util.Abort(_('no destination specified'))
409 dest = pats.pop()
409 dest = pats.pop()
410 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
410 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
411 if not destdirexists:
411 if not destdirexists:
412 if len(pats) > 1 or matchmod.patkind(pats[0]):
412 if len(pats) > 1 or matchmod.patkind(pats[0]):
413 raise util.Abort(_('with multiple sources, destination must be an '
413 raise util.Abort(_('with multiple sources, destination must be an '
414 'existing directory'))
414 'existing directory'))
415 if util.endswithsep(dest):
415 if util.endswithsep(dest):
416 raise util.Abort(_('destination %s is not a directory') % dest)
416 raise util.Abort(_('destination %s is not a directory') % dest)
417
417
418 tfn = targetpathfn
418 tfn = targetpathfn
419 if after:
419 if after:
420 tfn = targetpathafterfn
420 tfn = targetpathafterfn
421 copylist = []
421 copylist = []
422 for pat in pats:
422 for pat in pats:
423 srcs = walkpat(pat)
423 srcs = walkpat(pat)
424 if not srcs:
424 if not srcs:
425 continue
425 continue
426 copylist.append((tfn(pat, dest, srcs), srcs))
426 copylist.append((tfn(pat, dest, srcs), srcs))
427 if not copylist:
427 if not copylist:
428 raise util.Abort(_('no files to copy'))
428 raise util.Abort(_('no files to copy'))
429
429
430 errors = 0
430 errors = 0
431 for targetpath, srcs in copylist:
431 for targetpath, srcs in copylist:
432 for abssrc, relsrc, exact in srcs:
432 for abssrc, relsrc, exact in srcs:
433 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
433 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
434 errors += 1
434 errors += 1
435
435
436 if errors:
436 if errors:
437 ui.warn(_('(consider using --after)\n'))
437 ui.warn(_('(consider using --after)\n'))
438
438
439 return errors != 0
439 return errors != 0
440
440
441 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
441 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
442 runargs=None, appendpid=False):
442 runargs=None, appendpid=False):
443 '''Run a command as a service.'''
443 '''Run a command as a service.'''
444
444
445 if opts['daemon'] and not opts['daemon_pipefds']:
445 if opts['daemon'] and not opts['daemon_pipefds']:
446 # Signal child process startup with file removal
446 # Signal child process startup with file removal
447 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
447 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
448 os.close(lockfd)
448 os.close(lockfd)
449 try:
449 try:
450 if not runargs:
450 if not runargs:
451 runargs = util.hgcmd() + sys.argv[1:]
451 runargs = util.hgcmd() + sys.argv[1:]
452 runargs.append('--daemon-pipefds=%s' % lockpath)
452 runargs.append('--daemon-pipefds=%s' % lockpath)
453 # Don't pass --cwd to the child process, because we've already
453 # Don't pass --cwd to the child process, because we've already
454 # changed directory.
454 # changed directory.
455 for i in xrange(1, len(runargs)):
455 for i in xrange(1, len(runargs)):
456 if runargs[i].startswith('--cwd='):
456 if runargs[i].startswith('--cwd='):
457 del runargs[i]
457 del runargs[i]
458 break
458 break
459 elif runargs[i].startswith('--cwd'):
459 elif runargs[i].startswith('--cwd'):
460 del runargs[i:i + 2]
460 del runargs[i:i + 2]
461 break
461 break
462 def condfn():
462 def condfn():
463 return not os.path.exists(lockpath)
463 return not os.path.exists(lockpath)
464 pid = util.rundetached(runargs, condfn)
464 pid = util.rundetached(runargs, condfn)
465 if pid < 0:
465 if pid < 0:
466 raise util.Abort(_('child process failed to start'))
466 raise util.Abort(_('child process failed to start'))
467 finally:
467 finally:
468 try:
468 try:
469 os.unlink(lockpath)
469 os.unlink(lockpath)
470 except OSError, e:
470 except OSError, e:
471 if e.errno != errno.ENOENT:
471 if e.errno != errno.ENOENT:
472 raise
472 raise
473 if parentfn:
473 if parentfn:
474 return parentfn(pid)
474 return parentfn(pid)
475 else:
475 else:
476 return
476 return
477
477
478 if initfn:
478 if initfn:
479 initfn()
479 initfn()
480
480
481 if opts['pid_file']:
481 if opts['pid_file']:
482 mode = appendpid and 'a' or 'w'
482 mode = appendpid and 'a' or 'w'
483 fp = open(opts['pid_file'], mode)
483 fp = open(opts['pid_file'], mode)
484 fp.write(str(os.getpid()) + '\n')
484 fp.write(str(os.getpid()) + '\n')
485 fp.close()
485 fp.close()
486
486
487 if opts['daemon_pipefds']:
487 if opts['daemon_pipefds']:
488 lockpath = opts['daemon_pipefds']
488 lockpath = opts['daemon_pipefds']
489 try:
489 try:
490 os.setsid()
490 os.setsid()
491 except AttributeError:
491 except AttributeError:
492 pass
492 pass
493 os.unlink(lockpath)
493 os.unlink(lockpath)
494 util.hidewindow()
494 util.hidewindow()
495 sys.stdout.flush()
495 sys.stdout.flush()
496 sys.stderr.flush()
496 sys.stderr.flush()
497
497
498 nullfd = os.open(util.nulldev, os.O_RDWR)
498 nullfd = os.open(util.nulldev, os.O_RDWR)
499 logfilefd = nullfd
499 logfilefd = nullfd
500 if logfile:
500 if logfile:
501 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
501 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
502 os.dup2(nullfd, 0)
502 os.dup2(nullfd, 0)
503 os.dup2(logfilefd, 1)
503 os.dup2(logfilefd, 1)
504 os.dup2(logfilefd, 2)
504 os.dup2(logfilefd, 2)
505 if nullfd not in (0, 1, 2):
505 if nullfd not in (0, 1, 2):
506 os.close(nullfd)
506 os.close(nullfd)
507 if logfile and logfilefd not in (0, 1, 2):
507 if logfile and logfilefd not in (0, 1, 2):
508 os.close(logfilefd)
508 os.close(logfilefd)
509
509
510 if runfn:
510 if runfn:
511 return runfn()
511 return runfn()
512
512
513 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
513 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
514 opts=None):
514 opts=None):
515 '''export changesets as hg patches.'''
515 '''export changesets as hg patches.'''
516
516
517 total = len(revs)
517 total = len(revs)
518 revwidth = max([len(str(rev)) for rev in revs])
518 revwidth = max([len(str(rev)) for rev in revs])
519
519
520 def single(rev, seqno, fp):
520 def single(rev, seqno, fp):
521 ctx = repo[rev]
521 ctx = repo[rev]
522 node = ctx.node()
522 node = ctx.node()
523 parents = [p.node() for p in ctx.parents() if p]
523 parents = [p.node() for p in ctx.parents() if p]
524 branch = ctx.branch()
524 branch = ctx.branch()
525 if switch_parent:
525 if switch_parent:
526 parents.reverse()
526 parents.reverse()
527 prev = (parents and parents[0]) or nullid
527 prev = (parents and parents[0]) or nullid
528
528
529 shouldclose = False
529 shouldclose = False
530 if not fp:
530 if not fp:
531 desc_lines = ctx.description().rstrip().split('\n')
531 desc_lines = ctx.description().rstrip().split('\n')
532 desc = desc_lines[0] #Commit always has a first line.
532 desc = desc_lines[0] #Commit always has a first line.
533 fp = makefileobj(repo, template, node, desc=desc, total=total,
533 fp = makefileobj(repo, template, node, desc=desc, total=total,
534 seqno=seqno, revwidth=revwidth, mode='ab')
534 seqno=seqno, revwidth=revwidth, mode='ab')
535 if fp != template:
535 if fp != template:
536 shouldclose = True
536 shouldclose = True
537 if fp != sys.stdout and util.safehasattr(fp, 'name'):
537 if fp != sys.stdout and util.safehasattr(fp, 'name'):
538 repo.ui.note("%s\n" % fp.name)
538 repo.ui.note("%s\n" % fp.name)
539
539
540 fp.write("# HG changeset patch\n")
540 fp.write("# HG changeset patch\n")
541 fp.write("# User %s\n" % ctx.user())
541 fp.write("# User %s\n" % ctx.user())
542 fp.write("# Date %d %d\n" % ctx.date())
542 fp.write("# Date %d %d\n" % ctx.date())
543 if branch and branch != 'default':
543 if branch and branch != 'default':
544 fp.write("# Branch %s\n" % branch)
544 fp.write("# Branch %s\n" % branch)
545 fp.write("# Node ID %s\n" % hex(node))
545 fp.write("# Node ID %s\n" % hex(node))
546 fp.write("# Parent %s\n" % hex(prev))
546 fp.write("# Parent %s\n" % hex(prev))
547 if len(parents) > 1:
547 if len(parents) > 1:
548 fp.write("# Parent %s\n" % hex(parents[1]))
548 fp.write("# Parent %s\n" % hex(parents[1]))
549 fp.write(ctx.description().rstrip())
549 fp.write(ctx.description().rstrip())
550 fp.write("\n\n")
550 fp.write("\n\n")
551
551
552 for chunk in patch.diff(repo, prev, node, opts=opts):
552 for chunk in patch.diff(repo, prev, node, opts=opts):
553 fp.write(chunk)
553 fp.write(chunk)
554
554
555 if shouldclose:
555 if shouldclose:
556 fp.close()
556 fp.close()
557
557
558 for seqno, rev in enumerate(revs):
558 for seqno, rev in enumerate(revs):
559 single(rev, seqno + 1, fp)
559 single(rev, seqno + 1, fp)
560
560
561 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
561 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
562 changes=None, stat=False, fp=None, prefix='',
562 changes=None, stat=False, fp=None, prefix='',
563 listsubrepos=False):
563 listsubrepos=False):
564 '''show diff or diffstat.'''
564 '''show diff or diffstat.'''
565 if fp is None:
565 if fp is None:
566 write = ui.write
566 write = ui.write
567 else:
567 else:
568 def write(s, **kw):
568 def write(s, **kw):
569 fp.write(s)
569 fp.write(s)
570
570
571 if stat:
571 if stat:
572 diffopts = diffopts.copy(context=0)
572 diffopts = diffopts.copy(context=0)
573 width = 80
573 width = 80
574 if not ui.plain():
574 if not ui.plain():
575 width = ui.termwidth()
575 width = ui.termwidth()
576 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
576 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
577 prefix=prefix)
577 prefix=prefix)
578 for chunk, label in patch.diffstatui(util.iterlines(chunks),
578 for chunk, label in patch.diffstatui(util.iterlines(chunks),
579 width=width,
579 width=width,
580 git=diffopts.git):
580 git=diffopts.git):
581 write(chunk, label=label)
581 write(chunk, label=label)
582 else:
582 else:
583 for chunk, label in patch.diffui(repo, node1, node2, match,
583 for chunk, label in patch.diffui(repo, node1, node2, match,
584 changes, diffopts, prefix=prefix):
584 changes, diffopts, prefix=prefix):
585 write(chunk, label=label)
585 write(chunk, label=label)
586
586
587 if listsubrepos:
587 if listsubrepos:
588 ctx1 = repo[node1]
588 ctx1 = repo[node1]
589 ctx2 = repo[node2]
589 ctx2 = repo[node2]
590 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
590 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
591 tempnode2 = node2
591 tempnode2 = node2
592 try:
592 try:
593 if node2 is not None:
593 if node2 is not None:
594 tempnode2 = ctx2.substate[subpath][1]
594 tempnode2 = ctx2.substate[subpath][1]
595 except KeyError:
595 except KeyError:
596 # A subrepo that existed in node1 was deleted between node1 and
596 # A subrepo that existed in node1 was deleted between node1 and
597 # node2 (inclusive). Thus, ctx2's substate won't contain that
597 # node2 (inclusive). Thus, ctx2's substate won't contain that
598 # subpath. The best we can do is to ignore it.
598 # subpath. The best we can do is to ignore it.
599 tempnode2 = None
599 tempnode2 = None
600 submatch = matchmod.narrowmatcher(subpath, match)
600 submatch = matchmod.narrowmatcher(subpath, match)
601 sub.diff(diffopts, tempnode2, submatch, changes=changes,
601 sub.diff(diffopts, tempnode2, submatch, changes=changes,
602 stat=stat, fp=fp, prefix=prefix)
602 stat=stat, fp=fp, prefix=prefix)
603
603
604 class changeset_printer(object):
604 class changeset_printer(object):
605 '''show changeset information when templating not requested.'''
605 '''show changeset information when templating not requested.'''
606
606
607 def __init__(self, ui, repo, patch, diffopts, buffered):
607 def __init__(self, ui, repo, patch, diffopts, buffered):
608 self.ui = ui
608 self.ui = ui
609 self.repo = repo
609 self.repo = repo
610 self.buffered = buffered
610 self.buffered = buffered
611 self.patch = patch
611 self.patch = patch
612 self.diffopts = diffopts
612 self.diffopts = diffopts
613 self.header = {}
613 self.header = {}
614 self.hunk = {}
614 self.hunk = {}
615 self.lastheader = None
615 self.lastheader = None
616 self.footer = None
616 self.footer = None
617
617
618 def flush(self, rev):
618 def flush(self, rev):
619 if rev in self.header:
619 if rev in self.header:
620 h = self.header[rev]
620 h = self.header[rev]
621 if h != self.lastheader:
621 if h != self.lastheader:
622 self.lastheader = h
622 self.lastheader = h
623 self.ui.write(h)
623 self.ui.write(h)
624 del self.header[rev]
624 del self.header[rev]
625 if rev in self.hunk:
625 if rev in self.hunk:
626 self.ui.write(self.hunk[rev])
626 self.ui.write(self.hunk[rev])
627 del self.hunk[rev]
627 del self.hunk[rev]
628 return 1
628 return 1
629 return 0
629 return 0
630
630
631 def close(self):
631 def close(self):
632 if self.footer:
632 if self.footer:
633 self.ui.write(self.footer)
633 self.ui.write(self.footer)
634
634
635 def show(self, ctx, copies=None, matchfn=None, **props):
635 def show(self, ctx, copies=None, matchfn=None, **props):
636 if self.buffered:
636 if self.buffered:
637 self.ui.pushbuffer()
637 self.ui.pushbuffer()
638 self._show(ctx, copies, matchfn, props)
638 self._show(ctx, copies, matchfn, props)
639 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
639 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
640 else:
640 else:
641 self._show(ctx, copies, matchfn, props)
641 self._show(ctx, copies, matchfn, props)
642
642
643 def _show(self, ctx, copies, matchfn, props):
643 def _show(self, ctx, copies, matchfn, props):
644 '''show a single changeset or file revision'''
644 '''show a single changeset or file revision'''
645 changenode = ctx.node()
645 changenode = ctx.node()
646 rev = ctx.rev()
646 rev = ctx.rev()
647
647
648 if self.ui.quiet:
648 if self.ui.quiet:
649 self.ui.write("%d:%s\n" % (rev, short(changenode)),
649 self.ui.write("%d:%s\n" % (rev, short(changenode)),
650 label='log.node')
650 label='log.node')
651 return
651 return
652
652
653 log = self.repo.changelog
653 log = self.repo.changelog
654 date = util.datestr(ctx.date())
654 date = util.datestr(ctx.date())
655
655
656 hexfunc = self.ui.debugflag and hex or short
656 hexfunc = self.ui.debugflag and hex or short
657
657
658 parents = [(p, hexfunc(log.node(p)))
658 parents = [(p, hexfunc(log.node(p)))
659 for p in self._meaningful_parentrevs(log, rev)]
659 for p in self._meaningful_parentrevs(log, rev)]
660
660
661 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
661 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
662 label='log.changeset')
662 label='log.changeset')
663
663
664 branch = ctx.branch()
664 branch = ctx.branch()
665 # don't show the default branch name
665 # don't show the default branch name
666 if branch != 'default':
666 if branch != 'default':
667 self.ui.write(_("branch: %s\n") % branch,
667 self.ui.write(_("branch: %s\n") % branch,
668 label='log.branch')
668 label='log.branch')
669 for bookmark in self.repo.nodebookmarks(changenode):
669 for bookmark in self.repo.nodebookmarks(changenode):
670 self.ui.write(_("bookmark: %s\n") % bookmark,
670 self.ui.write(_("bookmark: %s\n") % bookmark,
671 label='log.bookmark')
671 label='log.bookmark')
672 for tag in self.repo.nodetags(changenode):
672 for tag in self.repo.nodetags(changenode):
673 self.ui.write(_("tag: %s\n") % tag,
673 self.ui.write(_("tag: %s\n") % tag,
674 label='log.tag')
674 label='log.tag')
675 if self.ui.debugflag and ctx.phase():
676 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
677 label='log.phase')
675 for parent in parents:
678 for parent in parents:
676 self.ui.write(_("parent: %d:%s\n") % parent,
679 self.ui.write(_("parent: %d:%s\n") % parent,
677 label='log.parent')
680 label='log.parent')
678
681
679 if self.ui.debugflag:
682 if self.ui.debugflag:
680 mnode = ctx.manifestnode()
683 mnode = ctx.manifestnode()
681 self.ui.write(_("manifest: %d:%s\n") %
684 self.ui.write(_("manifest: %d:%s\n") %
682 (self.repo.manifest.rev(mnode), hex(mnode)),
685 (self.repo.manifest.rev(mnode), hex(mnode)),
683 label='ui.debug log.manifest')
686 label='ui.debug log.manifest')
684 self.ui.write(_("user: %s\n") % ctx.user(),
687 self.ui.write(_("user: %s\n") % ctx.user(),
685 label='log.user')
688 label='log.user')
686 self.ui.write(_("date: %s\n") % date,
689 self.ui.write(_("date: %s\n") % date,
687 label='log.date')
690 label='log.date')
688
691
689 if self.ui.debugflag:
692 if self.ui.debugflag:
690 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
693 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
691 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
694 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
692 files):
695 files):
693 if value:
696 if value:
694 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
697 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
695 label='ui.debug log.files')
698 label='ui.debug log.files')
696 elif ctx.files() and self.ui.verbose:
699 elif ctx.files() and self.ui.verbose:
697 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
700 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
698 label='ui.note log.files')
701 label='ui.note log.files')
699 if copies and self.ui.verbose:
702 if copies and self.ui.verbose:
700 copies = ['%s (%s)' % c for c in copies]
703 copies = ['%s (%s)' % c for c in copies]
701 self.ui.write(_("copies: %s\n") % ' '.join(copies),
704 self.ui.write(_("copies: %s\n") % ' '.join(copies),
702 label='ui.note log.copies')
705 label='ui.note log.copies')
703
706
704 extra = ctx.extra()
707 extra = ctx.extra()
705 if extra and self.ui.debugflag:
708 if extra and self.ui.debugflag:
706 for key, value in sorted(extra.items()):
709 for key, value in sorted(extra.items()):
707 self.ui.write(_("extra: %s=%s\n")
710 self.ui.write(_("extra: %s=%s\n")
708 % (key, value.encode('string_escape')),
711 % (key, value.encode('string_escape')),
709 label='ui.debug log.extra')
712 label='ui.debug log.extra')
710
713
711 description = ctx.description().strip()
714 description = ctx.description().strip()
712 if description:
715 if description:
713 if self.ui.verbose:
716 if self.ui.verbose:
714 self.ui.write(_("description:\n"),
717 self.ui.write(_("description:\n"),
715 label='ui.note log.description')
718 label='ui.note log.description')
716 self.ui.write(description,
719 self.ui.write(description,
717 label='ui.note log.description')
720 label='ui.note log.description')
718 self.ui.write("\n\n")
721 self.ui.write("\n\n")
719 else:
722 else:
720 self.ui.write(_("summary: %s\n") %
723 self.ui.write(_("summary: %s\n") %
721 description.splitlines()[0],
724 description.splitlines()[0],
722 label='log.summary')
725 label='log.summary')
723 self.ui.write("\n")
726 self.ui.write("\n")
724
727
725 self.showpatch(changenode, matchfn)
728 self.showpatch(changenode, matchfn)
726
729
727 def showpatch(self, node, matchfn):
730 def showpatch(self, node, matchfn):
728 if not matchfn:
731 if not matchfn:
729 matchfn = self.patch
732 matchfn = self.patch
730 if matchfn:
733 if matchfn:
731 stat = self.diffopts.get('stat')
734 stat = self.diffopts.get('stat')
732 diff = self.diffopts.get('patch')
735 diff = self.diffopts.get('patch')
733 diffopts = patch.diffopts(self.ui, self.diffopts)
736 diffopts = patch.diffopts(self.ui, self.diffopts)
734 prev = self.repo.changelog.parents(node)[0]
737 prev = self.repo.changelog.parents(node)[0]
735 if stat:
738 if stat:
736 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
739 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
737 match=matchfn, stat=True)
740 match=matchfn, stat=True)
738 if diff:
741 if diff:
739 if stat:
742 if stat:
740 self.ui.write("\n")
743 self.ui.write("\n")
741 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
744 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
742 match=matchfn, stat=False)
745 match=matchfn, stat=False)
743 self.ui.write("\n")
746 self.ui.write("\n")
744
747
745 def _meaningful_parentrevs(self, log, rev):
748 def _meaningful_parentrevs(self, log, rev):
746 """Return list of meaningful (or all if debug) parentrevs for rev.
749 """Return list of meaningful (or all if debug) parentrevs for rev.
747
750
748 For merges (two non-nullrev revisions) both parents are meaningful.
751 For merges (two non-nullrev revisions) both parents are meaningful.
749 Otherwise the first parent revision is considered meaningful if it
752 Otherwise the first parent revision is considered meaningful if it
750 is not the preceding revision.
753 is not the preceding revision.
751 """
754 """
752 parents = log.parentrevs(rev)
755 parents = log.parentrevs(rev)
753 if not self.ui.debugflag and parents[1] == nullrev:
756 if not self.ui.debugflag and parents[1] == nullrev:
754 if parents[0] >= rev - 1:
757 if parents[0] >= rev - 1:
755 parents = []
758 parents = []
756 else:
759 else:
757 parents = [parents[0]]
760 parents = [parents[0]]
758 return parents
761 return parents
759
762
760
763
761 class changeset_templater(changeset_printer):
764 class changeset_templater(changeset_printer):
762 '''format changeset information.'''
765 '''format changeset information.'''
763
766
764 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
767 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
765 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
768 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
766 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
769 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
767 defaulttempl = {
770 defaulttempl = {
768 'parent': '{rev}:{node|formatnode} ',
771 'parent': '{rev}:{node|formatnode} ',
769 'manifest': '{rev}:{node|formatnode}',
772 'manifest': '{rev}:{node|formatnode}',
770 'file_copy': '{name} ({source})',
773 'file_copy': '{name} ({source})',
771 'extra': '{key}={value|stringescape}'
774 'extra': '{key}={value|stringescape}'
772 }
775 }
773 # filecopy is preserved for compatibility reasons
776 # filecopy is preserved for compatibility reasons
774 defaulttempl['filecopy'] = defaulttempl['file_copy']
777 defaulttempl['filecopy'] = defaulttempl['file_copy']
775 self.t = templater.templater(mapfile, {'formatnode': formatnode},
778 self.t = templater.templater(mapfile, {'formatnode': formatnode},
776 cache=defaulttempl)
779 cache=defaulttempl)
777 self.cache = {}
780 self.cache = {}
778
781
779 def use_template(self, t):
782 def use_template(self, t):
780 '''set template string to use'''
783 '''set template string to use'''
781 self.t.cache['changeset'] = t
784 self.t.cache['changeset'] = t
782
785
783 def _meaningful_parentrevs(self, ctx):
786 def _meaningful_parentrevs(self, ctx):
784 """Return list of meaningful (or all if debug) parentrevs for rev.
787 """Return list of meaningful (or all if debug) parentrevs for rev.
785 """
788 """
786 parents = ctx.parents()
789 parents = ctx.parents()
787 if len(parents) > 1:
790 if len(parents) > 1:
788 return parents
791 return parents
789 if self.ui.debugflag:
792 if self.ui.debugflag:
790 return [parents[0], self.repo['null']]
793 return [parents[0], self.repo['null']]
791 if parents[0].rev() >= ctx.rev() - 1:
794 if parents[0].rev() >= ctx.rev() - 1:
792 return []
795 return []
793 return parents
796 return parents
794
797
795 def _show(self, ctx, copies, matchfn, props):
798 def _show(self, ctx, copies, matchfn, props):
796 '''show a single changeset or file revision'''
799 '''show a single changeset or file revision'''
797
800
798 showlist = templatekw.showlist
801 showlist = templatekw.showlist
799
802
800 # showparents() behaviour depends on ui trace level which
803 # showparents() behaviour depends on ui trace level which
801 # causes unexpected behaviours at templating level and makes
804 # causes unexpected behaviours at templating level and makes
802 # it harder to extract it in a standalone function. Its
805 # it harder to extract it in a standalone function. Its
803 # behaviour cannot be changed so leave it here for now.
806 # behaviour cannot be changed so leave it here for now.
804 def showparents(**args):
807 def showparents(**args):
805 ctx = args['ctx']
808 ctx = args['ctx']
806 parents = [[('rev', p.rev()), ('node', p.hex())]
809 parents = [[('rev', p.rev()), ('node', p.hex())]
807 for p in self._meaningful_parentrevs(ctx)]
810 for p in self._meaningful_parentrevs(ctx)]
808 return showlist('parent', parents, **args)
811 return showlist('parent', parents, **args)
809
812
810 props = props.copy()
813 props = props.copy()
811 props.update(templatekw.keywords)
814 props.update(templatekw.keywords)
812 props['parents'] = showparents
815 props['parents'] = showparents
813 props['templ'] = self.t
816 props['templ'] = self.t
814 props['ctx'] = ctx
817 props['ctx'] = ctx
815 props['repo'] = self.repo
818 props['repo'] = self.repo
816 props['revcache'] = {'copies': copies}
819 props['revcache'] = {'copies': copies}
817 props['cache'] = self.cache
820 props['cache'] = self.cache
818
821
819 # find correct templates for current mode
822 # find correct templates for current mode
820
823
821 tmplmodes = [
824 tmplmodes = [
822 (True, None),
825 (True, None),
823 (self.ui.verbose, 'verbose'),
826 (self.ui.verbose, 'verbose'),
824 (self.ui.quiet, 'quiet'),
827 (self.ui.quiet, 'quiet'),
825 (self.ui.debugflag, 'debug'),
828 (self.ui.debugflag, 'debug'),
826 ]
829 ]
827
830
828 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
831 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
829 for mode, postfix in tmplmodes:
832 for mode, postfix in tmplmodes:
830 for type in types:
833 for type in types:
831 cur = postfix and ('%s_%s' % (type, postfix)) or type
834 cur = postfix and ('%s_%s' % (type, postfix)) or type
832 if mode and cur in self.t:
835 if mode and cur in self.t:
833 types[type] = cur
836 types[type] = cur
834
837
835 try:
838 try:
836
839
837 # write header
840 # write header
838 if types['header']:
841 if types['header']:
839 h = templater.stringify(self.t(types['header'], **props))
842 h = templater.stringify(self.t(types['header'], **props))
840 if self.buffered:
843 if self.buffered:
841 self.header[ctx.rev()] = h
844 self.header[ctx.rev()] = h
842 else:
845 else:
843 if self.lastheader != h:
846 if self.lastheader != h:
844 self.lastheader = h
847 self.lastheader = h
845 self.ui.write(h)
848 self.ui.write(h)
846
849
847 # write changeset metadata, then patch if requested
850 # write changeset metadata, then patch if requested
848 key = types['changeset']
851 key = types['changeset']
849 self.ui.write(templater.stringify(self.t(key, **props)))
852 self.ui.write(templater.stringify(self.t(key, **props)))
850 self.showpatch(ctx.node(), matchfn)
853 self.showpatch(ctx.node(), matchfn)
851
854
852 if types['footer']:
855 if types['footer']:
853 if not self.footer:
856 if not self.footer:
854 self.footer = templater.stringify(self.t(types['footer'],
857 self.footer = templater.stringify(self.t(types['footer'],
855 **props))
858 **props))
856
859
857 except KeyError, inst:
860 except KeyError, inst:
858 msg = _("%s: no key named '%s'")
861 msg = _("%s: no key named '%s'")
859 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
862 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
860 except SyntaxError, inst:
863 except SyntaxError, inst:
861 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
864 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
862
865
863 def show_changeset(ui, repo, opts, buffered=False):
866 def show_changeset(ui, repo, opts, buffered=False):
864 """show one changeset using template or regular display.
867 """show one changeset using template or regular display.
865
868
866 Display format will be the first non-empty hit of:
869 Display format will be the first non-empty hit of:
867 1. option 'template'
870 1. option 'template'
868 2. option 'style'
871 2. option 'style'
869 3. [ui] setting 'logtemplate'
872 3. [ui] setting 'logtemplate'
870 4. [ui] setting 'style'
873 4. [ui] setting 'style'
871 If all of these values are either the unset or the empty string,
874 If all of these values are either the unset or the empty string,
872 regular display via changeset_printer() is done.
875 regular display via changeset_printer() is done.
873 """
876 """
874 # options
877 # options
875 patch = False
878 patch = False
876 if opts.get('patch') or opts.get('stat'):
879 if opts.get('patch') or opts.get('stat'):
877 patch = scmutil.matchall(repo)
880 patch = scmutil.matchall(repo)
878
881
879 tmpl = opts.get('template')
882 tmpl = opts.get('template')
880 style = None
883 style = None
881 if tmpl:
884 if tmpl:
882 tmpl = templater.parsestring(tmpl, quoted=False)
885 tmpl = templater.parsestring(tmpl, quoted=False)
883 else:
886 else:
884 style = opts.get('style')
887 style = opts.get('style')
885
888
886 # ui settings
889 # ui settings
887 if not (tmpl or style):
890 if not (tmpl or style):
888 tmpl = ui.config('ui', 'logtemplate')
891 tmpl = ui.config('ui', 'logtemplate')
889 if tmpl:
892 if tmpl:
890 tmpl = templater.parsestring(tmpl)
893 tmpl = templater.parsestring(tmpl)
891 else:
894 else:
892 style = util.expandpath(ui.config('ui', 'style', ''))
895 style = util.expandpath(ui.config('ui', 'style', ''))
893
896
894 if not (tmpl or style):
897 if not (tmpl or style):
895 return changeset_printer(ui, repo, patch, opts, buffered)
898 return changeset_printer(ui, repo, patch, opts, buffered)
896
899
897 mapfile = None
900 mapfile = None
898 if style and not tmpl:
901 if style and not tmpl:
899 mapfile = style
902 mapfile = style
900 if not os.path.split(mapfile)[0]:
903 if not os.path.split(mapfile)[0]:
901 mapname = (templater.templatepath('map-cmdline.' + mapfile)
904 mapname = (templater.templatepath('map-cmdline.' + mapfile)
902 or templater.templatepath(mapfile))
905 or templater.templatepath(mapfile))
903 if mapname:
906 if mapname:
904 mapfile = mapname
907 mapfile = mapname
905
908
906 try:
909 try:
907 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
910 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
908 except SyntaxError, inst:
911 except SyntaxError, inst:
909 raise util.Abort(inst.args[0])
912 raise util.Abort(inst.args[0])
910 if tmpl:
913 if tmpl:
911 t.use_template(tmpl)
914 t.use_template(tmpl)
912 return t
915 return t
913
916
914 def finddate(ui, repo, date):
917 def finddate(ui, repo, date):
915 """Find the tipmost changeset that matches the given date spec"""
918 """Find the tipmost changeset that matches the given date spec"""
916
919
917 df = util.matchdate(date)
920 df = util.matchdate(date)
918 m = scmutil.matchall(repo)
921 m = scmutil.matchall(repo)
919 results = {}
922 results = {}
920
923
921 def prep(ctx, fns):
924 def prep(ctx, fns):
922 d = ctx.date()
925 d = ctx.date()
923 if df(d[0]):
926 if df(d[0]):
924 results[ctx.rev()] = d
927 results[ctx.rev()] = d
925
928
926 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
929 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
927 rev = ctx.rev()
930 rev = ctx.rev()
928 if rev in results:
931 if rev in results:
929 ui.status(_("Found revision %s from %s\n") %
932 ui.status(_("Found revision %s from %s\n") %
930 (rev, util.datestr(results[rev])))
933 (rev, util.datestr(results[rev])))
931 return str(rev)
934 return str(rev)
932
935
933 raise util.Abort(_("revision matching date not found"))
936 raise util.Abort(_("revision matching date not found"))
934
937
935 def walkchangerevs(repo, match, opts, prepare):
938 def walkchangerevs(repo, match, opts, prepare):
936 '''Iterate over files and the revs in which they changed.
939 '''Iterate over files and the revs in which they changed.
937
940
938 Callers most commonly need to iterate backwards over the history
941 Callers most commonly need to iterate backwards over the history
939 in which they are interested. Doing so has awful (quadratic-looking)
942 in which they are interested. Doing so has awful (quadratic-looking)
940 performance, so we use iterators in a "windowed" way.
943 performance, so we use iterators in a "windowed" way.
941
944
942 We walk a window of revisions in the desired order. Within the
945 We walk a window of revisions in the desired order. Within the
943 window, we first walk forwards to gather data, then in the desired
946 window, we first walk forwards to gather data, then in the desired
944 order (usually backwards) to display it.
947 order (usually backwards) to display it.
945
948
946 This function returns an iterator yielding contexts. Before
949 This function returns an iterator yielding contexts. Before
947 yielding each context, the iterator will first call the prepare
950 yielding each context, the iterator will first call the prepare
948 function on each context in the window in forward order.'''
951 function on each context in the window in forward order.'''
949
952
950 def increasing_windows(start, end, windowsize=8, sizelimit=512):
953 def increasing_windows(start, end, windowsize=8, sizelimit=512):
951 if start < end:
954 if start < end:
952 while start < end:
955 while start < end:
953 yield start, min(windowsize, end - start)
956 yield start, min(windowsize, end - start)
954 start += windowsize
957 start += windowsize
955 if windowsize < sizelimit:
958 if windowsize < sizelimit:
956 windowsize *= 2
959 windowsize *= 2
957 else:
960 else:
958 while start > end:
961 while start > end:
959 yield start, min(windowsize, start - end - 1)
962 yield start, min(windowsize, start - end - 1)
960 start -= windowsize
963 start -= windowsize
961 if windowsize < sizelimit:
964 if windowsize < sizelimit:
962 windowsize *= 2
965 windowsize *= 2
963
966
964 follow = opts.get('follow') or opts.get('follow_first')
967 follow = opts.get('follow') or opts.get('follow_first')
965
968
966 if not len(repo):
969 if not len(repo):
967 return []
970 return []
968
971
969 if follow:
972 if follow:
970 defrange = '%s:0' % repo['.'].rev()
973 defrange = '%s:0' % repo['.'].rev()
971 else:
974 else:
972 defrange = '-1:0'
975 defrange = '-1:0'
973 revs = scmutil.revrange(repo, opts['rev'] or [defrange])
976 revs = scmutil.revrange(repo, opts['rev'] or [defrange])
974 if not revs:
977 if not revs:
975 return []
978 return []
976 wanted = set()
979 wanted = set()
977 slowpath = match.anypats() or (match.files() and opts.get('removed'))
980 slowpath = match.anypats() or (match.files() and opts.get('removed'))
978 fncache = {}
981 fncache = {}
979 change = util.cachefunc(repo.changectx)
982 change = util.cachefunc(repo.changectx)
980
983
981 # First step is to fill wanted, the set of revisions that we want to yield.
984 # First step is to fill wanted, the set of revisions that we want to yield.
982 # When it does not induce extra cost, we also fill fncache for revisions in
985 # When it does not induce extra cost, we also fill fncache for revisions in
983 # wanted: a cache of filenames that were changed (ctx.files()) and that
986 # wanted: a cache of filenames that were changed (ctx.files()) and that
984 # match the file filtering conditions.
987 # match the file filtering conditions.
985
988
986 if not slowpath and not match.files():
989 if not slowpath and not match.files():
987 # No files, no patterns. Display all revs.
990 # No files, no patterns. Display all revs.
988 wanted = set(revs)
991 wanted = set(revs)
989 copies = []
992 copies = []
990
993
991 if not slowpath:
994 if not slowpath:
992 # We only have to read through the filelog to find wanted revisions
995 # We only have to read through the filelog to find wanted revisions
993
996
994 minrev, maxrev = min(revs), max(revs)
997 minrev, maxrev = min(revs), max(revs)
995 def filerevgen(filelog, last):
998 def filerevgen(filelog, last):
996 """
999 """
997 Only files, no patterns. Check the history of each file.
1000 Only files, no patterns. Check the history of each file.
998
1001
999 Examines filelog entries within minrev, maxrev linkrev range
1002 Examines filelog entries within minrev, maxrev linkrev range
1000 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1003 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1001 tuples in backwards order
1004 tuples in backwards order
1002 """
1005 """
1003 cl_count = len(repo)
1006 cl_count = len(repo)
1004 revs = []
1007 revs = []
1005 for j in xrange(0, last + 1):
1008 for j in xrange(0, last + 1):
1006 linkrev = filelog.linkrev(j)
1009 linkrev = filelog.linkrev(j)
1007 if linkrev < minrev:
1010 if linkrev < minrev:
1008 continue
1011 continue
1009 # only yield rev for which we have the changelog, it can
1012 # only yield rev for which we have the changelog, it can
1010 # happen while doing "hg log" during a pull or commit
1013 # happen while doing "hg log" during a pull or commit
1011 if linkrev >= cl_count:
1014 if linkrev >= cl_count:
1012 break
1015 break
1013
1016
1014 parentlinkrevs = []
1017 parentlinkrevs = []
1015 for p in filelog.parentrevs(j):
1018 for p in filelog.parentrevs(j):
1016 if p != nullrev:
1019 if p != nullrev:
1017 parentlinkrevs.append(filelog.linkrev(p))
1020 parentlinkrevs.append(filelog.linkrev(p))
1018 n = filelog.node(j)
1021 n = filelog.node(j)
1019 revs.append((linkrev, parentlinkrevs,
1022 revs.append((linkrev, parentlinkrevs,
1020 follow and filelog.renamed(n)))
1023 follow and filelog.renamed(n)))
1021
1024
1022 return reversed(revs)
1025 return reversed(revs)
1023 def iterfiles():
1026 def iterfiles():
1024 for filename in match.files():
1027 for filename in match.files():
1025 yield filename, None
1028 yield filename, None
1026 for filename_node in copies:
1029 for filename_node in copies:
1027 yield filename_node
1030 yield filename_node
1028 for file_, node in iterfiles():
1031 for file_, node in iterfiles():
1029 filelog = repo.file(file_)
1032 filelog = repo.file(file_)
1030 if not len(filelog):
1033 if not len(filelog):
1031 if node is None:
1034 if node is None:
1032 # A zero count may be a directory or deleted file, so
1035 # A zero count may be a directory or deleted file, so
1033 # try to find matching entries on the slow path.
1036 # try to find matching entries on the slow path.
1034 if follow:
1037 if follow:
1035 raise util.Abort(
1038 raise util.Abort(
1036 _('cannot follow nonexistent file: "%s"') % file_)
1039 _('cannot follow nonexistent file: "%s"') % file_)
1037 slowpath = True
1040 slowpath = True
1038 break
1041 break
1039 else:
1042 else:
1040 continue
1043 continue
1041
1044
1042 if node is None:
1045 if node is None:
1043 last = len(filelog) - 1
1046 last = len(filelog) - 1
1044 else:
1047 else:
1045 last = filelog.rev(node)
1048 last = filelog.rev(node)
1046
1049
1047
1050
1048 # keep track of all ancestors of the file
1051 # keep track of all ancestors of the file
1049 ancestors = set([filelog.linkrev(last)])
1052 ancestors = set([filelog.linkrev(last)])
1050
1053
1051 # iterate from latest to oldest revision
1054 # iterate from latest to oldest revision
1052 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1055 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1053 if not follow:
1056 if not follow:
1054 if rev > maxrev:
1057 if rev > maxrev:
1055 continue
1058 continue
1056 else:
1059 else:
1057 # Note that last might not be the first interesting
1060 # Note that last might not be the first interesting
1058 # rev to us:
1061 # rev to us:
1059 # if the file has been changed after maxrev, we'll
1062 # if the file has been changed after maxrev, we'll
1060 # have linkrev(last) > maxrev, and we still need
1063 # have linkrev(last) > maxrev, and we still need
1061 # to explore the file graph
1064 # to explore the file graph
1062 if rev not in ancestors:
1065 if rev not in ancestors:
1063 continue
1066 continue
1064 # XXX insert 1327 fix here
1067 # XXX insert 1327 fix here
1065 if flparentlinkrevs:
1068 if flparentlinkrevs:
1066 ancestors.update(flparentlinkrevs)
1069 ancestors.update(flparentlinkrevs)
1067
1070
1068 fncache.setdefault(rev, []).append(file_)
1071 fncache.setdefault(rev, []).append(file_)
1069 wanted.add(rev)
1072 wanted.add(rev)
1070 if copied:
1073 if copied:
1071 copies.append(copied)
1074 copies.append(copied)
1072 if slowpath:
1075 if slowpath:
1073 # We have to read the changelog to match filenames against
1076 # We have to read the changelog to match filenames against
1074 # changed files
1077 # changed files
1075
1078
1076 if follow:
1079 if follow:
1077 raise util.Abort(_('can only follow copies/renames for explicit '
1080 raise util.Abort(_('can only follow copies/renames for explicit '
1078 'filenames'))
1081 'filenames'))
1079
1082
1080 # The slow path checks files modified in every changeset.
1083 # The slow path checks files modified in every changeset.
1081 for i in sorted(revs):
1084 for i in sorted(revs):
1082 ctx = change(i)
1085 ctx = change(i)
1083 matches = filter(match, ctx.files())
1086 matches = filter(match, ctx.files())
1084 if matches:
1087 if matches:
1085 fncache[i] = matches
1088 fncache[i] = matches
1086 wanted.add(i)
1089 wanted.add(i)
1087
1090
1088 class followfilter(object):
1091 class followfilter(object):
1089 def __init__(self, onlyfirst=False):
1092 def __init__(self, onlyfirst=False):
1090 self.startrev = nullrev
1093 self.startrev = nullrev
1091 self.roots = set()
1094 self.roots = set()
1092 self.onlyfirst = onlyfirst
1095 self.onlyfirst = onlyfirst
1093
1096
1094 def match(self, rev):
1097 def match(self, rev):
1095 def realparents(rev):
1098 def realparents(rev):
1096 if self.onlyfirst:
1099 if self.onlyfirst:
1097 return repo.changelog.parentrevs(rev)[0:1]
1100 return repo.changelog.parentrevs(rev)[0:1]
1098 else:
1101 else:
1099 return filter(lambda x: x != nullrev,
1102 return filter(lambda x: x != nullrev,
1100 repo.changelog.parentrevs(rev))
1103 repo.changelog.parentrevs(rev))
1101
1104
1102 if self.startrev == nullrev:
1105 if self.startrev == nullrev:
1103 self.startrev = rev
1106 self.startrev = rev
1104 return True
1107 return True
1105
1108
1106 if rev > self.startrev:
1109 if rev > self.startrev:
1107 # forward: all descendants
1110 # forward: all descendants
1108 if not self.roots:
1111 if not self.roots:
1109 self.roots.add(self.startrev)
1112 self.roots.add(self.startrev)
1110 for parent in realparents(rev):
1113 for parent in realparents(rev):
1111 if parent in self.roots:
1114 if parent in self.roots:
1112 self.roots.add(rev)
1115 self.roots.add(rev)
1113 return True
1116 return True
1114 else:
1117 else:
1115 # backwards: all parents
1118 # backwards: all parents
1116 if not self.roots:
1119 if not self.roots:
1117 self.roots.update(realparents(self.startrev))
1120 self.roots.update(realparents(self.startrev))
1118 if rev in self.roots:
1121 if rev in self.roots:
1119 self.roots.remove(rev)
1122 self.roots.remove(rev)
1120 self.roots.update(realparents(rev))
1123 self.roots.update(realparents(rev))
1121 return True
1124 return True
1122
1125
1123 return False
1126 return False
1124
1127
1125 # it might be worthwhile to do this in the iterator if the rev range
1128 # it might be worthwhile to do this in the iterator if the rev range
1126 # is descending and the prune args are all within that range
1129 # is descending and the prune args are all within that range
1127 for rev in opts.get('prune', ()):
1130 for rev in opts.get('prune', ()):
1128 rev = repo.changelog.rev(repo.lookup(rev))
1131 rev = repo.changelog.rev(repo.lookup(rev))
1129 ff = followfilter()
1132 ff = followfilter()
1130 stop = min(revs[0], revs[-1])
1133 stop = min(revs[0], revs[-1])
1131 for x in xrange(rev, stop - 1, -1):
1134 for x in xrange(rev, stop - 1, -1):
1132 if ff.match(x):
1135 if ff.match(x):
1133 wanted.discard(x)
1136 wanted.discard(x)
1134
1137
1135 # Now that wanted is correctly initialized, we can iterate over the
1138 # Now that wanted is correctly initialized, we can iterate over the
1136 # revision range, yielding only revisions in wanted.
1139 # revision range, yielding only revisions in wanted.
1137 def iterate():
1140 def iterate():
1138 if follow and not match.files():
1141 if follow and not match.files():
1139 ff = followfilter(onlyfirst=opts.get('follow_first'))
1142 ff = followfilter(onlyfirst=opts.get('follow_first'))
1140 def want(rev):
1143 def want(rev):
1141 return ff.match(rev) and rev in wanted
1144 return ff.match(rev) and rev in wanted
1142 else:
1145 else:
1143 def want(rev):
1146 def want(rev):
1144 return rev in wanted
1147 return rev in wanted
1145
1148
1146 for i, window in increasing_windows(0, len(revs)):
1149 for i, window in increasing_windows(0, len(revs)):
1147 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1150 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1148 for rev in sorted(nrevs):
1151 for rev in sorted(nrevs):
1149 fns = fncache.get(rev)
1152 fns = fncache.get(rev)
1150 ctx = change(rev)
1153 ctx = change(rev)
1151 if not fns:
1154 if not fns:
1152 def fns_generator():
1155 def fns_generator():
1153 for f in ctx.files():
1156 for f in ctx.files():
1154 if match(f):
1157 if match(f):
1155 yield f
1158 yield f
1156 fns = fns_generator()
1159 fns = fns_generator()
1157 prepare(ctx, fns)
1160 prepare(ctx, fns)
1158 for rev in nrevs:
1161 for rev in nrevs:
1159 yield change(rev)
1162 yield change(rev)
1160 return iterate()
1163 return iterate()
1161
1164
1162 def add(ui, repo, match, dryrun, listsubrepos, prefix):
1165 def add(ui, repo, match, dryrun, listsubrepos, prefix):
1163 join = lambda f: os.path.join(prefix, f)
1166 join = lambda f: os.path.join(prefix, f)
1164 bad = []
1167 bad = []
1165 oldbad = match.bad
1168 oldbad = match.bad
1166 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1169 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1167 names = []
1170 names = []
1168 wctx = repo[None]
1171 wctx = repo[None]
1169 cca = None
1172 cca = None
1170 abort, warn = scmutil.checkportabilityalert(ui)
1173 abort, warn = scmutil.checkportabilityalert(ui)
1171 if abort or warn:
1174 if abort or warn:
1172 cca = scmutil.casecollisionauditor(ui, abort, wctx)
1175 cca = scmutil.casecollisionauditor(ui, abort, wctx)
1173 for f in repo.walk(match):
1176 for f in repo.walk(match):
1174 exact = match.exact(f)
1177 exact = match.exact(f)
1175 if exact or f not in repo.dirstate:
1178 if exact or f not in repo.dirstate:
1176 if cca:
1179 if cca:
1177 cca(f)
1180 cca(f)
1178 names.append(f)
1181 names.append(f)
1179 if ui.verbose or not exact:
1182 if ui.verbose or not exact:
1180 ui.status(_('adding %s\n') % match.rel(join(f)))
1183 ui.status(_('adding %s\n') % match.rel(join(f)))
1181
1184
1182 for subpath in wctx.substate:
1185 for subpath in wctx.substate:
1183 sub = wctx.sub(subpath)
1186 sub = wctx.sub(subpath)
1184 try:
1187 try:
1185 submatch = matchmod.narrowmatcher(subpath, match)
1188 submatch = matchmod.narrowmatcher(subpath, match)
1186 if listsubrepos:
1189 if listsubrepos:
1187 bad.extend(sub.add(ui, submatch, dryrun, prefix))
1190 bad.extend(sub.add(ui, submatch, dryrun, prefix))
1188 else:
1191 else:
1189 for f in sub.walk(submatch):
1192 for f in sub.walk(submatch):
1190 if submatch.exact(f):
1193 if submatch.exact(f):
1191 bad.extend(sub.add(ui, submatch, dryrun, prefix))
1194 bad.extend(sub.add(ui, submatch, dryrun, prefix))
1192 except error.LookupError:
1195 except error.LookupError:
1193 ui.status(_("skipping missing subrepository: %s\n")
1196 ui.status(_("skipping missing subrepository: %s\n")
1194 % join(subpath))
1197 % join(subpath))
1195
1198
1196 if not dryrun:
1199 if not dryrun:
1197 rejected = wctx.add(names, prefix)
1200 rejected = wctx.add(names, prefix)
1198 bad.extend(f for f in rejected if f in match.files())
1201 bad.extend(f for f in rejected if f in match.files())
1199 return bad
1202 return bad
1200
1203
1201 def duplicatecopies(repo, rev, p1):
1204 def duplicatecopies(repo, rev, p1):
1202 "Reproduce copies found in the source revision in the dirstate for grafts"
1205 "Reproduce copies found in the source revision in the dirstate for grafts"
1203 for dst, src in copies.pathcopies(repo[p1], repo[rev]).iteritems():
1206 for dst, src in copies.pathcopies(repo[p1], repo[rev]).iteritems():
1204 repo.dirstate.copy(src, dst)
1207 repo.dirstate.copy(src, dst)
1205
1208
1206 def commit(ui, repo, commitfunc, pats, opts):
1209 def commit(ui, repo, commitfunc, pats, opts):
1207 '''commit the specified files or all outstanding changes'''
1210 '''commit the specified files or all outstanding changes'''
1208 date = opts.get('date')
1211 date = opts.get('date')
1209 if date:
1212 if date:
1210 opts['date'] = util.parsedate(date)
1213 opts['date'] = util.parsedate(date)
1211 message = logmessage(ui, opts)
1214 message = logmessage(ui, opts)
1212
1215
1213 # extract addremove carefully -- this function can be called from a command
1216 # extract addremove carefully -- this function can be called from a command
1214 # that doesn't support addremove
1217 # that doesn't support addremove
1215 if opts.get('addremove'):
1218 if opts.get('addremove'):
1216 scmutil.addremove(repo, pats, opts)
1219 scmutil.addremove(repo, pats, opts)
1217
1220
1218 return commitfunc(ui, repo, message,
1221 return commitfunc(ui, repo, message,
1219 scmutil.match(repo[None], pats, opts), opts)
1222 scmutil.match(repo[None], pats, opts), opts)
1220
1223
1221 def commiteditor(repo, ctx, subs):
1224 def commiteditor(repo, ctx, subs):
1222 if ctx.description():
1225 if ctx.description():
1223 return ctx.description()
1226 return ctx.description()
1224 return commitforceeditor(repo, ctx, subs)
1227 return commitforceeditor(repo, ctx, subs)
1225
1228
1226 def commitforceeditor(repo, ctx, subs):
1229 def commitforceeditor(repo, ctx, subs):
1227 edittext = []
1230 edittext = []
1228 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1231 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1229 if ctx.description():
1232 if ctx.description():
1230 edittext.append(ctx.description())
1233 edittext.append(ctx.description())
1231 edittext.append("")
1234 edittext.append("")
1232 edittext.append("") # Empty line between message and comments.
1235 edittext.append("") # Empty line between message and comments.
1233 edittext.append(_("HG: Enter commit message."
1236 edittext.append(_("HG: Enter commit message."
1234 " Lines beginning with 'HG:' are removed."))
1237 " Lines beginning with 'HG:' are removed."))
1235 edittext.append(_("HG: Leave message empty to abort commit."))
1238 edittext.append(_("HG: Leave message empty to abort commit."))
1236 edittext.append("HG: --")
1239 edittext.append("HG: --")
1237 edittext.append(_("HG: user: %s") % ctx.user())
1240 edittext.append(_("HG: user: %s") % ctx.user())
1238 if ctx.p2():
1241 if ctx.p2():
1239 edittext.append(_("HG: branch merge"))
1242 edittext.append(_("HG: branch merge"))
1240 if ctx.branch():
1243 if ctx.branch():
1241 edittext.append(_("HG: branch '%s'") % ctx.branch())
1244 edittext.append(_("HG: branch '%s'") % ctx.branch())
1242 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1245 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1243 edittext.extend([_("HG: added %s") % f for f in added])
1246 edittext.extend([_("HG: added %s") % f for f in added])
1244 edittext.extend([_("HG: changed %s") % f for f in modified])
1247 edittext.extend([_("HG: changed %s") % f for f in modified])
1245 edittext.extend([_("HG: removed %s") % f for f in removed])
1248 edittext.extend([_("HG: removed %s") % f for f in removed])
1246 if not added and not modified and not removed:
1249 if not added and not modified and not removed:
1247 edittext.append(_("HG: no files changed"))
1250 edittext.append(_("HG: no files changed"))
1248 edittext.append("")
1251 edittext.append("")
1249 # run editor in the repository root
1252 # run editor in the repository root
1250 olddir = os.getcwd()
1253 olddir = os.getcwd()
1251 os.chdir(repo.root)
1254 os.chdir(repo.root)
1252 text = repo.ui.edit("\n".join(edittext), ctx.user())
1255 text = repo.ui.edit("\n".join(edittext), ctx.user())
1253 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
1256 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
1254 os.chdir(olddir)
1257 os.chdir(olddir)
1255
1258
1256 if not text.strip():
1259 if not text.strip():
1257 raise util.Abort(_("empty commit message"))
1260 raise util.Abort(_("empty commit message"))
1258
1261
1259 return text
1262 return text
1260
1263
1261 def command(table):
1264 def command(table):
1262 '''returns a function object bound to table which can be used as
1265 '''returns a function object bound to table which can be used as
1263 a decorator for populating table as a command table'''
1266 a decorator for populating table as a command table'''
1264
1267
1265 def cmd(name, options, synopsis=None):
1268 def cmd(name, options, synopsis=None):
1266 def decorator(func):
1269 def decorator(func):
1267 if synopsis:
1270 if synopsis:
1268 table[name] = func, options[:], synopsis
1271 table[name] = func, options[:], synopsis
1269 else:
1272 else:
1270 table[name] = func, options[:]
1273 table[name] = func, options[:]
1271 return func
1274 return func
1272 return decorator
1275 return decorator
1273
1276
1274 return cmd
1277 return cmd
@@ -1,25 +1,25 b''
1 changeset = 'changeset: {rev}:{node|short}\n{branches}{bookmarks}{tags}{parents}user: {author}\ndate: {date|date}\nsummary: {desc|firstline}\n\n'
1 changeset = 'changeset: {rev}:{node|short}\n{branches}{bookmarks}{tags}{parents}user: {author}\ndate: {date|date}\nsummary: {desc|firstline}\n\n'
2 changeset_quiet = '{rev}:{node|short}\n'
2 changeset_quiet = '{rev}:{node|short}\n'
3 changeset_verbose = 'changeset: {rev}:{node|short}\n{branches}{bookmarks}{tags}{parents}user: {author}\ndate: {date|date}\n{files}{file_copies_switch}description:\n{desc|strip}\n\n\n'
3 changeset_verbose = 'changeset: {rev}:{node|short}\n{branches}{bookmarks}{tags}{parents}user: {author}\ndate: {date|date}\n{files}{file_copies_switch}description:\n{desc|strip}\n\n\n'
4 changeset_debug = 'changeset: {rev}:{node}\n{branches}{bookmarks}{tags}{parents}{manifest}user: {author}\ndate: {date|date}\n{file_mods}{file_adds}{file_dels}{file_copies_switch}{extras}description:\n{desc|strip}\n\n\n'
4 changeset_debug = 'changeset: {rev}:{node}\n{branches}{bookmarks}{tags}phase: {phase}\n{parents}{manifest}user: {author}\ndate: {date|date}\n{file_mods}{file_adds}{file_dels}{file_copies_switch}{extras}description:\n{desc|strip}\n\n\n'
5 start_files = 'files: '
5 start_files = 'files: '
6 file = ' {file}'
6 file = ' {file}'
7 end_files = '\n'
7 end_files = '\n'
8 start_file_mods = 'files: '
8 start_file_mods = 'files: '
9 file_mod = ' {file_mod}'
9 file_mod = ' {file_mod}'
10 end_file_mods = '\n'
10 end_file_mods = '\n'
11 start_file_adds = 'files+: '
11 start_file_adds = 'files+: '
12 file_add = ' {file_add}'
12 file_add = ' {file_add}'
13 end_file_adds = '\n'
13 end_file_adds = '\n'
14 start_file_dels = 'files-: '
14 start_file_dels = 'files-: '
15 file_del = ' {file_del}'
15 file_del = ' {file_del}'
16 end_file_dels = '\n'
16 end_file_dels = '\n'
17 start_file_copies = 'copies: '
17 start_file_copies = 'copies: '
18 file_copy = ' {name} ({source})'
18 file_copy = ' {name} ({source})'
19 end_file_copies = '\n'
19 end_file_copies = '\n'
20 parent = 'parent: {rev}:{node|formatnode}\n'
20 parent = 'parent: {rev}:{node|formatnode}\n'
21 manifest = 'manifest: {rev}:{node}\n'
21 manifest = 'manifest: {rev}:{node}\n'
22 branch = 'branch: {branch}\n'
22 branch = 'branch: {branch}\n'
23 tag = 'tag: {tag}\n'
23 tag = 'tag: {tag}\n'
24 bookmark = 'bookmark: {bookmark}\n'
24 bookmark = 'bookmark: {bookmark}\n'
25 extra = 'extra: {key}={value|stringescape}\n'
25 extra = 'extra: {key}={value|stringescape}\n'
@@ -1,259 +1,261 b''
1 Create a repo with some stuff in it:
1 Create a repo with some stuff in it:
2
2
3 $ hg init a
3 $ hg init a
4 $ cd a
4 $ cd a
5 $ echo a > a
5 $ echo a > a
6 $ echo a > d
6 $ echo a > d
7 $ echo a > e
7 $ echo a > e
8 $ hg ci -qAm0
8 $ hg ci -qAm0
9 $ echo b > a
9 $ echo b > a
10 $ hg ci -m1 -u bar
10 $ hg ci -m1 -u bar
11 $ hg mv a b
11 $ hg mv a b
12 $ hg ci -m2
12 $ hg ci -m2
13 $ hg cp b c
13 $ hg cp b c
14 $ hg ci -m3 -u baz
14 $ hg ci -m3 -u baz
15 $ echo b > d
15 $ echo b > d
16 $ echo f > e
16 $ echo f > e
17 $ hg ci -m4
17 $ hg ci -m4
18 $ hg up -q 3
18 $ hg up -q 3
19 $ echo b > e
19 $ echo b > e
20 $ hg branch -q stable
20 $ hg branch -q stable
21 $ hg ci -m5
21 $ hg ci -m5
22 $ hg merge -q default --tool internal:local
22 $ hg merge -q default --tool internal:local
23 $ hg branch -q default
23 $ hg branch -q default
24 $ hg ci -m6
24 $ hg ci -m6
25
25
26 Need to specify a rev:
26 Need to specify a rev:
27
27
28 $ hg graft
28 $ hg graft
29 abort: no revisions specified
29 abort: no revisions specified
30 [255]
30 [255]
31
31
32 Can't graft ancestor:
32 Can't graft ancestor:
33
33
34 $ hg graft 1 2
34 $ hg graft 1 2
35 skipping ancestor revision 1
35 skipping ancestor revision 1
36 skipping ancestor revision 2
36 skipping ancestor revision 2
37 [255]
37 [255]
38
38
39 Can't graft with dirty wd:
39 Can't graft with dirty wd:
40
40
41 $ hg up -q 0
41 $ hg up -q 0
42 $ echo foo > a
42 $ echo foo > a
43 $ hg graft 1
43 $ hg graft 1
44 abort: outstanding uncommitted changes
44 abort: outstanding uncommitted changes
45 [255]
45 [255]
46 $ hg revert a
46 $ hg revert a
47
47
48 Graft a rename:
48 Graft a rename:
49
49
50 $ hg graft 2 -u foo
50 $ hg graft 2 -u foo
51 grafting revision 2
51 grafting revision 2
52 merging a and b to b
52 merging a and b to b
53 $ hg export tip --git
53 $ hg export tip --git
54 # HG changeset patch
54 # HG changeset patch
55 # User foo
55 # User foo
56 # Date 0 0
56 # Date 0 0
57 # Node ID d2e44c99fd3f31c176ea4efb9eca9f6306c81756
57 # Node ID d2e44c99fd3f31c176ea4efb9eca9f6306c81756
58 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
58 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
59 2
59 2
60
60
61 diff --git a/a b/b
61 diff --git a/a b/b
62 rename from a
62 rename from a
63 rename to b
63 rename to b
64 --- a/a
64 --- a/a
65 +++ b/b
65 +++ b/b
66 @@ -1,1 +1,1 @@
66 @@ -1,1 +1,1 @@
67 -a
67 -a
68 +b
68 +b
69
69
70 Look for extra:source
70 Look for extra:source
71
71
72 $ hg log --debug -r tip
72 $ hg log --debug -r tip
73 changeset: 7:d2e44c99fd3f31c176ea4efb9eca9f6306c81756
73 changeset: 7:d2e44c99fd3f31c176ea4efb9eca9f6306c81756
74 tag: tip
74 tag: tip
75 phase: draft
75 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
76 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
76 parent: -1:0000000000000000000000000000000000000000
77 parent: -1:0000000000000000000000000000000000000000
77 manifest: 7:5d59766436fd8fbcd38e7bebef0f6eaf3eebe637
78 manifest: 7:5d59766436fd8fbcd38e7bebef0f6eaf3eebe637
78 user: foo
79 user: foo
79 date: Thu Jan 01 00:00:00 1970 +0000
80 date: Thu Jan 01 00:00:00 1970 +0000
80 files+: b
81 files+: b
81 files-: a
82 files-: a
82 extra: branch=default
83 extra: branch=default
83 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
84 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
84 description:
85 description:
85 2
86 2
86
87
87
88
88
89
89 Graft out of order, skipping a merge and a duplicate
90 Graft out of order, skipping a merge and a duplicate
90
91
91 $ hg graft 1 5 4 3 'merge()' 2 --debug
92 $ hg graft 1 5 4 3 'merge()' 2 --debug
92 skipping ungraftable merge revision 6
93 skipping ungraftable merge revision 6
93 scanning for duplicate grafts
94 scanning for duplicate grafts
94 skipping already grafted revision 2
95 skipping already grafted revision 2
95 grafting revision 1
96 grafting revision 1
96 searching for copies back to rev 1
97 searching for copies back to rev 1
97 unmatched files in local:
98 unmatched files in local:
98 a.orig
99 a.orig
99 b
100 b
100 all copies found (* = to merge, ! = divergent):
101 all copies found (* = to merge, ! = divergent):
101 b -> a *
102 b -> a *
102 checking for directory renames
103 checking for directory renames
103 resolving manifests
104 resolving manifests
104 overwrite: False, partial: False
105 overwrite: False, partial: False
105 ancestor: 68795b066622, local: d2e44c99fd3f+, remote: 5d205f8b35b6
106 ancestor: 68795b066622, local: d2e44c99fd3f+, remote: 5d205f8b35b6
106 b: local copied/moved to a -> m
107 b: local copied/moved to a -> m
107 preserving b for resolve of b
108 preserving b for resolve of b
108 updating: b 1/1 files (100.00%)
109 updating: b 1/1 files (100.00%)
109 b
110 b
110 b: searching for copy revision for a
111 b: searching for copy revision for a
111 b: copy a:b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
112 b: copy a:b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
112 grafting revision 5
113 grafting revision 5
113 searching for copies back to rev 1
114 searching for copies back to rev 1
114 unmatched files in local:
115 unmatched files in local:
115 a.orig
116 a.orig
116 resolving manifests
117 resolving manifests
117 overwrite: False, partial: False
118 overwrite: False, partial: False
118 ancestor: 4c60f11aa304, local: 6f5ea6ac8b70+, remote: 97f8bfe72746
119 ancestor: 4c60f11aa304, local: 6f5ea6ac8b70+, remote: 97f8bfe72746
119 e: remote is newer -> g
120 e: remote is newer -> g
120 updating: e 1/1 files (100.00%)
121 updating: e 1/1 files (100.00%)
121 getting e
122 getting e
122 e
123 e
123 grafting revision 4
124 grafting revision 4
124 searching for copies back to rev 1
125 searching for copies back to rev 1
125 unmatched files in local:
126 unmatched files in local:
126 a.orig
127 a.orig
127 resolving manifests
128 resolving manifests
128 overwrite: False, partial: False
129 overwrite: False, partial: False
129 ancestor: 4c60f11aa304, local: 77eb504366ab+, remote: 9c233e8e184d
130 ancestor: 4c60f11aa304, local: 77eb504366ab+, remote: 9c233e8e184d
130 e: versions differ -> m
131 e: versions differ -> m
131 d: remote is newer -> g
132 d: remote is newer -> g
132 preserving e for resolve of e
133 preserving e for resolve of e
133 updating: d 1/2 files (50.00%)
134 updating: d 1/2 files (50.00%)
134 getting d
135 getting d
135 updating: e 2/2 files (100.00%)
136 updating: e 2/2 files (100.00%)
136 picked tool 'internal:merge' for e (binary False symlink False)
137 picked tool 'internal:merge' for e (binary False symlink False)
137 merging e
138 merging e
138 my e@77eb504366ab+ other e@9c233e8e184d ancestor e@68795b066622
139 my e@77eb504366ab+ other e@9c233e8e184d ancestor e@68795b066622
139 warning: conflicts during merge.
140 warning: conflicts during merge.
140 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
141 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
141 abort: unresolved conflicts, can't continue
142 abort: unresolved conflicts, can't continue
142 (use hg resolve and hg graft --continue)
143 (use hg resolve and hg graft --continue)
143 [255]
144 [255]
144
145
145 Continue without resolve should fail:
146 Continue without resolve should fail:
146
147
147 $ hg graft -c
148 $ hg graft -c
148 grafting revision 4
149 grafting revision 4
149 abort: unresolved merge conflicts (see hg help resolve)
150 abort: unresolved merge conflicts (see hg help resolve)
150 [255]
151 [255]
151
152
152 Fix up:
153 Fix up:
153
154
154 $ echo b > e
155 $ echo b > e
155 $ hg resolve -m e
156 $ hg resolve -m e
156
157
157 Continue with a revision should fail:
158 Continue with a revision should fail:
158
159
159 $ hg graft -c 6
160 $ hg graft -c 6
160 abort: can't specify --continue and revisions
161 abort: can't specify --continue and revisions
161 [255]
162 [255]
162
163
163 Continue for real, clobber usernames
164 Continue for real, clobber usernames
164
165
165 $ hg graft -c -U
166 $ hg graft -c -U
166 grafting revision 4
167 grafting revision 4
167 grafting revision 3
168 grafting revision 3
168
169
169 Compare with original:
170 Compare with original:
170
171
171 $ hg diff -r 6
172 $ hg diff -r 6
172 $ hg status --rev 0:. -C
173 $ hg status --rev 0:. -C
173 M d
174 M d
174 M e
175 M e
175 A b
176 A b
176 a
177 a
177 A c
178 A c
178 a
179 a
179 R a
180 R a
180
181
181 View graph:
182 View graph:
182
183
183 $ hg --config extensions.graphlog= log -G --template '{author}@{rev}: {desc}\n'
184 $ hg --config extensions.graphlog= log -G --template '{author}@{rev}: {desc}\n'
184 @ test@11: 3
185 @ test@11: 3
185 |
186 |
186 o test@10: 4
187 o test@10: 4
187 |
188 |
188 o test@9: 5
189 o test@9: 5
189 |
190 |
190 o bar@8: 1
191 o bar@8: 1
191 |
192 |
192 o foo@7: 2
193 o foo@7: 2
193 |
194 |
194 | o test@6: 6
195 | o test@6: 6
195 | |\
196 | |\
196 | | o test@5: 5
197 | | o test@5: 5
197 | | |
198 | | |
198 | o | test@4: 4
199 | o | test@4: 4
199 | |/
200 | |/
200 | o baz@3: 3
201 | o baz@3: 3
201 | |
202 | |
202 | o test@2: 2
203 | o test@2: 2
203 | |
204 | |
204 | o bar@1: 1
205 | o bar@1: 1
205 |/
206 |/
206 o test@0: 0
207 o test@0: 0
207
208
208 Graft again onto another branch should preserve the original source
209 Graft again onto another branch should preserve the original source
209 $ hg up -q 0
210 $ hg up -q 0
210 $ echo 'g'>g
211 $ echo 'g'>g
211 $ hg add g
212 $ hg add g
212 $ hg ci -m 7
213 $ hg ci -m 7
213 created new head
214 created new head
214 $ hg graft 7
215 $ hg graft 7
215 grafting revision 7
216 grafting revision 7
216
217
217 $ hg log -r 7 --template '{rev}:{node}\n'
218 $ hg log -r 7 --template '{rev}:{node}\n'
218 7:d2e44c99fd3f31c176ea4efb9eca9f6306c81756
219 7:d2e44c99fd3f31c176ea4efb9eca9f6306c81756
219 $ hg log -r 2 --template '{rev}:{node}\n'
220 $ hg log -r 2 --template '{rev}:{node}\n'
220 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
221 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
221
222
222 $ hg log --debug -r tip
223 $ hg log --debug -r tip
223 changeset: 13:39bb1d13572759bd1e6fc874fed1b12ece047a18
224 changeset: 13:39bb1d13572759bd1e6fc874fed1b12ece047a18
224 tag: tip
225 tag: tip
226 phase: draft
225 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
227 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
226 parent: -1:0000000000000000000000000000000000000000
228 parent: -1:0000000000000000000000000000000000000000
227 manifest: 13:0780e055d8f4cd12eadd5a2719481648f336f7a9
229 manifest: 13:0780e055d8f4cd12eadd5a2719481648f336f7a9
228 user: foo
230 user: foo
229 date: Thu Jan 01 00:00:00 1970 +0000
231 date: Thu Jan 01 00:00:00 1970 +0000
230 files+: b
232 files+: b
231 files-: a
233 files-: a
232 extra: branch=default
234 extra: branch=default
233 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
235 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
234 description:
236 description:
235 2
237 2
236
238
237
239
238 Disallow grafting an already grafted cset onto its original branch
240 Disallow grafting an already grafted cset onto its original branch
239 $ hg up -q 6
241 $ hg up -q 6
240 $ hg graft 7
242 $ hg graft 7
241 skipping already grafted revision 7 (was grafted from 2)
243 skipping already grafted revision 7 (was grafted from 2)
242 [255]
244 [255]
243
245
244 Disallow grafting already grafted csets with the same origin onto each other
246 Disallow grafting already grafted csets with the same origin onto each other
245 $ hg up -q 13
247 $ hg up -q 13
246 $ hg graft 2
248 $ hg graft 2
247 skipping already grafted revision 2
249 skipping already grafted revision 2
248 [255]
250 [255]
249 $ hg graft 7
251 $ hg graft 7
250 skipping already grafted revision 7 (same origin 2)
252 skipping already grafted revision 7 (same origin 2)
251 [255]
253 [255]
252
254
253 $ hg up -q 7
255 $ hg up -q 7
254 $ hg graft 2
256 $ hg graft 2
255 skipping already grafted revision 2
257 skipping already grafted revision 2
256 [255]
258 [255]
257 $ hg graft tip
259 $ hg graft tip
258 skipping already grafted revision 13 (same origin 2)
260 skipping already grafted revision 13 (same origin 2)
259 [255]
261 [255]
@@ -1,328 +1,329 b''
1 $ branchcache=.hg/cache/branchheads
1 $ branchcache=.hg/cache/branchheads
2
2
3 $ hg init t
3 $ hg init t
4 $ cd t
4 $ cd t
5
5
6 $ hg branches
6 $ hg branches
7 $ echo foo > a
7 $ echo foo > a
8 $ hg add a
8 $ hg add a
9 $ hg ci -m "initial"
9 $ hg ci -m "initial"
10 $ hg branch foo
10 $ hg branch foo
11 marked working directory as branch foo
11 marked working directory as branch foo
12 (branches are permanent and global, did you want a bookmark?)
12 (branches are permanent and global, did you want a bookmark?)
13 $ hg branch
13 $ hg branch
14 foo
14 foo
15 $ hg ci -m "add branch name"
15 $ hg ci -m "add branch name"
16 $ hg branch bar
16 $ hg branch bar
17 marked working directory as branch bar
17 marked working directory as branch bar
18 (branches are permanent and global, did you want a bookmark?)
18 (branches are permanent and global, did you want a bookmark?)
19 $ hg ci -m "change branch name"
19 $ hg ci -m "change branch name"
20
20
21 Branch shadowing:
21 Branch shadowing:
22
22
23 $ hg branch default
23 $ hg branch default
24 abort: a branch of the same name already exists
24 abort: a branch of the same name already exists
25 (use 'hg update' to switch to it)
25 (use 'hg update' to switch to it)
26 [255]
26 [255]
27
27
28 $ hg branch -f default
28 $ hg branch -f default
29 marked working directory as branch default
29 marked working directory as branch default
30 (branches are permanent and global, did you want a bookmark?)
30 (branches are permanent and global, did you want a bookmark?)
31
31
32 $ hg ci -m "clear branch name"
32 $ hg ci -m "clear branch name"
33 created new head
33 created new head
34
34
35 There should be only one default branch head
35 There should be only one default branch head
36
36
37 $ hg heads .
37 $ hg heads .
38 changeset: 3:1c28f494dae6
38 changeset: 3:1c28f494dae6
39 tag: tip
39 tag: tip
40 user: test
40 user: test
41 date: Thu Jan 01 00:00:00 1970 +0000
41 date: Thu Jan 01 00:00:00 1970 +0000
42 summary: clear branch name
42 summary: clear branch name
43
43
44
44
45 $ hg co foo
45 $ hg co foo
46 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
47 $ hg branch
47 $ hg branch
48 foo
48 foo
49 $ echo bleah > a
49 $ echo bleah > a
50 $ hg ci -m "modify a branch"
50 $ hg ci -m "modify a branch"
51
51
52 $ hg merge default
52 $ hg merge default
53 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
53 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
54 (branch merge, don't forget to commit)
54 (branch merge, don't forget to commit)
55
55
56 $ hg branch
56 $ hg branch
57 foo
57 foo
58 $ hg ci -m "merge"
58 $ hg ci -m "merge"
59
59
60 $ hg log
60 $ hg log
61 changeset: 5:530046499edf
61 changeset: 5:530046499edf
62 branch: foo
62 branch: foo
63 tag: tip
63 tag: tip
64 parent: 4:adf1a74a7f7b
64 parent: 4:adf1a74a7f7b
65 parent: 3:1c28f494dae6
65 parent: 3:1c28f494dae6
66 user: test
66 user: test
67 date: Thu Jan 01 00:00:00 1970 +0000
67 date: Thu Jan 01 00:00:00 1970 +0000
68 summary: merge
68 summary: merge
69
69
70 changeset: 4:adf1a74a7f7b
70 changeset: 4:adf1a74a7f7b
71 branch: foo
71 branch: foo
72 parent: 1:6c0e42da283a
72 parent: 1:6c0e42da283a
73 user: test
73 user: test
74 date: Thu Jan 01 00:00:00 1970 +0000
74 date: Thu Jan 01 00:00:00 1970 +0000
75 summary: modify a branch
75 summary: modify a branch
76
76
77 changeset: 3:1c28f494dae6
77 changeset: 3:1c28f494dae6
78 user: test
78 user: test
79 date: Thu Jan 01 00:00:00 1970 +0000
79 date: Thu Jan 01 00:00:00 1970 +0000
80 summary: clear branch name
80 summary: clear branch name
81
81
82 changeset: 2:c21617b13b22
82 changeset: 2:c21617b13b22
83 branch: bar
83 branch: bar
84 user: test
84 user: test
85 date: Thu Jan 01 00:00:00 1970 +0000
85 date: Thu Jan 01 00:00:00 1970 +0000
86 summary: change branch name
86 summary: change branch name
87
87
88 changeset: 1:6c0e42da283a
88 changeset: 1:6c0e42da283a
89 branch: foo
89 branch: foo
90 user: test
90 user: test
91 date: Thu Jan 01 00:00:00 1970 +0000
91 date: Thu Jan 01 00:00:00 1970 +0000
92 summary: add branch name
92 summary: add branch name
93
93
94 changeset: 0:db01e8ea3388
94 changeset: 0:db01e8ea3388
95 user: test
95 user: test
96 date: Thu Jan 01 00:00:00 1970 +0000
96 date: Thu Jan 01 00:00:00 1970 +0000
97 summary: initial
97 summary: initial
98
98
99 $ hg branches
99 $ hg branches
100 foo 5:530046499edf
100 foo 5:530046499edf
101 default 3:1c28f494dae6 (inactive)
101 default 3:1c28f494dae6 (inactive)
102 bar 2:c21617b13b22 (inactive)
102 bar 2:c21617b13b22 (inactive)
103
103
104 $ hg branches -q
104 $ hg branches -q
105 foo
105 foo
106 default
106 default
107 bar
107 bar
108
108
109 Test for invalid branch cache:
109 Test for invalid branch cache:
110
110
111 $ hg rollback
111 $ hg rollback
112 repository tip rolled back to revision 4 (undo commit)
112 repository tip rolled back to revision 4 (undo commit)
113 working directory now based on revisions 4 and 3
113 working directory now based on revisions 4 and 3
114
114
115 $ cp $branchcache .hg/bc-invalid
115 $ cp $branchcache .hg/bc-invalid
116
116
117 $ hg log -r foo
117 $ hg log -r foo
118 changeset: 4:adf1a74a7f7b
118 changeset: 4:adf1a74a7f7b
119 branch: foo
119 branch: foo
120 tag: tip
120 tag: tip
121 parent: 1:6c0e42da283a
121 parent: 1:6c0e42da283a
122 user: test
122 user: test
123 date: Thu Jan 01 00:00:00 1970 +0000
123 date: Thu Jan 01 00:00:00 1970 +0000
124 summary: modify a branch
124 summary: modify a branch
125
125
126 $ cp .hg/bc-invalid $branchcache
126 $ cp .hg/bc-invalid $branchcache
127
127
128 $ hg --debug log -r foo
128 $ hg --debug log -r foo
129 invalidating branch cache (tip differs)
129 invalidating branch cache (tip differs)
130 changeset: 4:adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6
130 changeset: 4:adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6
131 branch: foo
131 branch: foo
132 tag: tip
132 tag: tip
133 phase: draft
133 parent: 1:6c0e42da283a56b5edc5b4fadb491365ec7f5fa8
134 parent: 1:6c0e42da283a56b5edc5b4fadb491365ec7f5fa8
134 parent: -1:0000000000000000000000000000000000000000
135 parent: -1:0000000000000000000000000000000000000000
135 manifest: 1:8c342a37dfba0b3d3ce073562a00d8a813c54ffe
136 manifest: 1:8c342a37dfba0b3d3ce073562a00d8a813c54ffe
136 user: test
137 user: test
137 date: Thu Jan 01 00:00:00 1970 +0000
138 date: Thu Jan 01 00:00:00 1970 +0000
138 files: a
139 files: a
139 extra: branch=foo
140 extra: branch=foo
140 description:
141 description:
141 modify a branch
142 modify a branch
142
143
143
144
144 $ rm $branchcache
145 $ rm $branchcache
145 $ echo corrupted > $branchcache
146 $ echo corrupted > $branchcache
146
147
147 $ hg log -qr foo
148 $ hg log -qr foo
148 4:adf1a74a7f7b
149 4:adf1a74a7f7b
149
150
150 $ cat $branchcache
151 $ cat $branchcache
151 adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 4
152 adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 4
152 1c28f494dae69a2f8fc815059d257eccf3fcfe75 default
153 1c28f494dae69a2f8fc815059d257eccf3fcfe75 default
153 adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 foo
154 adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 foo
154 c21617b13b220988e7a2e26290fbe4325ffa7139 bar
155 c21617b13b220988e7a2e26290fbe4325ffa7139 bar
155
156
156 Push should update the branch cache:
157 Push should update the branch cache:
157
158
158 $ hg init ../target
159 $ hg init ../target
159
160
160 Pushing just rev 0:
161 Pushing just rev 0:
161
162
162 $ hg push -qr 0 ../target
163 $ hg push -qr 0 ../target
163
164
164 $ cat ../target/$branchcache
165 $ cat ../target/$branchcache
165 db01e8ea3388fd3c7c94e1436ea2bd6a53d581c5 0
166 db01e8ea3388fd3c7c94e1436ea2bd6a53d581c5 0
166 db01e8ea3388fd3c7c94e1436ea2bd6a53d581c5 default
167 db01e8ea3388fd3c7c94e1436ea2bd6a53d581c5 default
167
168
168 Pushing everything:
169 Pushing everything:
169
170
170 $ hg push -qf ../target
171 $ hg push -qf ../target
171
172
172 $ cat ../target/$branchcache
173 $ cat ../target/$branchcache
173 adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 4
174 adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 4
174 1c28f494dae69a2f8fc815059d257eccf3fcfe75 default
175 1c28f494dae69a2f8fc815059d257eccf3fcfe75 default
175 adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 foo
176 adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 foo
176 c21617b13b220988e7a2e26290fbe4325ffa7139 bar
177 c21617b13b220988e7a2e26290fbe4325ffa7139 bar
177
178
178 Update with no arguments: tipmost revision of the current branch:
179 Update with no arguments: tipmost revision of the current branch:
179
180
180 $ hg up -q -C 0
181 $ hg up -q -C 0
181 $ hg up -q
182 $ hg up -q
182 $ hg id
183 $ hg id
183 1c28f494dae6
184 1c28f494dae6
184
185
185 $ hg up -q 1
186 $ hg up -q 1
186 $ hg up -q
187 $ hg up -q
187 $ hg id
188 $ hg id
188 adf1a74a7f7b (foo) tip
189 adf1a74a7f7b (foo) tip
189
190
190 $ hg branch foobar
191 $ hg branch foobar
191 marked working directory as branch foobar
192 marked working directory as branch foobar
192 (branches are permanent and global, did you want a bookmark?)
193 (branches are permanent and global, did you want a bookmark?)
193
194
194 $ hg up
195 $ hg up
195 abort: branch foobar not found
196 abort: branch foobar not found
196 [255]
197 [255]
197
198
198 Fastforward merge:
199 Fastforward merge:
199
200
200 $ hg branch ff
201 $ hg branch ff
201 marked working directory as branch ff
202 marked working directory as branch ff
202 (branches are permanent and global, did you want a bookmark?)
203 (branches are permanent and global, did you want a bookmark?)
203
204
204 $ echo ff > ff
205 $ echo ff > ff
205 $ hg ci -Am'fast forward'
206 $ hg ci -Am'fast forward'
206 adding ff
207 adding ff
207
208
208 $ hg up foo
209 $ hg up foo
209 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
210 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
210
211
211 $ hg merge ff
212 $ hg merge ff
212 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
213 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
213 (branch merge, don't forget to commit)
214 (branch merge, don't forget to commit)
214
215
215 $ hg branch
216 $ hg branch
216 foo
217 foo
217 $ hg commit -m'Merge ff into foo'
218 $ hg commit -m'Merge ff into foo'
218 $ hg parents
219 $ hg parents
219 changeset: 6:185ffbfefa30
220 changeset: 6:185ffbfefa30
220 branch: foo
221 branch: foo
221 tag: tip
222 tag: tip
222 parent: 4:adf1a74a7f7b
223 parent: 4:adf1a74a7f7b
223 parent: 5:1a3c27dc5e11
224 parent: 5:1a3c27dc5e11
224 user: test
225 user: test
225 date: Thu Jan 01 00:00:00 1970 +0000
226 date: Thu Jan 01 00:00:00 1970 +0000
226 summary: Merge ff into foo
227 summary: Merge ff into foo
227
228
228 $ hg manifest
229 $ hg manifest
229 a
230 a
230 ff
231 ff
231
232
232
233
233 Test merging, add 3 default heads and one test head:
234 Test merging, add 3 default heads and one test head:
234
235
235 $ cd ..
236 $ cd ..
236 $ hg init merges
237 $ hg init merges
237 $ cd merges
238 $ cd merges
238 $ echo a > a
239 $ echo a > a
239 $ hg ci -Ama
240 $ hg ci -Ama
240 adding a
241 adding a
241
242
242 $ echo b > b
243 $ echo b > b
243 $ hg ci -Amb
244 $ hg ci -Amb
244 adding b
245 adding b
245
246
246 $ hg up 0
247 $ hg up 0
247 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
248 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
248 $ echo c > c
249 $ echo c > c
249 $ hg ci -Amc
250 $ hg ci -Amc
250 adding c
251 adding c
251 created new head
252 created new head
252
253
253 $ hg up 0
254 $ hg up 0
254 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
255 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
255 $ echo d > d
256 $ echo d > d
256 $ hg ci -Amd
257 $ hg ci -Amd
257 adding d
258 adding d
258 created new head
259 created new head
259
260
260 $ hg up 0
261 $ hg up 0
261 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
262 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
262 $ hg branch test
263 $ hg branch test
263 marked working directory as branch test
264 marked working directory as branch test
264 (branches are permanent and global, did you want a bookmark?)
265 (branches are permanent and global, did you want a bookmark?)
265 $ echo e >> e
266 $ echo e >> e
266 $ hg ci -Ame
267 $ hg ci -Ame
267 adding e
268 adding e
268
269
269 $ hg log
270 $ hg log
270 changeset: 4:3a1e01ed1df4
271 changeset: 4:3a1e01ed1df4
271 branch: test
272 branch: test
272 tag: tip
273 tag: tip
273 parent: 0:cb9a9f314b8b
274 parent: 0:cb9a9f314b8b
274 user: test
275 user: test
275 date: Thu Jan 01 00:00:00 1970 +0000
276 date: Thu Jan 01 00:00:00 1970 +0000
276 summary: e
277 summary: e
277
278
278 changeset: 3:980f7dc84c29
279 changeset: 3:980f7dc84c29
279 parent: 0:cb9a9f314b8b
280 parent: 0:cb9a9f314b8b
280 user: test
281 user: test
281 date: Thu Jan 01 00:00:00 1970 +0000
282 date: Thu Jan 01 00:00:00 1970 +0000
282 summary: d
283 summary: d
283
284
284 changeset: 2:d36c0562f908
285 changeset: 2:d36c0562f908
285 parent: 0:cb9a9f314b8b
286 parent: 0:cb9a9f314b8b
286 user: test
287 user: test
287 date: Thu Jan 01 00:00:00 1970 +0000
288 date: Thu Jan 01 00:00:00 1970 +0000
288 summary: c
289 summary: c
289
290
290 changeset: 1:d2ae7f538514
291 changeset: 1:d2ae7f538514
291 user: test
292 user: test
292 date: Thu Jan 01 00:00:00 1970 +0000
293 date: Thu Jan 01 00:00:00 1970 +0000
293 summary: b
294 summary: b
294
295
295 changeset: 0:cb9a9f314b8b
296 changeset: 0:cb9a9f314b8b
296 user: test
297 user: test
297 date: Thu Jan 01 00:00:00 1970 +0000
298 date: Thu Jan 01 00:00:00 1970 +0000
298 summary: a
299 summary: a
299
300
300 Implicit merge with test branch as parent:
301 Implicit merge with test branch as parent:
301
302
302 $ hg merge
303 $ hg merge
303 abort: branch 'test' has one head - please merge with an explicit rev
304 abort: branch 'test' has one head - please merge with an explicit rev
304 (run 'hg heads' to see all heads)
305 (run 'hg heads' to see all heads)
305 [255]
306 [255]
306 $ hg up -C default
307 $ hg up -C default
307 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
308 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
308
309
309 Implicit merge with default branch as parent:
310 Implicit merge with default branch as parent:
310
311
311 $ hg merge
312 $ hg merge
312 abort: branch 'default' has 3 heads - please merge with an explicit rev
313 abort: branch 'default' has 3 heads - please merge with an explicit rev
313 (run 'hg heads .' to see heads)
314 (run 'hg heads .' to see heads)
314 [255]
315 [255]
315
316
316 3 branch heads, explicit merge required:
317 3 branch heads, explicit merge required:
317
318
318 $ hg merge 2
319 $ hg merge 2
319 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
320 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
320 (branch merge, don't forget to commit)
321 (branch merge, don't forget to commit)
321 $ hg ci -m merge
322 $ hg ci -m merge
322
323
323 2 branch heads, implicit merge works:
324 2 branch heads, implicit merge works:
324
325
325 $ hg merge
326 $ hg merge
326 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
327 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
327 (branch merge, don't forget to commit)
328 (branch merge, don't forget to commit)
328
329
@@ -1,297 +1,404 b''
1 $ alias hglog='hg log --template "{rev} {phaseidx} {desc}\n"'
1 $ alias hglog='hg log --template "{rev} {phaseidx} {desc}\n"'
2 $ mkcommit() {
2 $ mkcommit() {
3 > echo "$1" > "$1"
3 > echo "$1" > "$1"
4 > hg add "$1"
4 > hg add "$1"
5 > message="$1"
5 > message="$1"
6 > shift
6 > shift
7 > hg ci -m "$message" $*
7 > hg ci -m "$message" $*
8 > }
8 > }
9
9
10 $ hg init initialrepo
10 $ hg init initialrepo
11 $ cd initialrepo
11 $ cd initialrepo
12 $ mkcommit A
12 $ mkcommit A
13
13
14 New commit are draft by default
14 New commit are draft by default
15
15
16 $ hglog
16 $ hglog
17 0 1 A
17 0 1 A
18
18
19 Following commit are draft too
19 Following commit are draft too
20
20
21 $ mkcommit B
21 $ mkcommit B
22
22
23 $ hglog
23 $ hglog
24 1 1 B
24 1 1 B
25 0 1 A
25 0 1 A
26
26
27 Draft commit are properly created over public one:
27 Draft commit are properly created over public one:
28
28
29 $ hg phase --public .
29 $ hg phase --public .
30 $ hglog
30 $ hglog
31 1 0 B
31 1 0 B
32 0 0 A
32 0 0 A
33
33
34 $ mkcommit C
34 $ mkcommit C
35 $ mkcommit D
35 $ mkcommit D
36
36
37 $ hglog
37 $ hglog
38 3 1 D
38 3 1 D
39 2 1 C
39 2 1 C
40 1 0 B
40 1 0 B
41 0 0 A
41 0 0 A
42
42
43 Test creating changeset as secret
43 Test creating changeset as secret
44
44
45 $ mkcommit E --config phases.new-commit=2
45 $ mkcommit E --config phases.new-commit=2
46 $ hglog
46 $ hglog
47 4 2 E
47 4 2 E
48 3 1 D
48 3 1 D
49 2 1 C
49 2 1 C
50 1 0 B
50 1 0 B
51 0 0 A
51 0 0 A
52
52
53 Test the secret property is inherited
53 Test the secret property is inherited
54
54
55 $ mkcommit H
55 $ mkcommit H
56 $ hglog
56 $ hglog
57 5 2 H
57 5 2 H
58 4 2 E
58 4 2 E
59 3 1 D
59 3 1 D
60 2 1 C
60 2 1 C
61 1 0 B
61 1 0 B
62 0 0 A
62 0 0 A
63
63
64 Even on merge
64 Even on merge
65
65
66 $ hg up -q 1
66 $ hg up -q 1
67 $ mkcommit "B'"
67 $ mkcommit "B'"
68 created new head
68 created new head
69 $ hglog
69 $ hglog
70 6 1 B'
70 6 1 B'
71 5 2 H
71 5 2 H
72 4 2 E
72 4 2 E
73 3 1 D
73 3 1 D
74 2 1 C
74 2 1 C
75 1 0 B
75 1 0 B
76 0 0 A
76 0 0 A
77 $ hg merge 4 # E
77 $ hg merge 4 # E
78 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
78 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 (branch merge, don't forget to commit)
79 (branch merge, don't forget to commit)
80 $ hg ci -m "merge B' and E"
80 $ hg ci -m "merge B' and E"
81 $ hglog
81 $ hglog
82 7 2 merge B' and E
82 7 2 merge B' and E
83 6 1 B'
83 6 1 B'
84 5 2 H
84 5 2 H
85 4 2 E
85 4 2 E
86 3 1 D
86 3 1 D
87 2 1 C
87 2 1 C
88 1 0 B
88 1 0 B
89 0 0 A
89 0 0 A
90
90
91 Test secret changeset are not pushed
91 Test secret changeset are not pushed
92
92
93 $ hg init ../push-dest
93 $ hg init ../push-dest
94 $ cat > ../push-dest/.hg/hgrc << EOF
94 $ cat > ../push-dest/.hg/hgrc << EOF
95 > [phases]
95 > [phases]
96 > publish=False
96 > publish=False
97 > EOF
97 > EOF
98 $ hg outgoing ../push-dest --template='{rev} {phase} {desc|firstline}\n'
98 $ hg outgoing ../push-dest --template='{rev} {phase} {desc|firstline}\n'
99 comparing with ../push-dest
99 comparing with ../push-dest
100 searching for changes
100 searching for changes
101 0 public A
101 0 public A
102 1 public B
102 1 public B
103 2 draft C
103 2 draft C
104 3 draft D
104 3 draft D
105 6 draft B'
105 6 draft B'
106 $ hg outgoing -r default ../push-dest --template='{rev} {phase} {desc|firstline}\n'
106 $ hg outgoing -r default ../push-dest --template='{rev} {phase} {desc|firstline}\n'
107 comparing with ../push-dest
107 comparing with ../push-dest
108 searching for changes
108 searching for changes
109 0 public A
109 0 public A
110 1 public B
110 1 public B
111 2 draft C
111 2 draft C
112 3 draft D
112 3 draft D
113 6 draft B'
113 6 draft B'
114
114
115 $ hg push ../push-dest -f # force because we push multiple heads
115 $ hg push ../push-dest -f # force because we push multiple heads
116 pushing to ../push-dest
116 pushing to ../push-dest
117 searching for changes
117 searching for changes
118 adding changesets
118 adding changesets
119 adding manifests
119 adding manifests
120 adding file changes
120 adding file changes
121 added 5 changesets with 5 changes to 5 files (+1 heads)
121 added 5 changesets with 5 changes to 5 files (+1 heads)
122 $ hglog
122 $ hglog
123 7 2 merge B' and E
123 7 2 merge B' and E
124 6 1 B'
124 6 1 B'
125 5 2 H
125 5 2 H
126 4 2 E
126 4 2 E
127 3 1 D
127 3 1 D
128 2 1 C
128 2 1 C
129 1 0 B
129 1 0 B
130 0 0 A
130 0 0 A
131 $ cd ../push-dest
131 $ cd ../push-dest
132 $ hglog
132 $ hglog
133 4 1 B'
133 4 1 B'
134 3 1 D
134 3 1 D
135 2 1 C
135 2 1 C
136 1 0 B
136 1 0 B
137 0 0 A
137 0 0 A
138 $ cd ..
138 $ cd ..
139
139
140 Test secret changeset are not pull
140 Test secret changeset are not pull
141
141
142 $ hg init pull-dest
142 $ hg init pull-dest
143 $ cd pull-dest
143 $ cd pull-dest
144 $ hg pull ../initialrepo
144 $ hg pull ../initialrepo
145 pulling from ../initialrepo
145 pulling from ../initialrepo
146 requesting all changes
146 requesting all changes
147 adding changesets
147 adding changesets
148 adding manifests
148 adding manifests
149 adding file changes
149 adding file changes
150 added 5 changesets with 5 changes to 5 files (+1 heads)
150 added 5 changesets with 5 changes to 5 files (+1 heads)
151 (run 'hg heads' to see heads, 'hg merge' to merge)
151 (run 'hg heads' to see heads, 'hg merge' to merge)
152 $ hglog
152 $ hglog
153 4 0 B'
153 4 0 B'
154 3 0 D
154 3 0 D
155 2 0 C
155 2 0 C
156 1 0 B
156 1 0 B
157 0 0 A
157 0 0 A
158 $ cd ..
158 $ cd ..
159
159
160 But secret can still be bundled explicitly
160 But secret can still be bundled explicitly
161
161
162 $ cd initialrepo
162 $ cd initialrepo
163 $ hg bundle --base '4^' -r 'children(4)' ../secret-bundle.hg
163 $ hg bundle --base '4^' -r 'children(4)' ../secret-bundle.hg
164 4 changesets found
164 4 changesets found
165 $ cd ..
165 $ cd ..
166
166
167 Test secret changeset are not cloned
167 Test secret changeset are not cloned
168 (during local clone)
168 (during local clone)
169
169
170 $ hg clone -qU initialrepo clone-dest
170 $ hg clone -qU initialrepo clone-dest
171 $ hglog -R clone-dest
171 $ hglog -R clone-dest
172 4 0 B'
172 4 0 B'
173 3 0 D
173 3 0 D
174 2 0 C
174 2 0 C
175 1 0 B
175 1 0 B
176 0 0 A
176 0 0 A
177
177
178 Test revset
178 Test revset
179
179
180 $ cd initialrepo
180 $ cd initialrepo
181 $ hglog -r 'public()'
181 $ hglog -r 'public()'
182 0 0 A
182 0 0 A
183 1 0 B
183 1 0 B
184 $ hglog -r 'draft()'
184 $ hglog -r 'draft()'
185 2 1 C
185 2 1 C
186 3 1 D
186 3 1 D
187 6 1 B'
187 6 1 B'
188 $ hglog -r 'secret()'
188 $ hglog -r 'secret()'
189 4 2 E
189 4 2 E
190 5 2 H
190 5 2 H
191 7 2 merge B' and E
191 7 2 merge B' and E
192
192
193 test that phase are displayed in log at debug level
194
195 $ hg log --debug
196 changeset: 7:17a481b3bccb796c0521ae97903d81c52bfee4af
197 tag: tip
198 phase: secret
199 parent: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
200 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
201 manifest: 7:5e724ffacba267b2ab726c91fc8b650710deaaa8
202 user: test
203 date: Thu Jan 01 00:00:00 1970 +0000
204 files+: C D E
205 extra: branch=default
206 description:
207 merge B' and E
208
209
210 changeset: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
211 phase: draft
212 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
213 parent: -1:0000000000000000000000000000000000000000
214 manifest: 6:ab8bfef2392903058bf4ebb9e7746e8d7026b27a
215 user: test
216 date: Thu Jan 01 00:00:00 1970 +0000
217 files+: B'
218 extra: branch=default
219 description:
220 B'
221
222
223 changeset: 5:a030c6be5127abc010fcbff1851536552e6951a8
224 phase: secret
225 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
226 parent: -1:0000000000000000000000000000000000000000
227 manifest: 5:5c710aa854874fe3d5fa7192e77bdb314cc08b5a
228 user: test
229 date: Thu Jan 01 00:00:00 1970 +0000
230 files+: H
231 extra: branch=default
232 description:
233 H
234
235
236 changeset: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
237 phase: secret
238 parent: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
239 parent: -1:0000000000000000000000000000000000000000
240 manifest: 4:7173fd1c27119750b959e3a0f47ed78abe75d6dc
241 user: test
242 date: Thu Jan 01 00:00:00 1970 +0000
243 files+: E
244 extra: branch=default
245 description:
246 E
247
248
249 changeset: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
250 phase: draft
251 parent: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
252 parent: -1:0000000000000000000000000000000000000000
253 manifest: 3:6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c
254 user: test
255 date: Thu Jan 01 00:00:00 1970 +0000
256 files+: D
257 extra: branch=default
258 description:
259 D
260
261
262 changeset: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
263 phase: draft
264 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
265 parent: -1:0000000000000000000000000000000000000000
266 manifest: 2:66a5a01817fdf5239c273802b5b7618d051c89e4
267 user: test
268 date: Thu Jan 01 00:00:00 1970 +0000
269 files+: C
270 extra: branch=default
271 description:
272 C
273
274
275 changeset: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
276 parent: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
277 parent: -1:0000000000000000000000000000000000000000
278 manifest: 1:cb5cbbc1bfbf24cc34b9e8c16914e9caa2d2a7fd
279 user: test
280 date: Thu Jan 01 00:00:00 1970 +0000
281 files+: B
282 extra: branch=default
283 description:
284 B
285
286
287 changeset: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
288 parent: -1:0000000000000000000000000000000000000000
289 parent: -1:0000000000000000000000000000000000000000
290 manifest: 0:007d8c9d88841325f5c6b06371b35b4e8a2b1a83
291 user: test
292 date: Thu Jan 01 00:00:00 1970 +0000
293 files+: A
294 extra: branch=default
295 description:
296 A
297
298
299
193 Test phase command
300 Test phase command
194 ===================
301 ===================
195
302
196 initial picture
303 initial picture
197
304
198 $ cat >> $HGRCPATH << EOF
305 $ cat >> $HGRCPATH << EOF
199 > [extensions]
306 > [extensions]
200 > hgext.graphlog=
307 > hgext.graphlog=
201 > EOF
308 > EOF
202 $ hg log -G --template "{rev} {phase} {desc}\n"
309 $ hg log -G --template "{rev} {phase} {desc}\n"
203 @ 7 secret merge B' and E
310 @ 7 secret merge B' and E
204 |\
311 |\
205 | o 6 draft B'
312 | o 6 draft B'
206 | |
313 | |
207 +---o 5 secret H
314 +---o 5 secret H
208 | |
315 | |
209 o | 4 secret E
316 o | 4 secret E
210 | |
317 | |
211 o | 3 draft D
318 o | 3 draft D
212 | |
319 | |
213 o | 2 draft C
320 o | 2 draft C
214 |/
321 |/
215 o 1 public B
322 o 1 public B
216 |
323 |
217 o 0 public A
324 o 0 public A
218
325
219
326
220 display changesets phase
327 display changesets phase
221
328
222 (mixing -r and plain rev specification)
329 (mixing -r and plain rev specification)
223
330
224 $ hg phase 1::4 -r 7
331 $ hg phase 1::4 -r 7
225 1: public
332 1: public
226 2: draft
333 2: draft
227 3: draft
334 3: draft
228 4: secret
335 4: secret
229 7: secret
336 7: secret
230
337
231
338
232 move changeset forward
339 move changeset forward
233
340
234 (with -r option)
341 (with -r option)
235
342
236 $ hg phase --public -r 2
343 $ hg phase --public -r 2
237 $ hg log -G --template "{rev} {phase} {desc}\n"
344 $ hg log -G --template "{rev} {phase} {desc}\n"
238 @ 7 secret merge B' and E
345 @ 7 secret merge B' and E
239 |\
346 |\
240 | o 6 draft B'
347 | o 6 draft B'
241 | |
348 | |
242 +---o 5 secret H
349 +---o 5 secret H
243 | |
350 | |
244 o | 4 secret E
351 o | 4 secret E
245 | |
352 | |
246 o | 3 draft D
353 o | 3 draft D
247 | |
354 | |
248 o | 2 public C
355 o | 2 public C
249 |/
356 |/
250 o 1 public B
357 o 1 public B
251 |
358 |
252 o 0 public A
359 o 0 public A
253
360
254
361
255 move changeset backward
362 move changeset backward
256
363
257 (without -r option)
364 (without -r option)
258
365
259 $ hg phase --draft --force 2
366 $ hg phase --draft --force 2
260 $ hg log -G --template "{rev} {phase} {desc}\n"
367 $ hg log -G --template "{rev} {phase} {desc}\n"
261 @ 7 secret merge B' and E
368 @ 7 secret merge B' and E
262 |\
369 |\
263 | o 6 draft B'
370 | o 6 draft B'
264 | |
371 | |
265 +---o 5 secret H
372 +---o 5 secret H
266 | |
373 | |
267 o | 4 secret E
374 o | 4 secret E
268 | |
375 | |
269 o | 3 draft D
376 o | 3 draft D
270 | |
377 | |
271 o | 2 draft C
378 o | 2 draft C
272 |/
379 |/
273 o 1 public B
380 o 1 public B
274 |
381 |
275 o 0 public A
382 o 0 public A
276
383
277
384
278 move changeset forward and backward
385 move changeset forward and backward
279
386
280 $ hg phase --draft --force 1::4
387 $ hg phase --draft --force 1::4
281 $ hg log -G --template "{rev} {phase} {desc}\n"
388 $ hg log -G --template "{rev} {phase} {desc}\n"
282 @ 7 secret merge B' and E
389 @ 7 secret merge B' and E
283 |\
390 |\
284 | o 6 draft B'
391 | o 6 draft B'
285 | |
392 | |
286 +---o 5 secret H
393 +---o 5 secret H
287 | |
394 | |
288 o | 4 draft E
395 o | 4 draft E
289 | |
396 | |
290 o | 3 draft D
397 o | 3 draft D
291 | |
398 | |
292 o | 2 draft C
399 o | 2 draft C
293 |/
400 |/
294 o 1 draft B
401 o 1 draft B
295 |
402 |
296 o 0 public A
403 o 0 public A
297
404
General Comments 0
You need to be logged in to leave comments. Login now