##// END OF EJS Templates
revrange: attempt to parse old-style queries as a first pass
Matt Mackall -
r11405:bf5d88c4 default
parent child Browse files
Show More
@@ -1,1232 +1,1238 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, glob, tempfile
10 import os, sys, errno, re, glob, tempfile
11 import util, templater, patch, error, encoding, templatekw
11 import util, templater, patch, error, encoding, templatekw
12 import match as _match
12 import match as _match
13 import similar, revset
13 import similar, revset
14
14
15 revrangesep = ':'
15 revrangesep = ':'
16
16
17 def parsealiases(cmd):
17 def parsealiases(cmd):
18 return cmd.lstrip("^").split("|")
18 return cmd.lstrip("^").split("|")
19
19
20 def findpossible(cmd, table, strict=False):
20 def findpossible(cmd, table, strict=False):
21 """
21 """
22 Return cmd -> (aliases, command table entry)
22 Return cmd -> (aliases, command table entry)
23 for each matching command.
23 for each matching command.
24 Return debug commands (or their aliases) only if no normal command matches.
24 Return debug commands (or their aliases) only if no normal command matches.
25 """
25 """
26 choice = {}
26 choice = {}
27 debugchoice = {}
27 debugchoice = {}
28 for e in table.keys():
28 for e in table.keys():
29 aliases = parsealiases(e)
29 aliases = parsealiases(e)
30 found = None
30 found = None
31 if cmd in aliases:
31 if cmd in aliases:
32 found = cmd
32 found = cmd
33 elif not strict:
33 elif not strict:
34 for a in aliases:
34 for a in aliases:
35 if a.startswith(cmd):
35 if a.startswith(cmd):
36 found = a
36 found = a
37 break
37 break
38 if found is not None:
38 if found is not None:
39 if aliases[0].startswith("debug") or found.startswith("debug"):
39 if aliases[0].startswith("debug") or found.startswith("debug"):
40 debugchoice[found] = (aliases, table[e])
40 debugchoice[found] = (aliases, table[e])
41 else:
41 else:
42 choice[found] = (aliases, table[e])
42 choice[found] = (aliases, table[e])
43
43
44 if not choice and debugchoice:
44 if not choice and debugchoice:
45 choice = debugchoice
45 choice = debugchoice
46
46
47 return choice
47 return choice
48
48
49 def findcmd(cmd, table, strict=True):
49 def findcmd(cmd, table, strict=True):
50 """Return (aliases, command table entry) for command string."""
50 """Return (aliases, command table entry) for command string."""
51 choice = findpossible(cmd, table, strict)
51 choice = findpossible(cmd, table, strict)
52
52
53 if cmd in choice:
53 if cmd in choice:
54 return choice[cmd]
54 return choice[cmd]
55
55
56 if len(choice) > 1:
56 if len(choice) > 1:
57 clist = choice.keys()
57 clist = choice.keys()
58 clist.sort()
58 clist.sort()
59 raise error.AmbiguousCommand(cmd, clist)
59 raise error.AmbiguousCommand(cmd, clist)
60
60
61 if choice:
61 if choice:
62 return choice.values()[0]
62 return choice.values()[0]
63
63
64 raise error.UnknownCommand(cmd)
64 raise error.UnknownCommand(cmd)
65
65
66 def findrepo(p):
66 def findrepo(p):
67 while not os.path.isdir(os.path.join(p, ".hg")):
67 while not os.path.isdir(os.path.join(p, ".hg")):
68 oldp, p = p, os.path.dirname(p)
68 oldp, p = p, os.path.dirname(p)
69 if p == oldp:
69 if p == oldp:
70 return None
70 return None
71
71
72 return p
72 return p
73
73
74 def bail_if_changed(repo):
74 def bail_if_changed(repo):
75 if repo.dirstate.parents()[1] != nullid:
75 if repo.dirstate.parents()[1] != nullid:
76 raise util.Abort(_('outstanding uncommitted merge'))
76 raise util.Abort(_('outstanding uncommitted merge'))
77 modified, added, removed, deleted = repo.status()[:4]
77 modified, added, removed, deleted = repo.status()[:4]
78 if modified or added or removed or deleted:
78 if modified or added or removed or deleted:
79 raise util.Abort(_("outstanding uncommitted changes"))
79 raise util.Abort(_("outstanding uncommitted changes"))
80
80
81 def logmessage(opts):
81 def logmessage(opts):
82 """ get the log message according to -m and -l option """
82 """ get the log message according to -m and -l option """
83 message = opts.get('message')
83 message = opts.get('message')
84 logfile = opts.get('logfile')
84 logfile = opts.get('logfile')
85
85
86 if message and logfile:
86 if message and logfile:
87 raise util.Abort(_('options --message and --logfile are mutually '
87 raise util.Abort(_('options --message and --logfile are mutually '
88 'exclusive'))
88 'exclusive'))
89 if not message and logfile:
89 if not message and logfile:
90 try:
90 try:
91 if logfile == '-':
91 if logfile == '-':
92 message = sys.stdin.read()
92 message = sys.stdin.read()
93 else:
93 else:
94 message = open(logfile).read()
94 message = open(logfile).read()
95 except IOError, inst:
95 except IOError, inst:
96 raise util.Abort(_("can't read commit message '%s': %s") %
96 raise util.Abort(_("can't read commit message '%s': %s") %
97 (logfile, inst.strerror))
97 (logfile, inst.strerror))
98 return message
98 return message
99
99
100 def loglimit(opts):
100 def loglimit(opts):
101 """get the log limit according to option -l/--limit"""
101 """get the log limit according to option -l/--limit"""
102 limit = opts.get('limit')
102 limit = opts.get('limit')
103 if limit:
103 if limit:
104 try:
104 try:
105 limit = int(limit)
105 limit = int(limit)
106 except ValueError:
106 except ValueError:
107 raise util.Abort(_('limit must be a positive integer'))
107 raise util.Abort(_('limit must be a positive integer'))
108 if limit <= 0:
108 if limit <= 0:
109 raise util.Abort(_('limit must be positive'))
109 raise util.Abort(_('limit must be positive'))
110 else:
110 else:
111 limit = None
111 limit = None
112 return limit
112 return limit
113
113
114 def revpair(repo, revs):
114 def revpair(repo, revs):
115 '''return pair of nodes, given list of revisions. second item can
115 '''return pair of nodes, given list of revisions. second item can
116 be None, meaning use working dir.'''
116 be None, meaning use working dir.'''
117
117
118 def revfix(repo, val, defval):
118 def revfix(repo, val, defval):
119 if not val and val != 0 and defval is not None:
119 if not val and val != 0 and defval is not None:
120 val = defval
120 val = defval
121 return repo.lookup(val)
121 return repo.lookup(val)
122
122
123 if not revs:
123 if not revs:
124 return repo.dirstate.parents()[0], None
124 return repo.dirstate.parents()[0], None
125 end = None
125 end = None
126 if len(revs) == 1:
126 if len(revs) == 1:
127 if revrangesep in revs[0]:
127 if revrangesep in revs[0]:
128 start, end = revs[0].split(revrangesep, 1)
128 start, end = revs[0].split(revrangesep, 1)
129 start = revfix(repo, start, 0)
129 start = revfix(repo, start, 0)
130 end = revfix(repo, end, len(repo) - 1)
130 end = revfix(repo, end, len(repo) - 1)
131 else:
131 else:
132 start = revfix(repo, revs[0], None)
132 start = revfix(repo, revs[0], None)
133 elif len(revs) == 2:
133 elif len(revs) == 2:
134 if revrangesep in revs[0] or revrangesep in revs[1]:
134 if revrangesep in revs[0] or revrangesep in revs[1]:
135 raise util.Abort(_('too many revisions specified'))
135 raise util.Abort(_('too many revisions specified'))
136 start = revfix(repo, revs[0], None)
136 start = revfix(repo, revs[0], None)
137 end = revfix(repo, revs[1], None)
137 end = revfix(repo, revs[1], None)
138 else:
138 else:
139 raise util.Abort(_('too many revisions specified'))
139 raise util.Abort(_('too many revisions specified'))
140 return start, end
140 return start, end
141
141
142 def revrange(repo, revs):
142 def revrange(repo, revs):
143 """Yield revision as strings from a list of revision specifications."""
143 """Yield revision as strings from a list of revision specifications."""
144
144
145 def revfix(repo, val, defval):
145 def revfix(repo, val, defval):
146 if not val and val != 0 and defval is not None:
146 if not val and val != 0 and defval is not None:
147 return defval
147 return defval
148 return repo.changelog.rev(repo.lookup(val))
148 return repo.changelog.rev(repo.lookup(val))
149
149
150 seen, l = set(), []
150 seen, l = set(), []
151 for spec in revs:
151 for spec in revs:
152 if spec and not (
152 # attempt to parse old-style ranges first to deal with
153 spec.startswith(revrangesep) or spec.endswith(revrangesep)):
153 # things like old-tag which contain query metacharacters
154 m = revset.match(spec)
154 try:
155 for r in m(repo, range(len(repo))):
155 if revrangesep in spec:
156 if r not in seen:
157 l.append(r)
158 seen.update(l)
159 elif revrangesep in spec:
160 start, end = spec.split(revrangesep, 1)
156 start, end = spec.split(revrangesep, 1)
161 start = revfix(repo, start, 0)
157 start = revfix(repo, start, 0)
162 end = revfix(repo, end, len(repo) - 1)
158 end = revfix(repo, end, len(repo) - 1)
163 step = start > end and -1 or 1
159 step = start > end and -1 or 1
164 for rev in xrange(start, end + step, step):
160 for rev in xrange(start, end + step, step):
165 if rev in seen:
161 if rev in seen:
166 continue
162 continue
167 seen.add(rev)
163 seen.add(rev)
168 l.append(rev)
164 l.append(rev)
169 else:
165 continue
166 elif spec in repo: # single unquoted rev
170 rev = revfix(repo, spec, None)
167 rev = revfix(repo, spec, None)
171 if rev in seen:
168 if rev in seen:
172 continue
169 continue
173 seen.add(rev)
170 seen.add(rev)
174 l.append(rev)
171 l.append(rev)
172 except error.RepoLookupError:
173 pass
174
175 # fall through to new-style queries if old-style fails
176 m = revset.match(spec)
177 for r in m(repo, range(len(repo))):
178 if r not in seen:
179 l.append(r)
180 seen.update(l)
175
181
176 return l
182 return l
177
183
178 def make_filename(repo, pat, node,
184 def make_filename(repo, pat, node,
179 total=None, seqno=None, revwidth=None, pathname=None):
185 total=None, seqno=None, revwidth=None, pathname=None):
180 node_expander = {
186 node_expander = {
181 'H': lambda: hex(node),
187 'H': lambda: hex(node),
182 'R': lambda: str(repo.changelog.rev(node)),
188 'R': lambda: str(repo.changelog.rev(node)),
183 'h': lambda: short(node),
189 'h': lambda: short(node),
184 }
190 }
185 expander = {
191 expander = {
186 '%': lambda: '%',
192 '%': lambda: '%',
187 'b': lambda: os.path.basename(repo.root),
193 'b': lambda: os.path.basename(repo.root),
188 }
194 }
189
195
190 try:
196 try:
191 if node:
197 if node:
192 expander.update(node_expander)
198 expander.update(node_expander)
193 if node:
199 if node:
194 expander['r'] = (lambda:
200 expander['r'] = (lambda:
195 str(repo.changelog.rev(node)).zfill(revwidth or 0))
201 str(repo.changelog.rev(node)).zfill(revwidth or 0))
196 if total is not None:
202 if total is not None:
197 expander['N'] = lambda: str(total)
203 expander['N'] = lambda: str(total)
198 if seqno is not None:
204 if seqno is not None:
199 expander['n'] = lambda: str(seqno)
205 expander['n'] = lambda: str(seqno)
200 if total is not None and seqno is not None:
206 if total is not None and seqno is not None:
201 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
207 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
202 if pathname is not None:
208 if pathname is not None:
203 expander['s'] = lambda: os.path.basename(pathname)
209 expander['s'] = lambda: os.path.basename(pathname)
204 expander['d'] = lambda: os.path.dirname(pathname) or '.'
210 expander['d'] = lambda: os.path.dirname(pathname) or '.'
205 expander['p'] = lambda: pathname
211 expander['p'] = lambda: pathname
206
212
207 newname = []
213 newname = []
208 patlen = len(pat)
214 patlen = len(pat)
209 i = 0
215 i = 0
210 while i < patlen:
216 while i < patlen:
211 c = pat[i]
217 c = pat[i]
212 if c == '%':
218 if c == '%':
213 i += 1
219 i += 1
214 c = pat[i]
220 c = pat[i]
215 c = expander[c]()
221 c = expander[c]()
216 newname.append(c)
222 newname.append(c)
217 i += 1
223 i += 1
218 return ''.join(newname)
224 return ''.join(newname)
219 except KeyError, inst:
225 except KeyError, inst:
220 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
226 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
221 inst.args[0])
227 inst.args[0])
222
228
223 def make_file(repo, pat, node=None,
229 def make_file(repo, pat, node=None,
224 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
230 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
225
231
226 writable = 'w' in mode or 'a' in mode
232 writable = 'w' in mode or 'a' in mode
227
233
228 if not pat or pat == '-':
234 if not pat or pat == '-':
229 return writable and sys.stdout or sys.stdin
235 return writable and sys.stdout or sys.stdin
230 if hasattr(pat, 'write') and writable:
236 if hasattr(pat, 'write') and writable:
231 return pat
237 return pat
232 if hasattr(pat, 'read') and 'r' in mode:
238 if hasattr(pat, 'read') and 'r' in mode:
233 return pat
239 return pat
234 return open(make_filename(repo, pat, node, total, seqno, revwidth,
240 return open(make_filename(repo, pat, node, total, seqno, revwidth,
235 pathname),
241 pathname),
236 mode)
242 mode)
237
243
238 def expandpats(pats):
244 def expandpats(pats):
239 if not util.expandglobs:
245 if not util.expandglobs:
240 return list(pats)
246 return list(pats)
241 ret = []
247 ret = []
242 for p in pats:
248 for p in pats:
243 kind, name = _match._patsplit(p, None)
249 kind, name = _match._patsplit(p, None)
244 if kind is None:
250 if kind is None:
245 try:
251 try:
246 globbed = glob.glob(name)
252 globbed = glob.glob(name)
247 except re.error:
253 except re.error:
248 globbed = [name]
254 globbed = [name]
249 if globbed:
255 if globbed:
250 ret.extend(globbed)
256 ret.extend(globbed)
251 continue
257 continue
252 ret.append(p)
258 ret.append(p)
253 return ret
259 return ret
254
260
255 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
261 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
256 if not globbed and default == 'relpath':
262 if not globbed and default == 'relpath':
257 pats = expandpats(pats or [])
263 pats = expandpats(pats or [])
258 m = _match.match(repo.root, repo.getcwd(), pats,
264 m = _match.match(repo.root, repo.getcwd(), pats,
259 opts.get('include'), opts.get('exclude'), default)
265 opts.get('include'), opts.get('exclude'), default)
260 def badfn(f, msg):
266 def badfn(f, msg):
261 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
267 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
262 m.bad = badfn
268 m.bad = badfn
263 return m
269 return m
264
270
265 def matchall(repo):
271 def matchall(repo):
266 return _match.always(repo.root, repo.getcwd())
272 return _match.always(repo.root, repo.getcwd())
267
273
268 def matchfiles(repo, files):
274 def matchfiles(repo, files):
269 return _match.exact(repo.root, repo.getcwd(), files)
275 return _match.exact(repo.root, repo.getcwd(), files)
270
276
271 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
277 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
272 if dry_run is None:
278 if dry_run is None:
273 dry_run = opts.get('dry_run')
279 dry_run = opts.get('dry_run')
274 if similarity is None:
280 if similarity is None:
275 similarity = float(opts.get('similarity') or 0)
281 similarity = float(opts.get('similarity') or 0)
276 # we'd use status here, except handling of symlinks and ignore is tricky
282 # we'd use status here, except handling of symlinks and ignore is tricky
277 added, unknown, deleted, removed = [], [], [], []
283 added, unknown, deleted, removed = [], [], [], []
278 audit_path = util.path_auditor(repo.root)
284 audit_path = util.path_auditor(repo.root)
279 m = match(repo, pats, opts)
285 m = match(repo, pats, opts)
280 for abs in repo.walk(m):
286 for abs in repo.walk(m):
281 target = repo.wjoin(abs)
287 target = repo.wjoin(abs)
282 good = True
288 good = True
283 try:
289 try:
284 audit_path(abs)
290 audit_path(abs)
285 except:
291 except:
286 good = False
292 good = False
287 rel = m.rel(abs)
293 rel = m.rel(abs)
288 exact = m.exact(abs)
294 exact = m.exact(abs)
289 if good and abs not in repo.dirstate:
295 if good and abs not in repo.dirstate:
290 unknown.append(abs)
296 unknown.append(abs)
291 if repo.ui.verbose or not exact:
297 if repo.ui.verbose or not exact:
292 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
298 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
293 elif repo.dirstate[abs] != 'r' and (not good or not util.lexists(target)
299 elif repo.dirstate[abs] != 'r' and (not good or not util.lexists(target)
294 or (os.path.isdir(target) and not os.path.islink(target))):
300 or (os.path.isdir(target) and not os.path.islink(target))):
295 deleted.append(abs)
301 deleted.append(abs)
296 if repo.ui.verbose or not exact:
302 if repo.ui.verbose or not exact:
297 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
303 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
298 # for finding renames
304 # for finding renames
299 elif repo.dirstate[abs] == 'r':
305 elif repo.dirstate[abs] == 'r':
300 removed.append(abs)
306 removed.append(abs)
301 elif repo.dirstate[abs] == 'a':
307 elif repo.dirstate[abs] == 'a':
302 added.append(abs)
308 added.append(abs)
303 copies = {}
309 copies = {}
304 if similarity > 0:
310 if similarity > 0:
305 for old, new, score in similar.findrenames(repo,
311 for old, new, score in similar.findrenames(repo,
306 added + unknown, removed + deleted, similarity):
312 added + unknown, removed + deleted, similarity):
307 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
313 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
308 repo.ui.status(_('recording removal of %s as rename to %s '
314 repo.ui.status(_('recording removal of %s as rename to %s '
309 '(%d%% similar)\n') %
315 '(%d%% similar)\n') %
310 (m.rel(old), m.rel(new), score * 100))
316 (m.rel(old), m.rel(new), score * 100))
311 copies[new] = old
317 copies[new] = old
312
318
313 if not dry_run:
319 if not dry_run:
314 wctx = repo[None]
320 wctx = repo[None]
315 wlock = repo.wlock()
321 wlock = repo.wlock()
316 try:
322 try:
317 wctx.remove(deleted)
323 wctx.remove(deleted)
318 wctx.add(unknown)
324 wctx.add(unknown)
319 for new, old in copies.iteritems():
325 for new, old in copies.iteritems():
320 wctx.copy(old, new)
326 wctx.copy(old, new)
321 finally:
327 finally:
322 wlock.release()
328 wlock.release()
323
329
324 def copy(ui, repo, pats, opts, rename=False):
330 def copy(ui, repo, pats, opts, rename=False):
325 # called with the repo lock held
331 # called with the repo lock held
326 #
332 #
327 # hgsep => pathname that uses "/" to separate directories
333 # hgsep => pathname that uses "/" to separate directories
328 # ossep => pathname that uses os.sep to separate directories
334 # ossep => pathname that uses os.sep to separate directories
329 cwd = repo.getcwd()
335 cwd = repo.getcwd()
330 targets = {}
336 targets = {}
331 after = opts.get("after")
337 after = opts.get("after")
332 dryrun = opts.get("dry_run")
338 dryrun = opts.get("dry_run")
333 wctx = repo[None]
339 wctx = repo[None]
334
340
335 def walkpat(pat):
341 def walkpat(pat):
336 srcs = []
342 srcs = []
337 badstates = after and '?' or '?r'
343 badstates = after and '?' or '?r'
338 m = match(repo, [pat], opts, globbed=True)
344 m = match(repo, [pat], opts, globbed=True)
339 for abs in repo.walk(m):
345 for abs in repo.walk(m):
340 state = repo.dirstate[abs]
346 state = repo.dirstate[abs]
341 rel = m.rel(abs)
347 rel = m.rel(abs)
342 exact = m.exact(abs)
348 exact = m.exact(abs)
343 if state in badstates:
349 if state in badstates:
344 if exact and state == '?':
350 if exact and state == '?':
345 ui.warn(_('%s: not copying - file is not managed\n') % rel)
351 ui.warn(_('%s: not copying - file is not managed\n') % rel)
346 if exact and state == 'r':
352 if exact and state == 'r':
347 ui.warn(_('%s: not copying - file has been marked for'
353 ui.warn(_('%s: not copying - file has been marked for'
348 ' remove\n') % rel)
354 ' remove\n') % rel)
349 continue
355 continue
350 # abs: hgsep
356 # abs: hgsep
351 # rel: ossep
357 # rel: ossep
352 srcs.append((abs, rel, exact))
358 srcs.append((abs, rel, exact))
353 return srcs
359 return srcs
354
360
355 # abssrc: hgsep
361 # abssrc: hgsep
356 # relsrc: ossep
362 # relsrc: ossep
357 # otarget: ossep
363 # otarget: ossep
358 def copyfile(abssrc, relsrc, otarget, exact):
364 def copyfile(abssrc, relsrc, otarget, exact):
359 abstarget = util.canonpath(repo.root, cwd, otarget)
365 abstarget = util.canonpath(repo.root, cwd, otarget)
360 reltarget = repo.pathto(abstarget, cwd)
366 reltarget = repo.pathto(abstarget, cwd)
361 target = repo.wjoin(abstarget)
367 target = repo.wjoin(abstarget)
362 src = repo.wjoin(abssrc)
368 src = repo.wjoin(abssrc)
363 state = repo.dirstate[abstarget]
369 state = repo.dirstate[abstarget]
364
370
365 # check for collisions
371 # check for collisions
366 prevsrc = targets.get(abstarget)
372 prevsrc = targets.get(abstarget)
367 if prevsrc is not None:
373 if prevsrc is not None:
368 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
374 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
369 (reltarget, repo.pathto(abssrc, cwd),
375 (reltarget, repo.pathto(abssrc, cwd),
370 repo.pathto(prevsrc, cwd)))
376 repo.pathto(prevsrc, cwd)))
371 return
377 return
372
378
373 # check for overwrites
379 # check for overwrites
374 exists = os.path.exists(target)
380 exists = os.path.exists(target)
375 if not after and exists or after and state in 'mn':
381 if not after and exists or after and state in 'mn':
376 if not opts['force']:
382 if not opts['force']:
377 ui.warn(_('%s: not overwriting - file exists\n') %
383 ui.warn(_('%s: not overwriting - file exists\n') %
378 reltarget)
384 reltarget)
379 return
385 return
380
386
381 if after:
387 if after:
382 if not exists:
388 if not exists:
383 if rename:
389 if rename:
384 ui.warn(_('%s: not recording move - %s does not exist\n') %
390 ui.warn(_('%s: not recording move - %s does not exist\n') %
385 (relsrc, reltarget))
391 (relsrc, reltarget))
386 else:
392 else:
387 ui.warn(_('%s: not recording copy - %s does not exist\n') %
393 ui.warn(_('%s: not recording copy - %s does not exist\n') %
388 (relsrc, reltarget))
394 (relsrc, reltarget))
389 return
395 return
390 elif not dryrun:
396 elif not dryrun:
391 try:
397 try:
392 if exists:
398 if exists:
393 os.unlink(target)
399 os.unlink(target)
394 targetdir = os.path.dirname(target) or '.'
400 targetdir = os.path.dirname(target) or '.'
395 if not os.path.isdir(targetdir):
401 if not os.path.isdir(targetdir):
396 os.makedirs(targetdir)
402 os.makedirs(targetdir)
397 util.copyfile(src, target)
403 util.copyfile(src, target)
398 except IOError, inst:
404 except IOError, inst:
399 if inst.errno == errno.ENOENT:
405 if inst.errno == errno.ENOENT:
400 ui.warn(_('%s: deleted in working copy\n') % relsrc)
406 ui.warn(_('%s: deleted in working copy\n') % relsrc)
401 else:
407 else:
402 ui.warn(_('%s: cannot copy - %s\n') %
408 ui.warn(_('%s: cannot copy - %s\n') %
403 (relsrc, inst.strerror))
409 (relsrc, inst.strerror))
404 return True # report a failure
410 return True # report a failure
405
411
406 if ui.verbose or not exact:
412 if ui.verbose or not exact:
407 if rename:
413 if rename:
408 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
414 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
409 else:
415 else:
410 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
416 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
411
417
412 targets[abstarget] = abssrc
418 targets[abstarget] = abssrc
413
419
414 # fix up dirstate
420 # fix up dirstate
415 origsrc = repo.dirstate.copied(abssrc) or abssrc
421 origsrc = repo.dirstate.copied(abssrc) or abssrc
416 if abstarget == origsrc: # copying back a copy?
422 if abstarget == origsrc: # copying back a copy?
417 if state not in 'mn' and not dryrun:
423 if state not in 'mn' and not dryrun:
418 repo.dirstate.normallookup(abstarget)
424 repo.dirstate.normallookup(abstarget)
419 else:
425 else:
420 if repo.dirstate[origsrc] == 'a' and origsrc == abssrc:
426 if repo.dirstate[origsrc] == 'a' and origsrc == abssrc:
421 if not ui.quiet:
427 if not ui.quiet:
422 ui.warn(_("%s has not been committed yet, so no copy "
428 ui.warn(_("%s has not been committed yet, so no copy "
423 "data will be stored for %s.\n")
429 "data will be stored for %s.\n")
424 % (repo.pathto(origsrc, cwd), reltarget))
430 % (repo.pathto(origsrc, cwd), reltarget))
425 if repo.dirstate[abstarget] in '?r' and not dryrun:
431 if repo.dirstate[abstarget] in '?r' and not dryrun:
426 wctx.add([abstarget])
432 wctx.add([abstarget])
427 elif not dryrun:
433 elif not dryrun:
428 wctx.copy(origsrc, abstarget)
434 wctx.copy(origsrc, abstarget)
429
435
430 if rename and not dryrun:
436 if rename and not dryrun:
431 wctx.remove([abssrc], not after)
437 wctx.remove([abssrc], not after)
432
438
433 # pat: ossep
439 # pat: ossep
434 # dest ossep
440 # dest ossep
435 # srcs: list of (hgsep, hgsep, ossep, bool)
441 # srcs: list of (hgsep, hgsep, ossep, bool)
436 # return: function that takes hgsep and returns ossep
442 # return: function that takes hgsep and returns ossep
437 def targetpathfn(pat, dest, srcs):
443 def targetpathfn(pat, dest, srcs):
438 if os.path.isdir(pat):
444 if os.path.isdir(pat):
439 abspfx = util.canonpath(repo.root, cwd, pat)
445 abspfx = util.canonpath(repo.root, cwd, pat)
440 abspfx = util.localpath(abspfx)
446 abspfx = util.localpath(abspfx)
441 if destdirexists:
447 if destdirexists:
442 striplen = len(os.path.split(abspfx)[0])
448 striplen = len(os.path.split(abspfx)[0])
443 else:
449 else:
444 striplen = len(abspfx)
450 striplen = len(abspfx)
445 if striplen:
451 if striplen:
446 striplen += len(os.sep)
452 striplen += len(os.sep)
447 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
453 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
448 elif destdirexists:
454 elif destdirexists:
449 res = lambda p: os.path.join(dest,
455 res = lambda p: os.path.join(dest,
450 os.path.basename(util.localpath(p)))
456 os.path.basename(util.localpath(p)))
451 else:
457 else:
452 res = lambda p: dest
458 res = lambda p: dest
453 return res
459 return res
454
460
455 # pat: ossep
461 # pat: ossep
456 # dest ossep
462 # dest ossep
457 # srcs: list of (hgsep, hgsep, ossep, bool)
463 # srcs: list of (hgsep, hgsep, ossep, bool)
458 # return: function that takes hgsep and returns ossep
464 # return: function that takes hgsep and returns ossep
459 def targetpathafterfn(pat, dest, srcs):
465 def targetpathafterfn(pat, dest, srcs):
460 if _match.patkind(pat):
466 if _match.patkind(pat):
461 # a mercurial pattern
467 # a mercurial pattern
462 res = lambda p: os.path.join(dest,
468 res = lambda p: os.path.join(dest,
463 os.path.basename(util.localpath(p)))
469 os.path.basename(util.localpath(p)))
464 else:
470 else:
465 abspfx = util.canonpath(repo.root, cwd, pat)
471 abspfx = util.canonpath(repo.root, cwd, pat)
466 if len(abspfx) < len(srcs[0][0]):
472 if len(abspfx) < len(srcs[0][0]):
467 # A directory. Either the target path contains the last
473 # A directory. Either the target path contains the last
468 # component of the source path or it does not.
474 # component of the source path or it does not.
469 def evalpath(striplen):
475 def evalpath(striplen):
470 score = 0
476 score = 0
471 for s in srcs:
477 for s in srcs:
472 t = os.path.join(dest, util.localpath(s[0])[striplen:])
478 t = os.path.join(dest, util.localpath(s[0])[striplen:])
473 if os.path.exists(t):
479 if os.path.exists(t):
474 score += 1
480 score += 1
475 return score
481 return score
476
482
477 abspfx = util.localpath(abspfx)
483 abspfx = util.localpath(abspfx)
478 striplen = len(abspfx)
484 striplen = len(abspfx)
479 if striplen:
485 if striplen:
480 striplen += len(os.sep)
486 striplen += len(os.sep)
481 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
487 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
482 score = evalpath(striplen)
488 score = evalpath(striplen)
483 striplen1 = len(os.path.split(abspfx)[0])
489 striplen1 = len(os.path.split(abspfx)[0])
484 if striplen1:
490 if striplen1:
485 striplen1 += len(os.sep)
491 striplen1 += len(os.sep)
486 if evalpath(striplen1) > score:
492 if evalpath(striplen1) > score:
487 striplen = striplen1
493 striplen = striplen1
488 res = lambda p: os.path.join(dest,
494 res = lambda p: os.path.join(dest,
489 util.localpath(p)[striplen:])
495 util.localpath(p)[striplen:])
490 else:
496 else:
491 # a file
497 # a file
492 if destdirexists:
498 if destdirexists:
493 res = lambda p: os.path.join(dest,
499 res = lambda p: os.path.join(dest,
494 os.path.basename(util.localpath(p)))
500 os.path.basename(util.localpath(p)))
495 else:
501 else:
496 res = lambda p: dest
502 res = lambda p: dest
497 return res
503 return res
498
504
499
505
500 pats = expandpats(pats)
506 pats = expandpats(pats)
501 if not pats:
507 if not pats:
502 raise util.Abort(_('no source or destination specified'))
508 raise util.Abort(_('no source or destination specified'))
503 if len(pats) == 1:
509 if len(pats) == 1:
504 raise util.Abort(_('no destination specified'))
510 raise util.Abort(_('no destination specified'))
505 dest = pats.pop()
511 dest = pats.pop()
506 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
512 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
507 if not destdirexists:
513 if not destdirexists:
508 if len(pats) > 1 or _match.patkind(pats[0]):
514 if len(pats) > 1 or _match.patkind(pats[0]):
509 raise util.Abort(_('with multiple sources, destination must be an '
515 raise util.Abort(_('with multiple sources, destination must be an '
510 'existing directory'))
516 'existing directory'))
511 if util.endswithsep(dest):
517 if util.endswithsep(dest):
512 raise util.Abort(_('destination %s is not a directory') % dest)
518 raise util.Abort(_('destination %s is not a directory') % dest)
513
519
514 tfn = targetpathfn
520 tfn = targetpathfn
515 if after:
521 if after:
516 tfn = targetpathafterfn
522 tfn = targetpathafterfn
517 copylist = []
523 copylist = []
518 for pat in pats:
524 for pat in pats:
519 srcs = walkpat(pat)
525 srcs = walkpat(pat)
520 if not srcs:
526 if not srcs:
521 continue
527 continue
522 copylist.append((tfn(pat, dest, srcs), srcs))
528 copylist.append((tfn(pat, dest, srcs), srcs))
523 if not copylist:
529 if not copylist:
524 raise util.Abort(_('no files to copy'))
530 raise util.Abort(_('no files to copy'))
525
531
526 errors = 0
532 errors = 0
527 for targetpath, srcs in copylist:
533 for targetpath, srcs in copylist:
528 for abssrc, relsrc, exact in srcs:
534 for abssrc, relsrc, exact in srcs:
529 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
535 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
530 errors += 1
536 errors += 1
531
537
532 if errors:
538 if errors:
533 ui.warn(_('(consider using --after)\n'))
539 ui.warn(_('(consider using --after)\n'))
534
540
535 return errors != 0
541 return errors != 0
536
542
537 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
543 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
538 runargs=None, appendpid=False):
544 runargs=None, appendpid=False):
539 '''Run a command as a service.'''
545 '''Run a command as a service.'''
540
546
541 if opts['daemon'] and not opts['daemon_pipefds']:
547 if opts['daemon'] and not opts['daemon_pipefds']:
542 # Signal child process startup with file removal
548 # Signal child process startup with file removal
543 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
549 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
544 os.close(lockfd)
550 os.close(lockfd)
545 try:
551 try:
546 if not runargs:
552 if not runargs:
547 runargs = util.hgcmd() + sys.argv[1:]
553 runargs = util.hgcmd() + sys.argv[1:]
548 runargs.append('--daemon-pipefds=%s' % lockpath)
554 runargs.append('--daemon-pipefds=%s' % lockpath)
549 # Don't pass --cwd to the child process, because we've already
555 # Don't pass --cwd to the child process, because we've already
550 # changed directory.
556 # changed directory.
551 for i in xrange(1, len(runargs)):
557 for i in xrange(1, len(runargs)):
552 if runargs[i].startswith('--cwd='):
558 if runargs[i].startswith('--cwd='):
553 del runargs[i]
559 del runargs[i]
554 break
560 break
555 elif runargs[i].startswith('--cwd'):
561 elif runargs[i].startswith('--cwd'):
556 del runargs[i:i + 2]
562 del runargs[i:i + 2]
557 break
563 break
558 def condfn():
564 def condfn():
559 return not os.path.exists(lockpath)
565 return not os.path.exists(lockpath)
560 pid = util.rundetached(runargs, condfn)
566 pid = util.rundetached(runargs, condfn)
561 if pid < 0:
567 if pid < 0:
562 raise util.Abort(_('child process failed to start'))
568 raise util.Abort(_('child process failed to start'))
563 finally:
569 finally:
564 try:
570 try:
565 os.unlink(lockpath)
571 os.unlink(lockpath)
566 except OSError, e:
572 except OSError, e:
567 if e.errno != errno.ENOENT:
573 if e.errno != errno.ENOENT:
568 raise
574 raise
569 if parentfn:
575 if parentfn:
570 return parentfn(pid)
576 return parentfn(pid)
571 else:
577 else:
572 return
578 return
573
579
574 if initfn:
580 if initfn:
575 initfn()
581 initfn()
576
582
577 if opts['pid_file']:
583 if opts['pid_file']:
578 mode = appendpid and 'a' or 'w'
584 mode = appendpid and 'a' or 'w'
579 fp = open(opts['pid_file'], mode)
585 fp = open(opts['pid_file'], mode)
580 fp.write(str(os.getpid()) + '\n')
586 fp.write(str(os.getpid()) + '\n')
581 fp.close()
587 fp.close()
582
588
583 if opts['daemon_pipefds']:
589 if opts['daemon_pipefds']:
584 lockpath = opts['daemon_pipefds']
590 lockpath = opts['daemon_pipefds']
585 try:
591 try:
586 os.setsid()
592 os.setsid()
587 except AttributeError:
593 except AttributeError:
588 pass
594 pass
589 os.unlink(lockpath)
595 os.unlink(lockpath)
590 util.hidewindow()
596 util.hidewindow()
591 sys.stdout.flush()
597 sys.stdout.flush()
592 sys.stderr.flush()
598 sys.stderr.flush()
593
599
594 nullfd = os.open(util.nulldev, os.O_RDWR)
600 nullfd = os.open(util.nulldev, os.O_RDWR)
595 logfilefd = nullfd
601 logfilefd = nullfd
596 if logfile:
602 if logfile:
597 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
603 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
598 os.dup2(nullfd, 0)
604 os.dup2(nullfd, 0)
599 os.dup2(logfilefd, 1)
605 os.dup2(logfilefd, 1)
600 os.dup2(logfilefd, 2)
606 os.dup2(logfilefd, 2)
601 if nullfd not in (0, 1, 2):
607 if nullfd not in (0, 1, 2):
602 os.close(nullfd)
608 os.close(nullfd)
603 if logfile and logfilefd not in (0, 1, 2):
609 if logfile and logfilefd not in (0, 1, 2):
604 os.close(logfilefd)
610 os.close(logfilefd)
605
611
606 if runfn:
612 if runfn:
607 return runfn()
613 return runfn()
608
614
609 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
615 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
610 opts=None):
616 opts=None):
611 '''export changesets as hg patches.'''
617 '''export changesets as hg patches.'''
612
618
613 total = len(revs)
619 total = len(revs)
614 revwidth = max([len(str(rev)) for rev in revs])
620 revwidth = max([len(str(rev)) for rev in revs])
615
621
616 def single(rev, seqno, fp):
622 def single(rev, seqno, fp):
617 ctx = repo[rev]
623 ctx = repo[rev]
618 node = ctx.node()
624 node = ctx.node()
619 parents = [p.node() for p in ctx.parents() if p]
625 parents = [p.node() for p in ctx.parents() if p]
620 branch = ctx.branch()
626 branch = ctx.branch()
621 if switch_parent:
627 if switch_parent:
622 parents.reverse()
628 parents.reverse()
623 prev = (parents and parents[0]) or nullid
629 prev = (parents and parents[0]) or nullid
624
630
625 if not fp:
631 if not fp:
626 fp = make_file(repo, template, node, total=total, seqno=seqno,
632 fp = make_file(repo, template, node, total=total, seqno=seqno,
627 revwidth=revwidth, mode='ab')
633 revwidth=revwidth, mode='ab')
628 if fp != sys.stdout and hasattr(fp, 'name'):
634 if fp != sys.stdout and hasattr(fp, 'name'):
629 repo.ui.note("%s\n" % fp.name)
635 repo.ui.note("%s\n" % fp.name)
630
636
631 fp.write("# HG changeset patch\n")
637 fp.write("# HG changeset patch\n")
632 fp.write("# User %s\n" % ctx.user())
638 fp.write("# User %s\n" % ctx.user())
633 fp.write("# Date %d %d\n" % ctx.date())
639 fp.write("# Date %d %d\n" % ctx.date())
634 if branch and (branch != 'default'):
640 if branch and (branch != 'default'):
635 fp.write("# Branch %s\n" % branch)
641 fp.write("# Branch %s\n" % branch)
636 fp.write("# Node ID %s\n" % hex(node))
642 fp.write("# Node ID %s\n" % hex(node))
637 fp.write("# Parent %s\n" % hex(prev))
643 fp.write("# Parent %s\n" % hex(prev))
638 if len(parents) > 1:
644 if len(parents) > 1:
639 fp.write("# Parent %s\n" % hex(parents[1]))
645 fp.write("# Parent %s\n" % hex(parents[1]))
640 fp.write(ctx.description().rstrip())
646 fp.write(ctx.description().rstrip())
641 fp.write("\n\n")
647 fp.write("\n\n")
642
648
643 for chunk in patch.diff(repo, prev, node, opts=opts):
649 for chunk in patch.diff(repo, prev, node, opts=opts):
644 fp.write(chunk)
650 fp.write(chunk)
645
651
646 for seqno, rev in enumerate(revs):
652 for seqno, rev in enumerate(revs):
647 single(rev, seqno + 1, fp)
653 single(rev, seqno + 1, fp)
648
654
649 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
655 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
650 changes=None, stat=False, fp=None):
656 changes=None, stat=False, fp=None):
651 '''show diff or diffstat.'''
657 '''show diff or diffstat.'''
652 if fp is None:
658 if fp is None:
653 write = ui.write
659 write = ui.write
654 else:
660 else:
655 def write(s, **kw):
661 def write(s, **kw):
656 fp.write(s)
662 fp.write(s)
657
663
658 if stat:
664 if stat:
659 diffopts.context = 0
665 diffopts.context = 0
660 width = 80
666 width = 80
661 if not ui.plain():
667 if not ui.plain():
662 width = util.termwidth()
668 width = util.termwidth()
663 chunks = patch.diff(repo, node1, node2, match, changes, diffopts)
669 chunks = patch.diff(repo, node1, node2, match, changes, diffopts)
664 for chunk, label in patch.diffstatui(util.iterlines(chunks),
670 for chunk, label in patch.diffstatui(util.iterlines(chunks),
665 width=width,
671 width=width,
666 git=diffopts.git):
672 git=diffopts.git):
667 write(chunk, label=label)
673 write(chunk, label=label)
668 else:
674 else:
669 for chunk, label in patch.diffui(repo, node1, node2, match,
675 for chunk, label in patch.diffui(repo, node1, node2, match,
670 changes, diffopts):
676 changes, diffopts):
671 write(chunk, label=label)
677 write(chunk, label=label)
672
678
673 class changeset_printer(object):
679 class changeset_printer(object):
674 '''show changeset information when templating not requested.'''
680 '''show changeset information when templating not requested.'''
675
681
676 def __init__(self, ui, repo, patch, diffopts, buffered):
682 def __init__(self, ui, repo, patch, diffopts, buffered):
677 self.ui = ui
683 self.ui = ui
678 self.repo = repo
684 self.repo = repo
679 self.buffered = buffered
685 self.buffered = buffered
680 self.patch = patch
686 self.patch = patch
681 self.diffopts = diffopts
687 self.diffopts = diffopts
682 self.header = {}
688 self.header = {}
683 self.hunk = {}
689 self.hunk = {}
684 self.lastheader = None
690 self.lastheader = None
685 self.footer = None
691 self.footer = None
686
692
687 def flush(self, rev):
693 def flush(self, rev):
688 if rev in self.header:
694 if rev in self.header:
689 h = self.header[rev]
695 h = self.header[rev]
690 if h != self.lastheader:
696 if h != self.lastheader:
691 self.lastheader = h
697 self.lastheader = h
692 self.ui.write(h)
698 self.ui.write(h)
693 del self.header[rev]
699 del self.header[rev]
694 if rev in self.hunk:
700 if rev in self.hunk:
695 self.ui.write(self.hunk[rev])
701 self.ui.write(self.hunk[rev])
696 del self.hunk[rev]
702 del self.hunk[rev]
697 return 1
703 return 1
698 return 0
704 return 0
699
705
700 def close(self):
706 def close(self):
701 if self.footer:
707 if self.footer:
702 self.ui.write(self.footer)
708 self.ui.write(self.footer)
703
709
704 def show(self, ctx, copies=None, **props):
710 def show(self, ctx, copies=None, **props):
705 if self.buffered:
711 if self.buffered:
706 self.ui.pushbuffer()
712 self.ui.pushbuffer()
707 self._show(ctx, copies, props)
713 self._show(ctx, copies, props)
708 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
714 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
709 else:
715 else:
710 self._show(ctx, copies, props)
716 self._show(ctx, copies, props)
711
717
712 def _show(self, ctx, copies, props):
718 def _show(self, ctx, copies, props):
713 '''show a single changeset or file revision'''
719 '''show a single changeset or file revision'''
714 changenode = ctx.node()
720 changenode = ctx.node()
715 rev = ctx.rev()
721 rev = ctx.rev()
716
722
717 if self.ui.quiet:
723 if self.ui.quiet:
718 self.ui.write("%d:%s\n" % (rev, short(changenode)),
724 self.ui.write("%d:%s\n" % (rev, short(changenode)),
719 label='log.node')
725 label='log.node')
720 return
726 return
721
727
722 log = self.repo.changelog
728 log = self.repo.changelog
723 date = util.datestr(ctx.date())
729 date = util.datestr(ctx.date())
724
730
725 hexfunc = self.ui.debugflag and hex or short
731 hexfunc = self.ui.debugflag and hex or short
726
732
727 parents = [(p, hexfunc(log.node(p)))
733 parents = [(p, hexfunc(log.node(p)))
728 for p in self._meaningful_parentrevs(log, rev)]
734 for p in self._meaningful_parentrevs(log, rev)]
729
735
730 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
736 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
731 label='log.changeset')
737 label='log.changeset')
732
738
733 branch = ctx.branch()
739 branch = ctx.branch()
734 # don't show the default branch name
740 # don't show the default branch name
735 if branch != 'default':
741 if branch != 'default':
736 branch = encoding.tolocal(branch)
742 branch = encoding.tolocal(branch)
737 self.ui.write(_("branch: %s\n") % branch,
743 self.ui.write(_("branch: %s\n") % branch,
738 label='log.branch')
744 label='log.branch')
739 for tag in self.repo.nodetags(changenode):
745 for tag in self.repo.nodetags(changenode):
740 self.ui.write(_("tag: %s\n") % tag,
746 self.ui.write(_("tag: %s\n") % tag,
741 label='log.tag')
747 label='log.tag')
742 for parent in parents:
748 for parent in parents:
743 self.ui.write(_("parent: %d:%s\n") % parent,
749 self.ui.write(_("parent: %d:%s\n") % parent,
744 label='log.parent')
750 label='log.parent')
745
751
746 if self.ui.debugflag:
752 if self.ui.debugflag:
747 mnode = ctx.manifestnode()
753 mnode = ctx.manifestnode()
748 self.ui.write(_("manifest: %d:%s\n") %
754 self.ui.write(_("manifest: %d:%s\n") %
749 (self.repo.manifest.rev(mnode), hex(mnode)),
755 (self.repo.manifest.rev(mnode), hex(mnode)),
750 label='ui.debug log.manifest')
756 label='ui.debug log.manifest')
751 self.ui.write(_("user: %s\n") % ctx.user(),
757 self.ui.write(_("user: %s\n") % ctx.user(),
752 label='log.user')
758 label='log.user')
753 self.ui.write(_("date: %s\n") % date,
759 self.ui.write(_("date: %s\n") % date,
754 label='log.date')
760 label='log.date')
755
761
756 if self.ui.debugflag:
762 if self.ui.debugflag:
757 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
763 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
758 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
764 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
759 files):
765 files):
760 if value:
766 if value:
761 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
767 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
762 label='ui.debug log.files')
768 label='ui.debug log.files')
763 elif ctx.files() and self.ui.verbose:
769 elif ctx.files() and self.ui.verbose:
764 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
770 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
765 label='ui.note log.files')
771 label='ui.note log.files')
766 if copies and self.ui.verbose:
772 if copies and self.ui.verbose:
767 copies = ['%s (%s)' % c for c in copies]
773 copies = ['%s (%s)' % c for c in copies]
768 self.ui.write(_("copies: %s\n") % ' '.join(copies),
774 self.ui.write(_("copies: %s\n") % ' '.join(copies),
769 label='ui.note log.copies')
775 label='ui.note log.copies')
770
776
771 extra = ctx.extra()
777 extra = ctx.extra()
772 if extra and self.ui.debugflag:
778 if extra and self.ui.debugflag:
773 for key, value in sorted(extra.items()):
779 for key, value in sorted(extra.items()):
774 self.ui.write(_("extra: %s=%s\n")
780 self.ui.write(_("extra: %s=%s\n")
775 % (key, value.encode('string_escape')),
781 % (key, value.encode('string_escape')),
776 label='ui.debug log.extra')
782 label='ui.debug log.extra')
777
783
778 description = ctx.description().strip()
784 description = ctx.description().strip()
779 if description:
785 if description:
780 if self.ui.verbose:
786 if self.ui.verbose:
781 self.ui.write(_("description:\n"),
787 self.ui.write(_("description:\n"),
782 label='ui.note log.description')
788 label='ui.note log.description')
783 self.ui.write(description,
789 self.ui.write(description,
784 label='ui.note log.description')
790 label='ui.note log.description')
785 self.ui.write("\n\n")
791 self.ui.write("\n\n")
786 else:
792 else:
787 self.ui.write(_("summary: %s\n") %
793 self.ui.write(_("summary: %s\n") %
788 description.splitlines()[0],
794 description.splitlines()[0],
789 label='log.summary')
795 label='log.summary')
790 self.ui.write("\n")
796 self.ui.write("\n")
791
797
792 self.showpatch(changenode)
798 self.showpatch(changenode)
793
799
794 def showpatch(self, node):
800 def showpatch(self, node):
795 if self.patch:
801 if self.patch:
796 stat = self.diffopts.get('stat')
802 stat = self.diffopts.get('stat')
797 diffopts = patch.diffopts(self.ui, self.diffopts)
803 diffopts = patch.diffopts(self.ui, self.diffopts)
798 prev = self.repo.changelog.parents(node)[0]
804 prev = self.repo.changelog.parents(node)[0]
799 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
805 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
800 match=self.patch, stat=stat)
806 match=self.patch, stat=stat)
801 self.ui.write("\n")
807 self.ui.write("\n")
802
808
803 def _meaningful_parentrevs(self, log, rev):
809 def _meaningful_parentrevs(self, log, rev):
804 """Return list of meaningful (or all if debug) parentrevs for rev.
810 """Return list of meaningful (or all if debug) parentrevs for rev.
805
811
806 For merges (two non-nullrev revisions) both parents are meaningful.
812 For merges (two non-nullrev revisions) both parents are meaningful.
807 Otherwise the first parent revision is considered meaningful if it
813 Otherwise the first parent revision is considered meaningful if it
808 is not the preceding revision.
814 is not the preceding revision.
809 """
815 """
810 parents = log.parentrevs(rev)
816 parents = log.parentrevs(rev)
811 if not self.ui.debugflag and parents[1] == nullrev:
817 if not self.ui.debugflag and parents[1] == nullrev:
812 if parents[0] >= rev - 1:
818 if parents[0] >= rev - 1:
813 parents = []
819 parents = []
814 else:
820 else:
815 parents = [parents[0]]
821 parents = [parents[0]]
816 return parents
822 return parents
817
823
818
824
819 class changeset_templater(changeset_printer):
825 class changeset_templater(changeset_printer):
820 '''format changeset information.'''
826 '''format changeset information.'''
821
827
822 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
828 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
823 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
829 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
824 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
830 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
825 defaulttempl = {
831 defaulttempl = {
826 'parent': '{rev}:{node|formatnode} ',
832 'parent': '{rev}:{node|formatnode} ',
827 'manifest': '{rev}:{node|formatnode}',
833 'manifest': '{rev}:{node|formatnode}',
828 'file_copy': '{name} ({source})',
834 'file_copy': '{name} ({source})',
829 'extra': '{key}={value|stringescape}'
835 'extra': '{key}={value|stringescape}'
830 }
836 }
831 # filecopy is preserved for compatibility reasons
837 # filecopy is preserved for compatibility reasons
832 defaulttempl['filecopy'] = defaulttempl['file_copy']
838 defaulttempl['filecopy'] = defaulttempl['file_copy']
833 self.t = templater.templater(mapfile, {'formatnode': formatnode},
839 self.t = templater.templater(mapfile, {'formatnode': formatnode},
834 cache=defaulttempl)
840 cache=defaulttempl)
835 self.cache = {}
841 self.cache = {}
836
842
837 def use_template(self, t):
843 def use_template(self, t):
838 '''set template string to use'''
844 '''set template string to use'''
839 self.t.cache['changeset'] = t
845 self.t.cache['changeset'] = t
840
846
841 def _meaningful_parentrevs(self, ctx):
847 def _meaningful_parentrevs(self, ctx):
842 """Return list of meaningful (or all if debug) parentrevs for rev.
848 """Return list of meaningful (or all if debug) parentrevs for rev.
843 """
849 """
844 parents = ctx.parents()
850 parents = ctx.parents()
845 if len(parents) > 1:
851 if len(parents) > 1:
846 return parents
852 return parents
847 if self.ui.debugflag:
853 if self.ui.debugflag:
848 return [parents[0], self.repo['null']]
854 return [parents[0], self.repo['null']]
849 if parents[0].rev() >= ctx.rev() - 1:
855 if parents[0].rev() >= ctx.rev() - 1:
850 return []
856 return []
851 return parents
857 return parents
852
858
853 def _show(self, ctx, copies, props):
859 def _show(self, ctx, copies, props):
854 '''show a single changeset or file revision'''
860 '''show a single changeset or file revision'''
855
861
856 showlist = templatekw.showlist
862 showlist = templatekw.showlist
857
863
858 # showparents() behaviour depends on ui trace level which
864 # showparents() behaviour depends on ui trace level which
859 # causes unexpected behaviours at templating level and makes
865 # causes unexpected behaviours at templating level and makes
860 # it harder to extract it in a standalone function. Its
866 # it harder to extract it in a standalone function. Its
861 # behaviour cannot be changed so leave it here for now.
867 # behaviour cannot be changed so leave it here for now.
862 def showparents(**args):
868 def showparents(**args):
863 ctx = args['ctx']
869 ctx = args['ctx']
864 parents = [[('rev', p.rev()), ('node', p.hex())]
870 parents = [[('rev', p.rev()), ('node', p.hex())]
865 for p in self._meaningful_parentrevs(ctx)]
871 for p in self._meaningful_parentrevs(ctx)]
866 return showlist('parent', parents, **args)
872 return showlist('parent', parents, **args)
867
873
868 props = props.copy()
874 props = props.copy()
869 props.update(templatekw.keywords)
875 props.update(templatekw.keywords)
870 props['parents'] = showparents
876 props['parents'] = showparents
871 props['templ'] = self.t
877 props['templ'] = self.t
872 props['ctx'] = ctx
878 props['ctx'] = ctx
873 props['repo'] = self.repo
879 props['repo'] = self.repo
874 props['revcache'] = {'copies': copies}
880 props['revcache'] = {'copies': copies}
875 props['cache'] = self.cache
881 props['cache'] = self.cache
876
882
877 # find correct templates for current mode
883 # find correct templates for current mode
878
884
879 tmplmodes = [
885 tmplmodes = [
880 (True, None),
886 (True, None),
881 (self.ui.verbose, 'verbose'),
887 (self.ui.verbose, 'verbose'),
882 (self.ui.quiet, 'quiet'),
888 (self.ui.quiet, 'quiet'),
883 (self.ui.debugflag, 'debug'),
889 (self.ui.debugflag, 'debug'),
884 ]
890 ]
885
891
886 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
892 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
887 for mode, postfix in tmplmodes:
893 for mode, postfix in tmplmodes:
888 for type in types:
894 for type in types:
889 cur = postfix and ('%s_%s' % (type, postfix)) or type
895 cur = postfix and ('%s_%s' % (type, postfix)) or type
890 if mode and cur in self.t:
896 if mode and cur in self.t:
891 types[type] = cur
897 types[type] = cur
892
898
893 try:
899 try:
894
900
895 # write header
901 # write header
896 if types['header']:
902 if types['header']:
897 h = templater.stringify(self.t(types['header'], **props))
903 h = templater.stringify(self.t(types['header'], **props))
898 if self.buffered:
904 if self.buffered:
899 self.header[ctx.rev()] = h
905 self.header[ctx.rev()] = h
900 else:
906 else:
901 self.ui.write(h)
907 self.ui.write(h)
902
908
903 # write changeset metadata, then patch if requested
909 # write changeset metadata, then patch if requested
904 key = types['changeset']
910 key = types['changeset']
905 self.ui.write(templater.stringify(self.t(key, **props)))
911 self.ui.write(templater.stringify(self.t(key, **props)))
906 self.showpatch(ctx.node())
912 self.showpatch(ctx.node())
907
913
908 if types['footer']:
914 if types['footer']:
909 if not self.footer:
915 if not self.footer:
910 self.footer = templater.stringify(self.t(types['footer'],
916 self.footer = templater.stringify(self.t(types['footer'],
911 **props))
917 **props))
912
918
913 except KeyError, inst:
919 except KeyError, inst:
914 msg = _("%s: no key named '%s'")
920 msg = _("%s: no key named '%s'")
915 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
921 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
916 except SyntaxError, inst:
922 except SyntaxError, inst:
917 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
923 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
918
924
919 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
925 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
920 """show one changeset using template or regular display.
926 """show one changeset using template or regular display.
921
927
922 Display format will be the first non-empty hit of:
928 Display format will be the first non-empty hit of:
923 1. option 'template'
929 1. option 'template'
924 2. option 'style'
930 2. option 'style'
925 3. [ui] setting 'logtemplate'
931 3. [ui] setting 'logtemplate'
926 4. [ui] setting 'style'
932 4. [ui] setting 'style'
927 If all of these values are either the unset or the empty string,
933 If all of these values are either the unset or the empty string,
928 regular display via changeset_printer() is done.
934 regular display via changeset_printer() is done.
929 """
935 """
930 # options
936 # options
931 patch = False
937 patch = False
932 if opts.get('patch') or opts.get('stat'):
938 if opts.get('patch') or opts.get('stat'):
933 patch = matchfn or matchall(repo)
939 patch = matchfn or matchall(repo)
934
940
935 tmpl = opts.get('template')
941 tmpl = opts.get('template')
936 style = None
942 style = None
937 if tmpl:
943 if tmpl:
938 tmpl = templater.parsestring(tmpl, quoted=False)
944 tmpl = templater.parsestring(tmpl, quoted=False)
939 else:
945 else:
940 style = opts.get('style')
946 style = opts.get('style')
941
947
942 # ui settings
948 # ui settings
943 if not (tmpl or style):
949 if not (tmpl or style):
944 tmpl = ui.config('ui', 'logtemplate')
950 tmpl = ui.config('ui', 'logtemplate')
945 if tmpl:
951 if tmpl:
946 tmpl = templater.parsestring(tmpl)
952 tmpl = templater.parsestring(tmpl)
947 else:
953 else:
948 style = util.expandpath(ui.config('ui', 'style', ''))
954 style = util.expandpath(ui.config('ui', 'style', ''))
949
955
950 if not (tmpl or style):
956 if not (tmpl or style):
951 return changeset_printer(ui, repo, patch, opts, buffered)
957 return changeset_printer(ui, repo, patch, opts, buffered)
952
958
953 mapfile = None
959 mapfile = None
954 if style and not tmpl:
960 if style and not tmpl:
955 mapfile = style
961 mapfile = style
956 if not os.path.split(mapfile)[0]:
962 if not os.path.split(mapfile)[0]:
957 mapname = (templater.templatepath('map-cmdline.' + mapfile)
963 mapname = (templater.templatepath('map-cmdline.' + mapfile)
958 or templater.templatepath(mapfile))
964 or templater.templatepath(mapfile))
959 if mapname:
965 if mapname:
960 mapfile = mapname
966 mapfile = mapname
961
967
962 try:
968 try:
963 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
969 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
964 except SyntaxError, inst:
970 except SyntaxError, inst:
965 raise util.Abort(inst.args[0])
971 raise util.Abort(inst.args[0])
966 if tmpl:
972 if tmpl:
967 t.use_template(tmpl)
973 t.use_template(tmpl)
968 return t
974 return t
969
975
970 def finddate(ui, repo, date):
976 def finddate(ui, repo, date):
971 """Find the tipmost changeset that matches the given date spec"""
977 """Find the tipmost changeset that matches the given date spec"""
972
978
973 df = util.matchdate(date)
979 df = util.matchdate(date)
974 m = matchall(repo)
980 m = matchall(repo)
975 results = {}
981 results = {}
976
982
977 def prep(ctx, fns):
983 def prep(ctx, fns):
978 d = ctx.date()
984 d = ctx.date()
979 if df(d[0]):
985 if df(d[0]):
980 results[ctx.rev()] = d
986 results[ctx.rev()] = d
981
987
982 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
988 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
983 rev = ctx.rev()
989 rev = ctx.rev()
984 if rev in results:
990 if rev in results:
985 ui.status(_("Found revision %s from %s\n") %
991 ui.status(_("Found revision %s from %s\n") %
986 (rev, util.datestr(results[rev])))
992 (rev, util.datestr(results[rev])))
987 return str(rev)
993 return str(rev)
988
994
989 raise util.Abort(_("revision matching date not found"))
995 raise util.Abort(_("revision matching date not found"))
990
996
991 def walkchangerevs(repo, match, opts, prepare):
997 def walkchangerevs(repo, match, opts, prepare):
992 '''Iterate over files and the revs in which they changed.
998 '''Iterate over files and the revs in which they changed.
993
999
994 Callers most commonly need to iterate backwards over the history
1000 Callers most commonly need to iterate backwards over the history
995 in which they are interested. Doing so has awful (quadratic-looking)
1001 in which they are interested. Doing so has awful (quadratic-looking)
996 performance, so we use iterators in a "windowed" way.
1002 performance, so we use iterators in a "windowed" way.
997
1003
998 We walk a window of revisions in the desired order. Within the
1004 We walk a window of revisions in the desired order. Within the
999 window, we first walk forwards to gather data, then in the desired
1005 window, we first walk forwards to gather data, then in the desired
1000 order (usually backwards) to display it.
1006 order (usually backwards) to display it.
1001
1007
1002 This function returns an iterator yielding contexts. Before
1008 This function returns an iterator yielding contexts. Before
1003 yielding each context, the iterator will first call the prepare
1009 yielding each context, the iterator will first call the prepare
1004 function on each context in the window in forward order.'''
1010 function on each context in the window in forward order.'''
1005
1011
1006 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1012 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1007 if start < end:
1013 if start < end:
1008 while start < end:
1014 while start < end:
1009 yield start, min(windowsize, end - start)
1015 yield start, min(windowsize, end - start)
1010 start += windowsize
1016 start += windowsize
1011 if windowsize < sizelimit:
1017 if windowsize < sizelimit:
1012 windowsize *= 2
1018 windowsize *= 2
1013 else:
1019 else:
1014 while start > end:
1020 while start > end:
1015 yield start, min(windowsize, start - end - 1)
1021 yield start, min(windowsize, start - end - 1)
1016 start -= windowsize
1022 start -= windowsize
1017 if windowsize < sizelimit:
1023 if windowsize < sizelimit:
1018 windowsize *= 2
1024 windowsize *= 2
1019
1025
1020 follow = opts.get('follow') or opts.get('follow_first')
1026 follow = opts.get('follow') or opts.get('follow_first')
1021
1027
1022 if not len(repo):
1028 if not len(repo):
1023 return []
1029 return []
1024
1030
1025 if follow:
1031 if follow:
1026 defrange = '%s:0' % repo['.'].rev()
1032 defrange = '%s:0' % repo['.'].rev()
1027 else:
1033 else:
1028 defrange = '-1:0'
1034 defrange = '-1:0'
1029 revs = revrange(repo, opts['rev'] or [defrange])
1035 revs = revrange(repo, opts['rev'] or [defrange])
1030 if not revs:
1036 if not revs:
1031 return []
1037 return []
1032 wanted = set()
1038 wanted = set()
1033 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1039 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1034 fncache = {}
1040 fncache = {}
1035 change = util.cachefunc(repo.changectx)
1041 change = util.cachefunc(repo.changectx)
1036
1042
1037 if not slowpath and not match.files():
1043 if not slowpath and not match.files():
1038 # No files, no patterns. Display all revs.
1044 # No files, no patterns. Display all revs.
1039 wanted = set(revs)
1045 wanted = set(revs)
1040 copies = []
1046 copies = []
1041
1047
1042 if not slowpath:
1048 if not slowpath:
1043 # Only files, no patterns. Check the history of each file.
1049 # Only files, no patterns. Check the history of each file.
1044 def filerevgen(filelog, node):
1050 def filerevgen(filelog, node):
1045 cl_count = len(repo)
1051 cl_count = len(repo)
1046 if node is None:
1052 if node is None:
1047 last = len(filelog) - 1
1053 last = len(filelog) - 1
1048 else:
1054 else:
1049 last = filelog.rev(node)
1055 last = filelog.rev(node)
1050 for i, window in increasing_windows(last, nullrev):
1056 for i, window in increasing_windows(last, nullrev):
1051 revs = []
1057 revs = []
1052 for j in xrange(i - window, i + 1):
1058 for j in xrange(i - window, i + 1):
1053 n = filelog.node(j)
1059 n = filelog.node(j)
1054 revs.append((filelog.linkrev(j),
1060 revs.append((filelog.linkrev(j),
1055 follow and filelog.renamed(n)))
1061 follow and filelog.renamed(n)))
1056 for rev in reversed(revs):
1062 for rev in reversed(revs):
1057 # only yield rev for which we have the changelog, it can
1063 # only yield rev for which we have the changelog, it can
1058 # happen while doing "hg log" during a pull or commit
1064 # happen while doing "hg log" during a pull or commit
1059 if rev[0] < cl_count:
1065 if rev[0] < cl_count:
1060 yield rev
1066 yield rev
1061 def iterfiles():
1067 def iterfiles():
1062 for filename in match.files():
1068 for filename in match.files():
1063 yield filename, None
1069 yield filename, None
1064 for filename_node in copies:
1070 for filename_node in copies:
1065 yield filename_node
1071 yield filename_node
1066 minrev, maxrev = min(revs), max(revs)
1072 minrev, maxrev = min(revs), max(revs)
1067 for file_, node in iterfiles():
1073 for file_, node in iterfiles():
1068 filelog = repo.file(file_)
1074 filelog = repo.file(file_)
1069 if not len(filelog):
1075 if not len(filelog):
1070 if node is None:
1076 if node is None:
1071 # A zero count may be a directory or deleted file, so
1077 # A zero count may be a directory or deleted file, so
1072 # try to find matching entries on the slow path.
1078 # try to find matching entries on the slow path.
1073 if follow:
1079 if follow:
1074 raise util.Abort(
1080 raise util.Abort(
1075 _('cannot follow nonexistent file: "%s"') % file_)
1081 _('cannot follow nonexistent file: "%s"') % file_)
1076 slowpath = True
1082 slowpath = True
1077 break
1083 break
1078 else:
1084 else:
1079 continue
1085 continue
1080 for rev, copied in filerevgen(filelog, node):
1086 for rev, copied in filerevgen(filelog, node):
1081 if rev <= maxrev:
1087 if rev <= maxrev:
1082 if rev < minrev:
1088 if rev < minrev:
1083 break
1089 break
1084 fncache.setdefault(rev, [])
1090 fncache.setdefault(rev, [])
1085 fncache[rev].append(file_)
1091 fncache[rev].append(file_)
1086 wanted.add(rev)
1092 wanted.add(rev)
1087 if copied:
1093 if copied:
1088 copies.append(copied)
1094 copies.append(copied)
1089 if slowpath:
1095 if slowpath:
1090 if follow:
1096 if follow:
1091 raise util.Abort(_('can only follow copies/renames for explicit '
1097 raise util.Abort(_('can only follow copies/renames for explicit '
1092 'filenames'))
1098 'filenames'))
1093
1099
1094 # The slow path checks files modified in every changeset.
1100 # The slow path checks files modified in every changeset.
1095 def changerevgen():
1101 def changerevgen():
1096 for i, window in increasing_windows(len(repo) - 1, nullrev):
1102 for i, window in increasing_windows(len(repo) - 1, nullrev):
1097 for j in xrange(i - window, i + 1):
1103 for j in xrange(i - window, i + 1):
1098 yield change(j)
1104 yield change(j)
1099
1105
1100 for ctx in changerevgen():
1106 for ctx in changerevgen():
1101 matches = filter(match, ctx.files())
1107 matches = filter(match, ctx.files())
1102 if matches:
1108 if matches:
1103 fncache[ctx.rev()] = matches
1109 fncache[ctx.rev()] = matches
1104 wanted.add(ctx.rev())
1110 wanted.add(ctx.rev())
1105
1111
1106 class followfilter(object):
1112 class followfilter(object):
1107 def __init__(self, onlyfirst=False):
1113 def __init__(self, onlyfirst=False):
1108 self.startrev = nullrev
1114 self.startrev = nullrev
1109 self.roots = set()
1115 self.roots = set()
1110 self.onlyfirst = onlyfirst
1116 self.onlyfirst = onlyfirst
1111
1117
1112 def match(self, rev):
1118 def match(self, rev):
1113 def realparents(rev):
1119 def realparents(rev):
1114 if self.onlyfirst:
1120 if self.onlyfirst:
1115 return repo.changelog.parentrevs(rev)[0:1]
1121 return repo.changelog.parentrevs(rev)[0:1]
1116 else:
1122 else:
1117 return filter(lambda x: x != nullrev,
1123 return filter(lambda x: x != nullrev,
1118 repo.changelog.parentrevs(rev))
1124 repo.changelog.parentrevs(rev))
1119
1125
1120 if self.startrev == nullrev:
1126 if self.startrev == nullrev:
1121 self.startrev = rev
1127 self.startrev = rev
1122 return True
1128 return True
1123
1129
1124 if rev > self.startrev:
1130 if rev > self.startrev:
1125 # forward: all descendants
1131 # forward: all descendants
1126 if not self.roots:
1132 if not self.roots:
1127 self.roots.add(self.startrev)
1133 self.roots.add(self.startrev)
1128 for parent in realparents(rev):
1134 for parent in realparents(rev):
1129 if parent in self.roots:
1135 if parent in self.roots:
1130 self.roots.add(rev)
1136 self.roots.add(rev)
1131 return True
1137 return True
1132 else:
1138 else:
1133 # backwards: all parents
1139 # backwards: all parents
1134 if not self.roots:
1140 if not self.roots:
1135 self.roots.update(realparents(self.startrev))
1141 self.roots.update(realparents(self.startrev))
1136 if rev in self.roots:
1142 if rev in self.roots:
1137 self.roots.remove(rev)
1143 self.roots.remove(rev)
1138 self.roots.update(realparents(rev))
1144 self.roots.update(realparents(rev))
1139 return True
1145 return True
1140
1146
1141 return False
1147 return False
1142
1148
1143 # it might be worthwhile to do this in the iterator if the rev range
1149 # it might be worthwhile to do this in the iterator if the rev range
1144 # is descending and the prune args are all within that range
1150 # is descending and the prune args are all within that range
1145 for rev in opts.get('prune', ()):
1151 for rev in opts.get('prune', ()):
1146 rev = repo.changelog.rev(repo.lookup(rev))
1152 rev = repo.changelog.rev(repo.lookup(rev))
1147 ff = followfilter()
1153 ff = followfilter()
1148 stop = min(revs[0], revs[-1])
1154 stop = min(revs[0], revs[-1])
1149 for x in xrange(rev, stop - 1, -1):
1155 for x in xrange(rev, stop - 1, -1):
1150 if ff.match(x):
1156 if ff.match(x):
1151 wanted.discard(x)
1157 wanted.discard(x)
1152
1158
1153 def iterate():
1159 def iterate():
1154 if follow and not match.files():
1160 if follow and not match.files():
1155 ff = followfilter(onlyfirst=opts.get('follow_first'))
1161 ff = followfilter(onlyfirst=opts.get('follow_first'))
1156 def want(rev):
1162 def want(rev):
1157 return ff.match(rev) and rev in wanted
1163 return ff.match(rev) and rev in wanted
1158 else:
1164 else:
1159 def want(rev):
1165 def want(rev):
1160 return rev in wanted
1166 return rev in wanted
1161
1167
1162 for i, window in increasing_windows(0, len(revs)):
1168 for i, window in increasing_windows(0, len(revs)):
1163 change = util.cachefunc(repo.changectx)
1169 change = util.cachefunc(repo.changectx)
1164 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1170 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1165 for rev in sorted(nrevs):
1171 for rev in sorted(nrevs):
1166 fns = fncache.get(rev)
1172 fns = fncache.get(rev)
1167 ctx = change(rev)
1173 ctx = change(rev)
1168 if not fns:
1174 if not fns:
1169 def fns_generator():
1175 def fns_generator():
1170 for f in ctx.files():
1176 for f in ctx.files():
1171 if match(f):
1177 if match(f):
1172 yield f
1178 yield f
1173 fns = fns_generator()
1179 fns = fns_generator()
1174 prepare(ctx, fns)
1180 prepare(ctx, fns)
1175 for rev in nrevs:
1181 for rev in nrevs:
1176 yield change(rev)
1182 yield change(rev)
1177 return iterate()
1183 return iterate()
1178
1184
1179 def commit(ui, repo, commitfunc, pats, opts):
1185 def commit(ui, repo, commitfunc, pats, opts):
1180 '''commit the specified files or all outstanding changes'''
1186 '''commit the specified files or all outstanding changes'''
1181 date = opts.get('date')
1187 date = opts.get('date')
1182 if date:
1188 if date:
1183 opts['date'] = util.parsedate(date)
1189 opts['date'] = util.parsedate(date)
1184 message = logmessage(opts)
1190 message = logmessage(opts)
1185
1191
1186 # extract addremove carefully -- this function can be called from a command
1192 # extract addremove carefully -- this function can be called from a command
1187 # that doesn't support addremove
1193 # that doesn't support addremove
1188 if opts.get('addremove'):
1194 if opts.get('addremove'):
1189 addremove(repo, pats, opts)
1195 addremove(repo, pats, opts)
1190
1196
1191 return commitfunc(ui, repo, message, match(repo, pats, opts), opts)
1197 return commitfunc(ui, repo, message, match(repo, pats, opts), opts)
1192
1198
1193 def commiteditor(repo, ctx, subs):
1199 def commiteditor(repo, ctx, subs):
1194 if ctx.description():
1200 if ctx.description():
1195 return ctx.description()
1201 return ctx.description()
1196 return commitforceeditor(repo, ctx, subs)
1202 return commitforceeditor(repo, ctx, subs)
1197
1203
1198 def commitforceeditor(repo, ctx, subs):
1204 def commitforceeditor(repo, ctx, subs):
1199 edittext = []
1205 edittext = []
1200 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1206 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1201 if ctx.description():
1207 if ctx.description():
1202 edittext.append(ctx.description())
1208 edittext.append(ctx.description())
1203 edittext.append("")
1209 edittext.append("")
1204 edittext.append("") # Empty line between message and comments.
1210 edittext.append("") # Empty line between message and comments.
1205 edittext.append(_("HG: Enter commit message."
1211 edittext.append(_("HG: Enter commit message."
1206 " Lines beginning with 'HG:' are removed."))
1212 " Lines beginning with 'HG:' are removed."))
1207 edittext.append(_("HG: Leave message empty to abort commit."))
1213 edittext.append(_("HG: Leave message empty to abort commit."))
1208 edittext.append("HG: --")
1214 edittext.append("HG: --")
1209 edittext.append(_("HG: user: %s") % ctx.user())
1215 edittext.append(_("HG: user: %s") % ctx.user())
1210 if ctx.p2():
1216 if ctx.p2():
1211 edittext.append(_("HG: branch merge"))
1217 edittext.append(_("HG: branch merge"))
1212 if ctx.branch():
1218 if ctx.branch():
1213 edittext.append(_("HG: branch '%s'")
1219 edittext.append(_("HG: branch '%s'")
1214 % encoding.tolocal(ctx.branch()))
1220 % encoding.tolocal(ctx.branch()))
1215 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1221 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1216 edittext.extend([_("HG: added %s") % f for f in added])
1222 edittext.extend([_("HG: added %s") % f for f in added])
1217 edittext.extend([_("HG: changed %s") % f for f in modified])
1223 edittext.extend([_("HG: changed %s") % f for f in modified])
1218 edittext.extend([_("HG: removed %s") % f for f in removed])
1224 edittext.extend([_("HG: removed %s") % f for f in removed])
1219 if not added and not modified and not removed:
1225 if not added and not modified and not removed:
1220 edittext.append(_("HG: no files changed"))
1226 edittext.append(_("HG: no files changed"))
1221 edittext.append("")
1227 edittext.append("")
1222 # run editor in the repository root
1228 # run editor in the repository root
1223 olddir = os.getcwd()
1229 olddir = os.getcwd()
1224 os.chdir(repo.root)
1230 os.chdir(repo.root)
1225 text = repo.ui.edit("\n".join(edittext), ctx.user())
1231 text = repo.ui.edit("\n".join(edittext), ctx.user())
1226 text = re.sub("(?m)^HG:.*\n", "", text)
1232 text = re.sub("(?m)^HG:.*\n", "", text)
1227 os.chdir(olddir)
1233 os.chdir(olddir)
1228
1234
1229 if not text.strip():
1235 if not text.strip():
1230 raise util.Abort(_("empty commit message"))
1236 raise util.Abort(_("empty commit message"))
1231
1237
1232 return text
1238 return text
@@ -1,466 +1,466 b''
1 adding a
1 adding a
2 changeset: 0:8580ff50825a
2 changeset: 0:8580ff50825a
3 user: test
3 user: test
4 date: Thu Jan 01 00:00:01 1970 +0000
4 date: Thu Jan 01 00:00:01 1970 +0000
5 summary: a
5 summary: a
6
6
7 % -f, directory
7 % -f, directory
8 abort: cannot follow nonexistent file: "dir"
8 abort: cannot follow nonexistent file: "dir"
9 % -f, but no args
9 % -f, but no args
10 changeset: 4:66c1345dc4f9
10 changeset: 4:66c1345dc4f9
11 tag: tip
11 tag: tip
12 user: test
12 user: test
13 date: Thu Jan 01 00:00:05 1970 +0000
13 date: Thu Jan 01 00:00:05 1970 +0000
14 summary: e
14 summary: e
15
15
16 changeset: 3:7c6c671bb7cc
16 changeset: 3:7c6c671bb7cc
17 user: test
17 user: test
18 date: Thu Jan 01 00:00:04 1970 +0000
18 date: Thu Jan 01 00:00:04 1970 +0000
19 summary: d
19 summary: d
20
20
21 changeset: 2:41dd4284081e
21 changeset: 2:41dd4284081e
22 user: test
22 user: test
23 date: Thu Jan 01 00:00:03 1970 +0000
23 date: Thu Jan 01 00:00:03 1970 +0000
24 summary: c
24 summary: c
25
25
26 changeset: 1:784de7cef101
26 changeset: 1:784de7cef101
27 user: test
27 user: test
28 date: Thu Jan 01 00:00:02 1970 +0000
28 date: Thu Jan 01 00:00:02 1970 +0000
29 summary: b
29 summary: b
30
30
31 changeset: 0:8580ff50825a
31 changeset: 0:8580ff50825a
32 user: test
32 user: test
33 date: Thu Jan 01 00:00:01 1970 +0000
33 date: Thu Jan 01 00:00:01 1970 +0000
34 summary: a
34 summary: a
35
35
36 % one rename
36 % one rename
37 changeset: 0:8580ff50825a
37 changeset: 0:8580ff50825a
38 user: test
38 user: test
39 date: Thu Jan 01 00:00:01 1970 +0000
39 date: Thu Jan 01 00:00:01 1970 +0000
40 files: a
40 files: a
41 description:
41 description:
42 a
42 a
43
43
44
44
45 % many renames
45 % many renames
46 changeset: 4:66c1345dc4f9
46 changeset: 4:66c1345dc4f9
47 tag: tip
47 tag: tip
48 user: test
48 user: test
49 date: Thu Jan 01 00:00:05 1970 +0000
49 date: Thu Jan 01 00:00:05 1970 +0000
50 files: dir/b e
50 files: dir/b e
51 description:
51 description:
52 e
52 e
53
53
54
54
55 changeset: 2:41dd4284081e
55 changeset: 2:41dd4284081e
56 user: test
56 user: test
57 date: Thu Jan 01 00:00:03 1970 +0000
57 date: Thu Jan 01 00:00:03 1970 +0000
58 files: b dir/b
58 files: b dir/b
59 description:
59 description:
60 c
60 c
61
61
62
62
63 changeset: 1:784de7cef101
63 changeset: 1:784de7cef101
64 user: test
64 user: test
65 date: Thu Jan 01 00:00:02 1970 +0000
65 date: Thu Jan 01 00:00:02 1970 +0000
66 files: b
66 files: b
67 description:
67 description:
68 b
68 b
69
69
70
70
71 changeset: 0:8580ff50825a
71 changeset: 0:8580ff50825a
72 user: test
72 user: test
73 date: Thu Jan 01 00:00:01 1970 +0000
73 date: Thu Jan 01 00:00:01 1970 +0000
74 files: a
74 files: a
75 description:
75 description:
76 a
76 a
77
77
78
78
79 % log copies with --copies
79 % log copies with --copies
80 4 e (dir/b)
80 4 e (dir/b)
81 3 b (a)
81 3 b (a)
82 2 dir/b (b)
82 2 dir/b (b)
83 1 b (a)
83 1 b (a)
84 0
84 0
85 % log copies switch without --copies, with old filecopy template
85 % log copies switch without --copies, with old filecopy template
86 4
86 4
87 3
87 3
88 2
88 2
89 1
89 1
90 0
90 0
91 % log copies switch with --copies
91 % log copies switch with --copies
92 4 e (dir/b)
92 4 e (dir/b)
93 3 b (a)
93 3 b (a)
94 2 dir/b (b)
94 2 dir/b (b)
95 1 b (a)
95 1 b (a)
96 0
96 0
97 % log copies with hardcoded style and with --style=default
97 % log copies with hardcoded style and with --style=default
98 changeset: 4:66c1345dc4f9
98 changeset: 4:66c1345dc4f9
99 tag: tip
99 tag: tip
100 user: test
100 user: test
101 date: Thu Jan 01 00:00:05 1970 +0000
101 date: Thu Jan 01 00:00:05 1970 +0000
102 files: dir/b e
102 files: dir/b e
103 copies: e (dir/b)
103 copies: e (dir/b)
104 description:
104 description:
105 e
105 e
106
106
107
107
108 changeset: 4:66c1345dc4f9
108 changeset: 4:66c1345dc4f9
109 tag: tip
109 tag: tip
110 user: test
110 user: test
111 date: Thu Jan 01 00:00:05 1970 +0000
111 date: Thu Jan 01 00:00:05 1970 +0000
112 files: dir/b e
112 files: dir/b e
113 copies: e (dir/b)
113 copies: e (dir/b)
114 description:
114 description:
115 e
115 e
116
116
117
117
118 % log copies, non-linear manifest
118 % log copies, non-linear manifest
119 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
119 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
120 adding foo
120 adding foo
121 created new head
121 created new head
122 5 e (dir/b)
122 5 e (dir/b)
123 % log copies, execute bit set
123 % log copies, execute bit set
124 6
124 6
125 % log -p d
125 % log -p d
126 changeset: 3:7c6c671bb7cc
126 changeset: 3:7c6c671bb7cc
127 user: test
127 user: test
128 date: Thu Jan 01 00:00:04 1970 +0000
128 date: Thu Jan 01 00:00:04 1970 +0000
129 files: a b d
129 files: a b d
130 description:
130 description:
131 d
131 d
132
132
133
133
134 diff -r 41dd4284081e -r 7c6c671bb7cc d
134 diff -r 41dd4284081e -r 7c6c671bb7cc d
135 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
135 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
136 +++ b/d Thu Jan 01 00:00:04 1970 +0000
136 +++ b/d Thu Jan 01 00:00:04 1970 +0000
137 @@ -0,0 +1,1 @@
137 @@ -0,0 +1,1 @@
138 +a
138 +a
139
139
140 adding base
140 adding base
141 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
142 adding b1
142 adding b1
143 created new head
143 created new head
144 % log -f
144 % log -f
145 changeset: 3:e62f78d544b4
145 changeset: 3:e62f78d544b4
146 tag: tip
146 tag: tip
147 parent: 1:3d5bf5654eda
147 parent: 1:3d5bf5654eda
148 user: test
148 user: test
149 date: Thu Jan 01 00:00:01 1970 +0000
149 date: Thu Jan 01 00:00:01 1970 +0000
150 summary: b1
150 summary: b1
151
151
152 changeset: 1:3d5bf5654eda
152 changeset: 1:3d5bf5654eda
153 user: test
153 user: test
154 date: Thu Jan 01 00:00:01 1970 +0000
154 date: Thu Jan 01 00:00:01 1970 +0000
155 summary: r1
155 summary: r1
156
156
157 changeset: 0:67e992f2c4f3
157 changeset: 0:67e992f2c4f3
158 user: test
158 user: test
159 date: Thu Jan 01 00:00:01 1970 +0000
159 date: Thu Jan 01 00:00:01 1970 +0000
160 summary: base
160 summary: base
161
161
162 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
162 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
163 adding b2
163 adding b2
164 created new head
164 created new head
165 % log -f -r 1:tip
165 % log -f -r 1:tip
166 changeset: 1:3d5bf5654eda
166 changeset: 1:3d5bf5654eda
167 user: test
167 user: test
168 date: Thu Jan 01 00:00:01 1970 +0000
168 date: Thu Jan 01 00:00:01 1970 +0000
169 summary: r1
169 summary: r1
170
170
171 changeset: 2:60c670bf5b30
171 changeset: 2:60c670bf5b30
172 user: test
172 user: test
173 date: Thu Jan 01 00:00:01 1970 +0000
173 date: Thu Jan 01 00:00:01 1970 +0000
174 summary: r2
174 summary: r2
175
175
176 changeset: 3:e62f78d544b4
176 changeset: 3:e62f78d544b4
177 parent: 1:3d5bf5654eda
177 parent: 1:3d5bf5654eda
178 user: test
178 user: test
179 date: Thu Jan 01 00:00:01 1970 +0000
179 date: Thu Jan 01 00:00:01 1970 +0000
180 summary: b1
180 summary: b1
181
181
182 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
182 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
183 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
183 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
184 (branch merge, don't forget to commit)
184 (branch merge, don't forget to commit)
185 % log -r . with two parents
185 % log -r . with two parents
186 changeset: 3:e62f78d544b4
186 changeset: 3:e62f78d544b4
187 parent: 1:3d5bf5654eda
187 parent: 1:3d5bf5654eda
188 user: test
188 user: test
189 date: Thu Jan 01 00:00:01 1970 +0000
189 date: Thu Jan 01 00:00:01 1970 +0000
190 summary: b1
190 summary: b1
191
191
192 % log -r . with one parent
192 % log -r . with one parent
193 changeset: 5:302e9dd6890d
193 changeset: 5:302e9dd6890d
194 tag: tip
194 tag: tip
195 parent: 3:e62f78d544b4
195 parent: 3:e62f78d544b4
196 parent: 4:ddb82e70d1a1
196 parent: 4:ddb82e70d1a1
197 user: test
197 user: test
198 date: Thu Jan 01 00:00:01 1970 +0000
198 date: Thu Jan 01 00:00:01 1970 +0000
199 summary: m12
199 summary: m12
200
200
201 % log --follow-first
201 % log --follow-first
202 changeset: 6:2404bbcab562
202 changeset: 6:2404bbcab562
203 tag: tip
203 tag: tip
204 user: test
204 user: test
205 date: Thu Jan 01 00:00:01 1970 +0000
205 date: Thu Jan 01 00:00:01 1970 +0000
206 summary: b1.1
206 summary: b1.1
207
207
208 changeset: 5:302e9dd6890d
208 changeset: 5:302e9dd6890d
209 parent: 3:e62f78d544b4
209 parent: 3:e62f78d544b4
210 parent: 4:ddb82e70d1a1
210 parent: 4:ddb82e70d1a1
211 user: test
211 user: test
212 date: Thu Jan 01 00:00:01 1970 +0000
212 date: Thu Jan 01 00:00:01 1970 +0000
213 summary: m12
213 summary: m12
214
214
215 changeset: 3:e62f78d544b4
215 changeset: 3:e62f78d544b4
216 parent: 1:3d5bf5654eda
216 parent: 1:3d5bf5654eda
217 user: test
217 user: test
218 date: Thu Jan 01 00:00:01 1970 +0000
218 date: Thu Jan 01 00:00:01 1970 +0000
219 summary: b1
219 summary: b1
220
220
221 changeset: 1:3d5bf5654eda
221 changeset: 1:3d5bf5654eda
222 user: test
222 user: test
223 date: Thu Jan 01 00:00:01 1970 +0000
223 date: Thu Jan 01 00:00:01 1970 +0000
224 summary: r1
224 summary: r1
225
225
226 changeset: 0:67e992f2c4f3
226 changeset: 0:67e992f2c4f3
227 user: test
227 user: test
228 date: Thu Jan 01 00:00:01 1970 +0000
228 date: Thu Jan 01 00:00:01 1970 +0000
229 summary: base
229 summary: base
230
230
231 % log -P 2
231 % log -P 2
232 changeset: 6:2404bbcab562
232 changeset: 6:2404bbcab562
233 tag: tip
233 tag: tip
234 user: test
234 user: test
235 date: Thu Jan 01 00:00:01 1970 +0000
235 date: Thu Jan 01 00:00:01 1970 +0000
236 summary: b1.1
236 summary: b1.1
237
237
238 changeset: 5:302e9dd6890d
238 changeset: 5:302e9dd6890d
239 parent: 3:e62f78d544b4
239 parent: 3:e62f78d544b4
240 parent: 4:ddb82e70d1a1
240 parent: 4:ddb82e70d1a1
241 user: test
241 user: test
242 date: Thu Jan 01 00:00:01 1970 +0000
242 date: Thu Jan 01 00:00:01 1970 +0000
243 summary: m12
243 summary: m12
244
244
245 changeset: 4:ddb82e70d1a1
245 changeset: 4:ddb82e70d1a1
246 parent: 0:67e992f2c4f3
246 parent: 0:67e992f2c4f3
247 user: test
247 user: test
248 date: Thu Jan 01 00:00:01 1970 +0000
248 date: Thu Jan 01 00:00:01 1970 +0000
249 summary: b2
249 summary: b2
250
250
251 changeset: 3:e62f78d544b4
251 changeset: 3:e62f78d544b4
252 parent: 1:3d5bf5654eda
252 parent: 1:3d5bf5654eda
253 user: test
253 user: test
254 date: Thu Jan 01 00:00:01 1970 +0000
254 date: Thu Jan 01 00:00:01 1970 +0000
255 summary: b1
255 summary: b1
256
256
257 % log -r tip -p --git
257 % log -r tip -p --git
258 changeset: 6:2404bbcab562
258 changeset: 6:2404bbcab562
259 tag: tip
259 tag: tip
260 user: test
260 user: test
261 date: Thu Jan 01 00:00:01 1970 +0000
261 date: Thu Jan 01 00:00:01 1970 +0000
262 summary: b1.1
262 summary: b1.1
263
263
264 diff --git a/b1 b/b1
264 diff --git a/b1 b/b1
265 --- a/b1
265 --- a/b1
266 +++ b/b1
266 +++ b/b1
267 @@ -1,1 +1,2 @@
267 @@ -1,1 +1,2 @@
268 b1
268 b1
269 +postm
269 +postm
270
270
271 % log -r ""
271 % log -r ""
272 abort: 00changelog.i@: ambiguous identifier!
272 hg: parse error: empty query
273 % log -r <some unknown node id>
273 % log -r <some unknown node id>
274 abort: unknown revision '1000000000000000000000000000000000000000'!
274 abort: unknown revision '1000000000000000000000000000000000000000'!
275 % log -k r1
275 % log -k r1
276 changeset: 1:3d5bf5654eda
276 changeset: 1:3d5bf5654eda
277 user: test
277 user: test
278 date: Thu Jan 01 00:00:01 1970 +0000
278 date: Thu Jan 01 00:00:01 1970 +0000
279 summary: r1
279 summary: r1
280
280
281 % log -d -1
281 % log -d -1
282 % log -p -l2 --color=always
282 % log -p -l2 --color=always
283 changeset: 6:2404bbcab562
283 changeset: 6:2404bbcab562
284 tag: tip
284 tag: tip
285 user: test
285 user: test
286 date: Thu Jan 01 00:00:01 1970 +0000
286 date: Thu Jan 01 00:00:01 1970 +0000
287 summary: b1.1
287 summary: b1.1
288
288
289 diff -r 302e9dd6890d -r 2404bbcab562 b1
289 diff -r 302e9dd6890d -r 2404bbcab562 b1
290 --- a/b1 Thu Jan 01 00:00:01 1970 +0000
290 --- a/b1 Thu Jan 01 00:00:01 1970 +0000
291 +++ b/b1 Thu Jan 01 00:00:01 1970 +0000
291 +++ b/b1 Thu Jan 01 00:00:01 1970 +0000
292 @@ -1,1 +1,2 @@
292 @@ -1,1 +1,2 @@
293 b1
293 b1
294 +postm
294 +postm
295
295
296 changeset: 5:302e9dd6890d
296 changeset: 5:302e9dd6890d
297 parent: 3:e62f78d544b4
297 parent: 3:e62f78d544b4
298 parent: 4:ddb82e70d1a1
298 parent: 4:ddb82e70d1a1
299 user: test
299 user: test
300 date: Thu Jan 01 00:00:01 1970 +0000
300 date: Thu Jan 01 00:00:01 1970 +0000
301 summary: m12
301 summary: m12
302
302
303 diff -r e62f78d544b4 -r 302e9dd6890d b2
303 diff -r e62f78d544b4 -r 302e9dd6890d b2
304 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
304 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
305 +++ b/b2 Thu Jan 01 00:00:01 1970 +0000
305 +++ b/b2 Thu Jan 01 00:00:01 1970 +0000
306 @@ -0,0 +1,1 @@
306 @@ -0,0 +1,1 @@
307 +b2
307 +b2
308
308
309 % log -r tip --stat
309 % log -r tip --stat
310 changeset: 6:2404bbcab562
310 changeset: 6:2404bbcab562
311 tag: tip
311 tag: tip
312 user: test
312 user: test
313 date: Thu Jan 01 00:00:01 1970 +0000
313 date: Thu Jan 01 00:00:01 1970 +0000
314 summary: b1.1
314 summary: b1.1
315
315
316 b1 | 1 +
316 b1 | 1 +
317 1 files changed, 1 insertions(+), 0 deletions(-)
317 1 files changed, 1 insertions(+), 0 deletions(-)
318
318
319 adding a
319 adding a
320 adding b
320 adding b
321 changeset: 0:29a4c94f1924
321 changeset: 0:29a4c94f1924
322 user: User One <user1@example.org>
322 user: User One <user1@example.org>
323 date: Thu Jan 01 00:00:00 1970 +0000
323 date: Thu Jan 01 00:00:00 1970 +0000
324 summary: a
324 summary: a
325
325
326 changeset: 1:e834b5e69c0e
326 changeset: 1:e834b5e69c0e
327 tag: tip
327 tag: tip
328 user: User Two <user2@example.org>
328 user: User Two <user2@example.org>
329 date: Thu Jan 01 00:00:00 1970 +0000
329 date: Thu Jan 01 00:00:00 1970 +0000
330 summary: b
330 summary: b
331
331
332 changeset: 0:29a4c94f1924
332 changeset: 0:29a4c94f1924
333 user: User One <user1@example.org>
333 user: User One <user1@example.org>
334 date: Thu Jan 01 00:00:00 1970 +0000
334 date: Thu Jan 01 00:00:00 1970 +0000
335 summary: a
335 summary: a
336
336
337 adding a
337 adding a
338 marked working directory as branch test
338 marked working directory as branch test
339 adding b
339 adding b
340 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
340 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
341 adding c
341 adding c
342 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
342 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
343 adding c
343 adding c
344 % log -b default
344 % log -b default
345 changeset: 2:c3a4f03cc9a7
345 changeset: 2:c3a4f03cc9a7
346 parent: 0:24427303d56f
346 parent: 0:24427303d56f
347 user: test
347 user: test
348 date: Thu Jan 01 00:00:00 1970 +0000
348 date: Thu Jan 01 00:00:00 1970 +0000
349 summary: commit on default
349 summary: commit on default
350
350
351 changeset: 0:24427303d56f
351 changeset: 0:24427303d56f
352 user: test
352 user: test
353 date: Thu Jan 01 00:00:00 1970 +0000
353 date: Thu Jan 01 00:00:00 1970 +0000
354 summary: commit on default
354 summary: commit on default
355
355
356 % log -b test
356 % log -b test
357 changeset: 3:f5d8de11c2e2
357 changeset: 3:f5d8de11c2e2
358 branch: test
358 branch: test
359 tag: tip
359 tag: tip
360 parent: 1:d32277701ccb
360 parent: 1:d32277701ccb
361 user: test
361 user: test
362 date: Thu Jan 01 00:00:00 1970 +0000
362 date: Thu Jan 01 00:00:00 1970 +0000
363 summary: commit on test
363 summary: commit on test
364
364
365 changeset: 1:d32277701ccb
365 changeset: 1:d32277701ccb
366 branch: test
366 branch: test
367 user: test
367 user: test
368 date: Thu Jan 01 00:00:00 1970 +0000
368 date: Thu Jan 01 00:00:00 1970 +0000
369 summary: commit on test
369 summary: commit on test
370
370
371 % log -b dummy
371 % log -b dummy
372 abort: unknown revision 'dummy'!
372 abort: unknown revision 'dummy'!
373 % log -b .
373 % log -b .
374 changeset: 3:f5d8de11c2e2
374 changeset: 3:f5d8de11c2e2
375 branch: test
375 branch: test
376 tag: tip
376 tag: tip
377 parent: 1:d32277701ccb
377 parent: 1:d32277701ccb
378 user: test
378 user: test
379 date: Thu Jan 01 00:00:00 1970 +0000
379 date: Thu Jan 01 00:00:00 1970 +0000
380 summary: commit on test
380 summary: commit on test
381
381
382 changeset: 1:d32277701ccb
382 changeset: 1:d32277701ccb
383 branch: test
383 branch: test
384 user: test
384 user: test
385 date: Thu Jan 01 00:00:00 1970 +0000
385 date: Thu Jan 01 00:00:00 1970 +0000
386 summary: commit on test
386 summary: commit on test
387
387
388 % log -b default -b test
388 % log -b default -b test
389 changeset: 3:f5d8de11c2e2
389 changeset: 3:f5d8de11c2e2
390 branch: test
390 branch: test
391 tag: tip
391 tag: tip
392 parent: 1:d32277701ccb
392 parent: 1:d32277701ccb
393 user: test
393 user: test
394 date: Thu Jan 01 00:00:00 1970 +0000
394 date: Thu Jan 01 00:00:00 1970 +0000
395 summary: commit on test
395 summary: commit on test
396
396
397 changeset: 2:c3a4f03cc9a7
397 changeset: 2:c3a4f03cc9a7
398 parent: 0:24427303d56f
398 parent: 0:24427303d56f
399 user: test
399 user: test
400 date: Thu Jan 01 00:00:00 1970 +0000
400 date: Thu Jan 01 00:00:00 1970 +0000
401 summary: commit on default
401 summary: commit on default
402
402
403 changeset: 1:d32277701ccb
403 changeset: 1:d32277701ccb
404 branch: test
404 branch: test
405 user: test
405 user: test
406 date: Thu Jan 01 00:00:00 1970 +0000
406 date: Thu Jan 01 00:00:00 1970 +0000
407 summary: commit on test
407 summary: commit on test
408
408
409 changeset: 0:24427303d56f
409 changeset: 0:24427303d56f
410 user: test
410 user: test
411 date: Thu Jan 01 00:00:00 1970 +0000
411 date: Thu Jan 01 00:00:00 1970 +0000
412 summary: commit on default
412 summary: commit on default
413
413
414 % log -b default -b .
414 % log -b default -b .
415 changeset: 3:f5d8de11c2e2
415 changeset: 3:f5d8de11c2e2
416 branch: test
416 branch: test
417 tag: tip
417 tag: tip
418 parent: 1:d32277701ccb
418 parent: 1:d32277701ccb
419 user: test
419 user: test
420 date: Thu Jan 01 00:00:00 1970 +0000
420 date: Thu Jan 01 00:00:00 1970 +0000
421 summary: commit on test
421 summary: commit on test
422
422
423 changeset: 2:c3a4f03cc9a7
423 changeset: 2:c3a4f03cc9a7
424 parent: 0:24427303d56f
424 parent: 0:24427303d56f
425 user: test
425 user: test
426 date: Thu Jan 01 00:00:00 1970 +0000
426 date: Thu Jan 01 00:00:00 1970 +0000
427 summary: commit on default
427 summary: commit on default
428
428
429 changeset: 1:d32277701ccb
429 changeset: 1:d32277701ccb
430 branch: test
430 branch: test
431 user: test
431 user: test
432 date: Thu Jan 01 00:00:00 1970 +0000
432 date: Thu Jan 01 00:00:00 1970 +0000
433 summary: commit on test
433 summary: commit on test
434
434
435 changeset: 0:24427303d56f
435 changeset: 0:24427303d56f
436 user: test
436 user: test
437 date: Thu Jan 01 00:00:00 1970 +0000
437 date: Thu Jan 01 00:00:00 1970 +0000
438 summary: commit on default
438 summary: commit on default
439
439
440 % log -b . -b test
440 % log -b . -b test
441 changeset: 3:f5d8de11c2e2
441 changeset: 3:f5d8de11c2e2
442 branch: test
442 branch: test
443 tag: tip
443 tag: tip
444 parent: 1:d32277701ccb
444 parent: 1:d32277701ccb
445 user: test
445 user: test
446 date: Thu Jan 01 00:00:00 1970 +0000
446 date: Thu Jan 01 00:00:00 1970 +0000
447 summary: commit on test
447 summary: commit on test
448
448
449 changeset: 1:d32277701ccb
449 changeset: 1:d32277701ccb
450 branch: test
450 branch: test
451 user: test
451 user: test
452 date: Thu Jan 01 00:00:00 1970 +0000
452 date: Thu Jan 01 00:00:00 1970 +0000
453 summary: commit on test
453 summary: commit on test
454
454
455 % log -b 2
455 % log -b 2
456 changeset: 2:c3a4f03cc9a7
456 changeset: 2:c3a4f03cc9a7
457 parent: 0:24427303d56f
457 parent: 0:24427303d56f
458 user: test
458 user: test
459 date: Thu Jan 01 00:00:00 1970 +0000
459 date: Thu Jan 01 00:00:00 1970 +0000
460 summary: commit on default
460 summary: commit on default
461
461
462 changeset: 0:24427303d56f
462 changeset: 0:24427303d56f
463 user: test
463 user: test
464 date: Thu Jan 01 00:00:00 1970 +0000
464 date: Thu Jan 01 00:00:00 1970 +0000
465 summary: commit on default
465 summary: commit on default
466
466
General Comments 0
You need to be logged in to leave comments. Login now