##// END OF EJS Templates
log: add bookmark option to "hg log"...
Sebastien Boisvert -
r46512:0aa118f1 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,127 b''
1 Test 'hg log' with a bookmark
2
3
4 Create the repository
5
6 $ hg init Test-D8973
7 $ cd Test-D8973
8 $ echo "bar" > foo.txt
9 $ hg add foo.txt
10 $ hg commit -m "Add foo in 'default'"
11
12
13 Add a bookmark for topic X
14
15 $ hg branch -f sebhtml
16 marked working directory as branch sebhtml
17 (branches are permanent and global, did you want a bookmark?)
18
19 $ hg bookmark sebhtml/99991-topic-X
20 $ hg up sebhtml/99991-topic-X
21 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
22
23 $ echo "X" > x.txt
24 $ hg add x.txt
25 $ hg commit -m "Add x.txt in 'sebhtml/99991-topic-X'"
26
27 $ hg log -B sebhtml/99991-topic-X
28 changeset: 1:29f39dea9bf9
29 branch: sebhtml
30 bookmark: sebhtml/99991-topic-X
31 tag: tip
32 user: test
33 date: Thu Jan 01 00:00:00 1970 +0000
34 summary: Add x.txt in 'sebhtml/99991-topic-X'
35
36
37 Add a bookmark for topic Y
38
39 $ hg update default
40 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
41 (leaving bookmark sebhtml/99991-topic-X)
42
43 $ echo "Y" > y.txt
44 $ hg add y.txt
45 $ hg branch -f sebhtml
46 marked working directory as branch sebhtml
47 $ hg bookmark sebhtml/99992-topic-Y
48 $ hg up sebhtml/99992-topic-Y
49 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
50 $ hg commit -m "Add y.txt in 'sebhtml/99992-topic-Y'"
51 created new head
52
53 $ hg log -B sebhtml/99992-topic-Y
54 changeset: 2:11df7969cf8d
55 branch: sebhtml
56 bookmark: sebhtml/99992-topic-Y
57 tag: tip
58 parent: 0:eaea25376a59
59 user: test
60 date: Thu Jan 01 00:00:00 1970 +0000
61 summary: Add y.txt in 'sebhtml/99992-topic-Y'
62
63
64 The log of topic Y does not interfere with the log of topic X
65
66 $ hg log -B sebhtml/99991-topic-X
67 changeset: 1:29f39dea9bf9
68 branch: sebhtml
69 bookmark: sebhtml/99991-topic-X
70 user: test
71 date: Thu Jan 01 00:00:00 1970 +0000
72 summary: Add x.txt in 'sebhtml/99991-topic-X'
73
74
75 Merge topics Y and X in the default branch
76
77 $ hg update default
78 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
79 (leaving bookmark sebhtml/99992-topic-Y)
80
81 $ hg bookmark
82 sebhtml/99991-topic-X 1:29f39dea9bf9
83 sebhtml/99992-topic-Y 2:11df7969cf8d
84
85 $ hg merge sebhtml/99992-topic-Y
86 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
87 (branch merge, don't forget to commit)
88
89 $ hg commit -m "Merge branch 'sebhtml/99992-topic-Y' into 'default'"
90
91 $ hg update default
92 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
93
94 $ hg merge sebhtml/99991-topic-X
95 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
96 (branch merge, don't forget to commit)
97
98 $ hg commit -m "Merge branch 'sebhtml/99991-topic-X' into 'default'"
99
100
101 Check the log of topic X, topic Y, and default branch
102
103 $ hg log -B sebhtml/99992-topic-Y
104
105 $ hg log -B sebhtml/99991-topic-X
106
107 $ hg log -b default
108 changeset: 4:c26ba8c1e1cb
109 tag: tip
110 parent: 3:2189f3fb90d6
111 parent: 1:29f39dea9bf9
112 user: test
113 date: Thu Jan 01 00:00:00 1970 +0000
114 summary: Merge branch 'sebhtml/99991-topic-X' into 'default'
115
116 changeset: 3:2189f3fb90d6
117 parent: 0:eaea25376a59
118 parent: 2:11df7969cf8d
119 user: test
120 date: Thu Jan 01 00:00:00 1970 +0000
121 summary: Merge branch 'sebhtml/99992-topic-Y' into 'default'
122
123 changeset: 0:eaea25376a59
124 user: test
125 date: Thu Jan 01 00:00:00 1970 +0000
126 summary: Add foo in 'default'
127
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,1210 +1,1230 b''
1 # logcmdutil.py - utility for log-like commands
1 # logcmdutil.py - utility for log-like commands
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import itertools
10 import itertools
11 import os
11 import os
12 import posixpath
12 import posixpath
13
13
14 from .i18n import _
14 from .i18n import _
15 from .node import (
15 from .node import (
16 nullid,
16 nullid,
17 wdirid,
17 wdirid,
18 wdirrev,
18 wdirrev,
19 )
19 )
20
20
21 from .thirdparty import attr
21 from .thirdparty import attr
22
22
23 from . import (
23 from . import (
24 dagop,
24 dagop,
25 error,
25 error,
26 formatter,
26 formatter,
27 graphmod,
27 graphmod,
28 match as matchmod,
28 match as matchmod,
29 mdiff,
29 mdiff,
30 patch,
30 patch,
31 pathutil,
31 pathutil,
32 pycompat,
32 pycompat,
33 revset,
33 revset,
34 revsetlang,
34 revsetlang,
35 scmutil,
35 scmutil,
36 smartset,
36 smartset,
37 templatekw,
37 templatekw,
38 templater,
38 templater,
39 util,
39 util,
40 )
40 )
41 from .utils import (
41 from .utils import (
42 dateutil,
42 dateutil,
43 stringutil,
43 stringutil,
44 )
44 )
45
45
46
46
47 if pycompat.TYPE_CHECKING:
47 if pycompat.TYPE_CHECKING:
48 from typing import (
48 from typing import (
49 Any,
49 Any,
50 Callable,
50 Callable,
51 Dict,
51 Dict,
52 List,
52 List,
53 Optional,
53 Optional,
54 Tuple,
54 Tuple,
55 )
55 )
56
56
57 for t in (Any, Callable, Dict, List, Optional, Tuple):
57 for t in (Any, Callable, Dict, List, Optional, Tuple):
58 assert t
58 assert t
59
59
60
60
61 def getlimit(opts):
61 def getlimit(opts):
62 """get the log limit according to option -l/--limit"""
62 """get the log limit according to option -l/--limit"""
63 limit = opts.get(b'limit')
63 limit = opts.get(b'limit')
64 if limit:
64 if limit:
65 try:
65 try:
66 limit = int(limit)
66 limit = int(limit)
67 except ValueError:
67 except ValueError:
68 raise error.Abort(_(b'limit must be a positive integer'))
68 raise error.Abort(_(b'limit must be a positive integer'))
69 if limit <= 0:
69 if limit <= 0:
70 raise error.Abort(_(b'limit must be positive'))
70 raise error.Abort(_(b'limit must be positive'))
71 else:
71 else:
72 limit = None
72 limit = None
73 return limit
73 return limit
74
74
75
75
76 def diffordiffstat(
76 def diffordiffstat(
77 ui,
77 ui,
78 repo,
78 repo,
79 diffopts,
79 diffopts,
80 ctx1,
80 ctx1,
81 ctx2,
81 ctx2,
82 match,
82 match,
83 changes=None,
83 changes=None,
84 stat=False,
84 stat=False,
85 fp=None,
85 fp=None,
86 graphwidth=0,
86 graphwidth=0,
87 prefix=b'',
87 prefix=b'',
88 root=b'',
88 root=b'',
89 listsubrepos=False,
89 listsubrepos=False,
90 hunksfilterfn=None,
90 hunksfilterfn=None,
91 ):
91 ):
92 '''show diff or diffstat.'''
92 '''show diff or diffstat.'''
93 if root:
93 if root:
94 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
94 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
95 else:
95 else:
96 relroot = b''
96 relroot = b''
97 copysourcematch = None
97 copysourcematch = None
98
98
99 def compose(f, g):
99 def compose(f, g):
100 return lambda x: f(g(x))
100 return lambda x: f(g(x))
101
101
102 def pathfn(f):
102 def pathfn(f):
103 return posixpath.join(prefix, f)
103 return posixpath.join(prefix, f)
104
104
105 if relroot != b'':
105 if relroot != b'':
106 # XXX relative roots currently don't work if the root is within a
106 # XXX relative roots currently don't work if the root is within a
107 # subrepo
107 # subrepo
108 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
108 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
109 uirelroot = uipathfn(pathfn(relroot))
109 uirelroot = uipathfn(pathfn(relroot))
110 relroot += b'/'
110 relroot += b'/'
111 for matchroot in match.files():
111 for matchroot in match.files():
112 if not matchroot.startswith(relroot):
112 if not matchroot.startswith(relroot):
113 ui.warn(
113 ui.warn(
114 _(b'warning: %s not inside relative root %s\n')
114 _(b'warning: %s not inside relative root %s\n')
115 % (uipathfn(pathfn(matchroot)), uirelroot)
115 % (uipathfn(pathfn(matchroot)), uirelroot)
116 )
116 )
117
117
118 relrootmatch = scmutil.match(ctx2, pats=[relroot], default=b'path')
118 relrootmatch = scmutil.match(ctx2, pats=[relroot], default=b'path')
119 match = matchmod.intersectmatchers(match, relrootmatch)
119 match = matchmod.intersectmatchers(match, relrootmatch)
120 copysourcematch = relrootmatch
120 copysourcematch = relrootmatch
121
121
122 checkroot = repo.ui.configbool(
122 checkroot = repo.ui.configbool(
123 b'devel', b'all-warnings'
123 b'devel', b'all-warnings'
124 ) or repo.ui.configbool(b'devel', b'check-relroot')
124 ) or repo.ui.configbool(b'devel', b'check-relroot')
125
125
126 def relrootpathfn(f):
126 def relrootpathfn(f):
127 if checkroot and not f.startswith(relroot):
127 if checkroot and not f.startswith(relroot):
128 raise AssertionError(
128 raise AssertionError(
129 b"file %s doesn't start with relroot %s" % (f, relroot)
129 b"file %s doesn't start with relroot %s" % (f, relroot)
130 )
130 )
131 return f[len(relroot) :]
131 return f[len(relroot) :]
132
132
133 pathfn = compose(relrootpathfn, pathfn)
133 pathfn = compose(relrootpathfn, pathfn)
134
134
135 if stat:
135 if stat:
136 diffopts = diffopts.copy(context=0, noprefix=False)
136 diffopts = diffopts.copy(context=0, noprefix=False)
137 width = 80
137 width = 80
138 if not ui.plain():
138 if not ui.plain():
139 width = ui.termwidth() - graphwidth
139 width = ui.termwidth() - graphwidth
140 # If an explicit --root was given, don't respect ui.relative-paths
140 # If an explicit --root was given, don't respect ui.relative-paths
141 if not relroot:
141 if not relroot:
142 pathfn = compose(scmutil.getuipathfn(repo), pathfn)
142 pathfn = compose(scmutil.getuipathfn(repo), pathfn)
143
143
144 chunks = ctx2.diff(
144 chunks = ctx2.diff(
145 ctx1,
145 ctx1,
146 match,
146 match,
147 changes,
147 changes,
148 opts=diffopts,
148 opts=diffopts,
149 pathfn=pathfn,
149 pathfn=pathfn,
150 copysourcematch=copysourcematch,
150 copysourcematch=copysourcematch,
151 hunksfilterfn=hunksfilterfn,
151 hunksfilterfn=hunksfilterfn,
152 )
152 )
153
153
154 if fp is not None or ui.canwritewithoutlabels():
154 if fp is not None or ui.canwritewithoutlabels():
155 out = fp or ui
155 out = fp or ui
156 if stat:
156 if stat:
157 chunks = [patch.diffstat(util.iterlines(chunks), width=width)]
157 chunks = [patch.diffstat(util.iterlines(chunks), width=width)]
158 for chunk in util.filechunkiter(util.chunkbuffer(chunks)):
158 for chunk in util.filechunkiter(util.chunkbuffer(chunks)):
159 out.write(chunk)
159 out.write(chunk)
160 else:
160 else:
161 if stat:
161 if stat:
162 chunks = patch.diffstatui(util.iterlines(chunks), width=width)
162 chunks = patch.diffstatui(util.iterlines(chunks), width=width)
163 else:
163 else:
164 chunks = patch.difflabel(
164 chunks = patch.difflabel(
165 lambda chunks, **kwargs: chunks, chunks, opts=diffopts
165 lambda chunks, **kwargs: chunks, chunks, opts=diffopts
166 )
166 )
167 if ui.canbatchlabeledwrites():
167 if ui.canbatchlabeledwrites():
168
168
169 def gen():
169 def gen():
170 for chunk, label in chunks:
170 for chunk, label in chunks:
171 yield ui.label(chunk, label=label)
171 yield ui.label(chunk, label=label)
172
172
173 for chunk in util.filechunkiter(util.chunkbuffer(gen())):
173 for chunk in util.filechunkiter(util.chunkbuffer(gen())):
174 ui.write(chunk)
174 ui.write(chunk)
175 else:
175 else:
176 for chunk, label in chunks:
176 for chunk, label in chunks:
177 ui.write(chunk, label=label)
177 ui.write(chunk, label=label)
178
178
179 node2 = ctx2.node()
179 node2 = ctx2.node()
180 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
180 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
181 tempnode2 = node2
181 tempnode2 = node2
182 try:
182 try:
183 if node2 is not None:
183 if node2 is not None:
184 tempnode2 = ctx2.substate[subpath][1]
184 tempnode2 = ctx2.substate[subpath][1]
185 except KeyError:
185 except KeyError:
186 # A subrepo that existed in node1 was deleted between node1 and
186 # A subrepo that existed in node1 was deleted between node1 and
187 # node2 (inclusive). Thus, ctx2's substate won't contain that
187 # node2 (inclusive). Thus, ctx2's substate won't contain that
188 # subpath. The best we can do is to ignore it.
188 # subpath. The best we can do is to ignore it.
189 tempnode2 = None
189 tempnode2 = None
190 submatch = matchmod.subdirmatcher(subpath, match)
190 submatch = matchmod.subdirmatcher(subpath, match)
191 subprefix = repo.wvfs.reljoin(prefix, subpath)
191 subprefix = repo.wvfs.reljoin(prefix, subpath)
192 if listsubrepos or match.exact(subpath) or any(submatch.files()):
192 if listsubrepos or match.exact(subpath) or any(submatch.files()):
193 sub.diff(
193 sub.diff(
194 ui,
194 ui,
195 diffopts,
195 diffopts,
196 tempnode2,
196 tempnode2,
197 submatch,
197 submatch,
198 changes=changes,
198 changes=changes,
199 stat=stat,
199 stat=stat,
200 fp=fp,
200 fp=fp,
201 prefix=subprefix,
201 prefix=subprefix,
202 )
202 )
203
203
204
204
205 class changesetdiffer(object):
205 class changesetdiffer(object):
206 """Generate diff of changeset with pre-configured filtering functions"""
206 """Generate diff of changeset with pre-configured filtering functions"""
207
207
208 def _makefilematcher(self, ctx):
208 def _makefilematcher(self, ctx):
209 return scmutil.matchall(ctx.repo())
209 return scmutil.matchall(ctx.repo())
210
210
211 def _makehunksfilter(self, ctx):
211 def _makehunksfilter(self, ctx):
212 return None
212 return None
213
213
214 def showdiff(self, ui, ctx, diffopts, graphwidth=0, stat=False):
214 def showdiff(self, ui, ctx, diffopts, graphwidth=0, stat=False):
215 diffordiffstat(
215 diffordiffstat(
216 ui,
216 ui,
217 ctx.repo(),
217 ctx.repo(),
218 diffopts,
218 diffopts,
219 ctx.p1(),
219 ctx.p1(),
220 ctx,
220 ctx,
221 match=self._makefilematcher(ctx),
221 match=self._makefilematcher(ctx),
222 stat=stat,
222 stat=stat,
223 graphwidth=graphwidth,
223 graphwidth=graphwidth,
224 hunksfilterfn=self._makehunksfilter(ctx),
224 hunksfilterfn=self._makehunksfilter(ctx),
225 )
225 )
226
226
227
227
228 def changesetlabels(ctx):
228 def changesetlabels(ctx):
229 labels = [b'log.changeset', b'changeset.%s' % ctx.phasestr()]
229 labels = [b'log.changeset', b'changeset.%s' % ctx.phasestr()]
230 if ctx.obsolete():
230 if ctx.obsolete():
231 labels.append(b'changeset.obsolete')
231 labels.append(b'changeset.obsolete')
232 if ctx.isunstable():
232 if ctx.isunstable():
233 labels.append(b'changeset.unstable')
233 labels.append(b'changeset.unstable')
234 for instability in ctx.instabilities():
234 for instability in ctx.instabilities():
235 labels.append(b'instability.%s' % instability)
235 labels.append(b'instability.%s' % instability)
236 return b' '.join(labels)
236 return b' '.join(labels)
237
237
238
238
239 class changesetprinter(object):
239 class changesetprinter(object):
240 '''show changeset information when templating not requested.'''
240 '''show changeset information when templating not requested.'''
241
241
242 def __init__(self, ui, repo, differ=None, diffopts=None, buffered=False):
242 def __init__(self, ui, repo, differ=None, diffopts=None, buffered=False):
243 self.ui = ui
243 self.ui = ui
244 self.repo = repo
244 self.repo = repo
245 self.buffered = buffered
245 self.buffered = buffered
246 self._differ = differ or changesetdiffer()
246 self._differ = differ or changesetdiffer()
247 self._diffopts = patch.diffallopts(ui, diffopts)
247 self._diffopts = patch.diffallopts(ui, diffopts)
248 self._includestat = diffopts and diffopts.get(b'stat')
248 self._includestat = diffopts and diffopts.get(b'stat')
249 self._includediff = diffopts and diffopts.get(b'patch')
249 self._includediff = diffopts and diffopts.get(b'patch')
250 self.header = {}
250 self.header = {}
251 self.hunk = {}
251 self.hunk = {}
252 self.lastheader = None
252 self.lastheader = None
253 self.footer = None
253 self.footer = None
254 self._columns = templatekw.getlogcolumns()
254 self._columns = templatekw.getlogcolumns()
255
255
256 def flush(self, ctx):
256 def flush(self, ctx):
257 rev = ctx.rev()
257 rev = ctx.rev()
258 if rev in self.header:
258 if rev in self.header:
259 h = self.header[rev]
259 h = self.header[rev]
260 if h != self.lastheader:
260 if h != self.lastheader:
261 self.lastheader = h
261 self.lastheader = h
262 self.ui.write(h)
262 self.ui.write(h)
263 del self.header[rev]
263 del self.header[rev]
264 if rev in self.hunk:
264 if rev in self.hunk:
265 self.ui.write(self.hunk[rev])
265 self.ui.write(self.hunk[rev])
266 del self.hunk[rev]
266 del self.hunk[rev]
267
267
268 def close(self):
268 def close(self):
269 if self.footer:
269 if self.footer:
270 self.ui.write(self.footer)
270 self.ui.write(self.footer)
271
271
272 def show(self, ctx, copies=None, **props):
272 def show(self, ctx, copies=None, **props):
273 props = pycompat.byteskwargs(props)
273 props = pycompat.byteskwargs(props)
274 if self.buffered:
274 if self.buffered:
275 self.ui.pushbuffer(labeled=True)
275 self.ui.pushbuffer(labeled=True)
276 self._show(ctx, copies, props)
276 self._show(ctx, copies, props)
277 self.hunk[ctx.rev()] = self.ui.popbuffer()
277 self.hunk[ctx.rev()] = self.ui.popbuffer()
278 else:
278 else:
279 self._show(ctx, copies, props)
279 self._show(ctx, copies, props)
280
280
281 def _show(self, ctx, copies, props):
281 def _show(self, ctx, copies, props):
282 '''show a single changeset or file revision'''
282 '''show a single changeset or file revision'''
283 changenode = ctx.node()
283 changenode = ctx.node()
284 graphwidth = props.get(b'graphwidth', 0)
284 graphwidth = props.get(b'graphwidth', 0)
285
285
286 if self.ui.quiet:
286 if self.ui.quiet:
287 self.ui.write(
287 self.ui.write(
288 b"%s\n" % scmutil.formatchangeid(ctx), label=b'log.node'
288 b"%s\n" % scmutil.formatchangeid(ctx), label=b'log.node'
289 )
289 )
290 return
290 return
291
291
292 columns = self._columns
292 columns = self._columns
293 self.ui.write(
293 self.ui.write(
294 columns[b'changeset'] % scmutil.formatchangeid(ctx),
294 columns[b'changeset'] % scmutil.formatchangeid(ctx),
295 label=changesetlabels(ctx),
295 label=changesetlabels(ctx),
296 )
296 )
297
297
298 # branches are shown first before any other names due to backwards
298 # branches are shown first before any other names due to backwards
299 # compatibility
299 # compatibility
300 branch = ctx.branch()
300 branch = ctx.branch()
301 # don't show the default branch name
301 # don't show the default branch name
302 if branch != b'default':
302 if branch != b'default':
303 self.ui.write(columns[b'branch'] % branch, label=b'log.branch')
303 self.ui.write(columns[b'branch'] % branch, label=b'log.branch')
304
304
305 for nsname, ns in pycompat.iteritems(self.repo.names):
305 for nsname, ns in pycompat.iteritems(self.repo.names):
306 # branches has special logic already handled above, so here we just
306 # branches has special logic already handled above, so here we just
307 # skip it
307 # skip it
308 if nsname == b'branches':
308 if nsname == b'branches':
309 continue
309 continue
310 # we will use the templatename as the color name since those two
310 # we will use the templatename as the color name since those two
311 # should be the same
311 # should be the same
312 for name in ns.names(self.repo, changenode):
312 for name in ns.names(self.repo, changenode):
313 self.ui.write(ns.logfmt % name, label=b'log.%s' % ns.colorname)
313 self.ui.write(ns.logfmt % name, label=b'log.%s' % ns.colorname)
314 if self.ui.debugflag:
314 if self.ui.debugflag:
315 self.ui.write(
315 self.ui.write(
316 columns[b'phase'] % ctx.phasestr(), label=b'log.phase'
316 columns[b'phase'] % ctx.phasestr(), label=b'log.phase'
317 )
317 )
318 for pctx in scmutil.meaningfulparents(self.repo, ctx):
318 for pctx in scmutil.meaningfulparents(self.repo, ctx):
319 label = b'log.parent changeset.%s' % pctx.phasestr()
319 label = b'log.parent changeset.%s' % pctx.phasestr()
320 self.ui.write(
320 self.ui.write(
321 columns[b'parent'] % scmutil.formatchangeid(pctx), label=label
321 columns[b'parent'] % scmutil.formatchangeid(pctx), label=label
322 )
322 )
323
323
324 if self.ui.debugflag:
324 if self.ui.debugflag:
325 mnode = ctx.manifestnode()
325 mnode = ctx.manifestnode()
326 if mnode is None:
326 if mnode is None:
327 mnode = wdirid
327 mnode = wdirid
328 mrev = wdirrev
328 mrev = wdirrev
329 else:
329 else:
330 mrev = self.repo.manifestlog.rev(mnode)
330 mrev = self.repo.manifestlog.rev(mnode)
331 self.ui.write(
331 self.ui.write(
332 columns[b'manifest']
332 columns[b'manifest']
333 % scmutil.formatrevnode(self.ui, mrev, mnode),
333 % scmutil.formatrevnode(self.ui, mrev, mnode),
334 label=b'ui.debug log.manifest',
334 label=b'ui.debug log.manifest',
335 )
335 )
336 self.ui.write(columns[b'user'] % ctx.user(), label=b'log.user')
336 self.ui.write(columns[b'user'] % ctx.user(), label=b'log.user')
337 self.ui.write(
337 self.ui.write(
338 columns[b'date'] % dateutil.datestr(ctx.date()), label=b'log.date'
338 columns[b'date'] % dateutil.datestr(ctx.date()), label=b'log.date'
339 )
339 )
340
340
341 if ctx.isunstable():
341 if ctx.isunstable():
342 instabilities = ctx.instabilities()
342 instabilities = ctx.instabilities()
343 self.ui.write(
343 self.ui.write(
344 columns[b'instability'] % b', '.join(instabilities),
344 columns[b'instability'] % b', '.join(instabilities),
345 label=b'log.instability',
345 label=b'log.instability',
346 )
346 )
347
347
348 elif ctx.obsolete():
348 elif ctx.obsolete():
349 self._showobsfate(ctx)
349 self._showobsfate(ctx)
350
350
351 self._exthook(ctx)
351 self._exthook(ctx)
352
352
353 if self.ui.debugflag:
353 if self.ui.debugflag:
354 files = ctx.p1().status(ctx)
354 files = ctx.p1().status(ctx)
355 for key, value in zip(
355 for key, value in zip(
356 [b'files', b'files+', b'files-'],
356 [b'files', b'files+', b'files-'],
357 [files.modified, files.added, files.removed],
357 [files.modified, files.added, files.removed],
358 ):
358 ):
359 if value:
359 if value:
360 self.ui.write(
360 self.ui.write(
361 columns[key] % b" ".join(value),
361 columns[key] % b" ".join(value),
362 label=b'ui.debug log.files',
362 label=b'ui.debug log.files',
363 )
363 )
364 elif ctx.files() and self.ui.verbose:
364 elif ctx.files() and self.ui.verbose:
365 self.ui.write(
365 self.ui.write(
366 columns[b'files'] % b" ".join(ctx.files()),
366 columns[b'files'] % b" ".join(ctx.files()),
367 label=b'ui.note log.files',
367 label=b'ui.note log.files',
368 )
368 )
369 if copies and self.ui.verbose:
369 if copies and self.ui.verbose:
370 copies = [b'%s (%s)' % c for c in copies]
370 copies = [b'%s (%s)' % c for c in copies]
371 self.ui.write(
371 self.ui.write(
372 columns[b'copies'] % b' '.join(copies),
372 columns[b'copies'] % b' '.join(copies),
373 label=b'ui.note log.copies',
373 label=b'ui.note log.copies',
374 )
374 )
375
375
376 extra = ctx.extra()
376 extra = ctx.extra()
377 if extra and self.ui.debugflag:
377 if extra and self.ui.debugflag:
378 for key, value in sorted(extra.items()):
378 for key, value in sorted(extra.items()):
379 self.ui.write(
379 self.ui.write(
380 columns[b'extra'] % (key, stringutil.escapestr(value)),
380 columns[b'extra'] % (key, stringutil.escapestr(value)),
381 label=b'ui.debug log.extra',
381 label=b'ui.debug log.extra',
382 )
382 )
383
383
384 description = ctx.description().strip()
384 description = ctx.description().strip()
385 if description:
385 if description:
386 if self.ui.verbose:
386 if self.ui.verbose:
387 self.ui.write(
387 self.ui.write(
388 _(b"description:\n"), label=b'ui.note log.description'
388 _(b"description:\n"), label=b'ui.note log.description'
389 )
389 )
390 self.ui.write(description, label=b'ui.note log.description')
390 self.ui.write(description, label=b'ui.note log.description')
391 self.ui.write(b"\n\n")
391 self.ui.write(b"\n\n")
392 else:
392 else:
393 self.ui.write(
393 self.ui.write(
394 columns[b'summary'] % description.splitlines()[0],
394 columns[b'summary'] % description.splitlines()[0],
395 label=b'log.summary',
395 label=b'log.summary',
396 )
396 )
397 self.ui.write(b"\n")
397 self.ui.write(b"\n")
398
398
399 self._showpatch(ctx, graphwidth)
399 self._showpatch(ctx, graphwidth)
400
400
401 def _showobsfate(self, ctx):
401 def _showobsfate(self, ctx):
402 # TODO: do not depend on templater
402 # TODO: do not depend on templater
403 tres = formatter.templateresources(self.repo.ui, self.repo)
403 tres = formatter.templateresources(self.repo.ui, self.repo)
404 t = formatter.maketemplater(
404 t = formatter.maketemplater(
405 self.repo.ui,
405 self.repo.ui,
406 b'{join(obsfate, "\n")}',
406 b'{join(obsfate, "\n")}',
407 defaults=templatekw.keywords,
407 defaults=templatekw.keywords,
408 resources=tres,
408 resources=tres,
409 )
409 )
410 obsfate = t.renderdefault({b'ctx': ctx}).splitlines()
410 obsfate = t.renderdefault({b'ctx': ctx}).splitlines()
411
411
412 if obsfate:
412 if obsfate:
413 for obsfateline in obsfate:
413 for obsfateline in obsfate:
414 self.ui.write(
414 self.ui.write(
415 self._columns[b'obsolete'] % obsfateline,
415 self._columns[b'obsolete'] % obsfateline,
416 label=b'log.obsfate',
416 label=b'log.obsfate',
417 )
417 )
418
418
419 def _exthook(self, ctx):
419 def _exthook(self, ctx):
420 '''empty method used by extension as a hook point
420 '''empty method used by extension as a hook point
421 '''
421 '''
422
422
423 def _showpatch(self, ctx, graphwidth=0):
423 def _showpatch(self, ctx, graphwidth=0):
424 if self._includestat:
424 if self._includestat:
425 self._differ.showdiff(
425 self._differ.showdiff(
426 self.ui, ctx, self._diffopts, graphwidth, stat=True
426 self.ui, ctx, self._diffopts, graphwidth, stat=True
427 )
427 )
428 if self._includestat and self._includediff:
428 if self._includestat and self._includediff:
429 self.ui.write(b"\n")
429 self.ui.write(b"\n")
430 if self._includediff:
430 if self._includediff:
431 self._differ.showdiff(
431 self._differ.showdiff(
432 self.ui, ctx, self._diffopts, graphwidth, stat=False
432 self.ui, ctx, self._diffopts, graphwidth, stat=False
433 )
433 )
434 if self._includestat or self._includediff:
434 if self._includestat or self._includediff:
435 self.ui.write(b"\n")
435 self.ui.write(b"\n")
436
436
437
437
438 class changesetformatter(changesetprinter):
438 class changesetformatter(changesetprinter):
439 """Format changeset information by generic formatter"""
439 """Format changeset information by generic formatter"""
440
440
441 def __init__(
441 def __init__(
442 self, ui, repo, fm, differ=None, diffopts=None, buffered=False
442 self, ui, repo, fm, differ=None, diffopts=None, buffered=False
443 ):
443 ):
444 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
444 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
445 self._diffopts = patch.difffeatureopts(ui, diffopts, git=True)
445 self._diffopts = patch.difffeatureopts(ui, diffopts, git=True)
446 self._fm = fm
446 self._fm = fm
447
447
448 def close(self):
448 def close(self):
449 self._fm.end()
449 self._fm.end()
450
450
451 def _show(self, ctx, copies, props):
451 def _show(self, ctx, copies, props):
452 '''show a single changeset or file revision'''
452 '''show a single changeset or file revision'''
453 fm = self._fm
453 fm = self._fm
454 fm.startitem()
454 fm.startitem()
455 fm.context(ctx=ctx)
455 fm.context(ctx=ctx)
456 fm.data(rev=scmutil.intrev(ctx), node=fm.hexfunc(scmutil.binnode(ctx)))
456 fm.data(rev=scmutil.intrev(ctx), node=fm.hexfunc(scmutil.binnode(ctx)))
457
457
458 datahint = fm.datahint()
458 datahint = fm.datahint()
459 if self.ui.quiet and not datahint:
459 if self.ui.quiet and not datahint:
460 return
460 return
461
461
462 fm.data(
462 fm.data(
463 branch=ctx.branch(),
463 branch=ctx.branch(),
464 phase=ctx.phasestr(),
464 phase=ctx.phasestr(),
465 user=ctx.user(),
465 user=ctx.user(),
466 date=fm.formatdate(ctx.date()),
466 date=fm.formatdate(ctx.date()),
467 desc=ctx.description(),
467 desc=ctx.description(),
468 bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'),
468 bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'),
469 tags=fm.formatlist(ctx.tags(), name=b'tag'),
469 tags=fm.formatlist(ctx.tags(), name=b'tag'),
470 parents=fm.formatlist(
470 parents=fm.formatlist(
471 [fm.hexfunc(c.node()) for c in ctx.parents()], name=b'node'
471 [fm.hexfunc(c.node()) for c in ctx.parents()], name=b'node'
472 ),
472 ),
473 )
473 )
474
474
475 if self.ui.debugflag or b'manifest' in datahint:
475 if self.ui.debugflag or b'manifest' in datahint:
476 fm.data(manifest=fm.hexfunc(ctx.manifestnode() or wdirid))
476 fm.data(manifest=fm.hexfunc(ctx.manifestnode() or wdirid))
477 if self.ui.debugflag or b'extra' in datahint:
477 if self.ui.debugflag or b'extra' in datahint:
478 fm.data(extra=fm.formatdict(ctx.extra()))
478 fm.data(extra=fm.formatdict(ctx.extra()))
479
479
480 if (
480 if (
481 self.ui.debugflag
481 self.ui.debugflag
482 or b'modified' in datahint
482 or b'modified' in datahint
483 or b'added' in datahint
483 or b'added' in datahint
484 or b'removed' in datahint
484 or b'removed' in datahint
485 ):
485 ):
486 files = ctx.p1().status(ctx)
486 files = ctx.p1().status(ctx)
487 fm.data(
487 fm.data(
488 modified=fm.formatlist(files.modified, name=b'file'),
488 modified=fm.formatlist(files.modified, name=b'file'),
489 added=fm.formatlist(files.added, name=b'file'),
489 added=fm.formatlist(files.added, name=b'file'),
490 removed=fm.formatlist(files.removed, name=b'file'),
490 removed=fm.formatlist(files.removed, name=b'file'),
491 )
491 )
492
492
493 verbose = not self.ui.debugflag and self.ui.verbose
493 verbose = not self.ui.debugflag and self.ui.verbose
494 if verbose or b'files' in datahint:
494 if verbose or b'files' in datahint:
495 fm.data(files=fm.formatlist(ctx.files(), name=b'file'))
495 fm.data(files=fm.formatlist(ctx.files(), name=b'file'))
496 if verbose and copies or b'copies' in datahint:
496 if verbose and copies or b'copies' in datahint:
497 fm.data(
497 fm.data(
498 copies=fm.formatdict(copies or {}, key=b'name', value=b'source')
498 copies=fm.formatdict(copies or {}, key=b'name', value=b'source')
499 )
499 )
500
500
501 if self._includestat or b'diffstat' in datahint:
501 if self._includestat or b'diffstat' in datahint:
502 self.ui.pushbuffer()
502 self.ui.pushbuffer()
503 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=True)
503 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=True)
504 fm.data(diffstat=self.ui.popbuffer())
504 fm.data(diffstat=self.ui.popbuffer())
505 if self._includediff or b'diff' in datahint:
505 if self._includediff or b'diff' in datahint:
506 self.ui.pushbuffer()
506 self.ui.pushbuffer()
507 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=False)
507 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=False)
508 fm.data(diff=self.ui.popbuffer())
508 fm.data(diff=self.ui.popbuffer())
509
509
510
510
511 class changesettemplater(changesetprinter):
511 class changesettemplater(changesetprinter):
512 '''format changeset information.
512 '''format changeset information.
513
513
514 Note: there are a variety of convenience functions to build a
514 Note: there are a variety of convenience functions to build a
515 changesettemplater for common cases. See functions such as:
515 changesettemplater for common cases. See functions such as:
516 maketemplater, changesetdisplayer, buildcommittemplate, or other
516 maketemplater, changesetdisplayer, buildcommittemplate, or other
517 functions that use changesest_templater.
517 functions that use changesest_templater.
518 '''
518 '''
519
519
520 # Arguments before "buffered" used to be positional. Consider not
520 # Arguments before "buffered" used to be positional. Consider not
521 # adding/removing arguments before "buffered" to not break callers.
521 # adding/removing arguments before "buffered" to not break callers.
522 def __init__(
522 def __init__(
523 self, ui, repo, tmplspec, differ=None, diffopts=None, buffered=False
523 self, ui, repo, tmplspec, differ=None, diffopts=None, buffered=False
524 ):
524 ):
525 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
525 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
526 # tres is shared with _graphnodeformatter()
526 # tres is shared with _graphnodeformatter()
527 self._tresources = tres = formatter.templateresources(ui, repo)
527 self._tresources = tres = formatter.templateresources(ui, repo)
528 self.t = formatter.loadtemplater(
528 self.t = formatter.loadtemplater(
529 ui,
529 ui,
530 tmplspec,
530 tmplspec,
531 defaults=templatekw.keywords,
531 defaults=templatekw.keywords,
532 resources=tres,
532 resources=tres,
533 cache=templatekw.defaulttempl,
533 cache=templatekw.defaulttempl,
534 )
534 )
535 self._counter = itertools.count()
535 self._counter = itertools.count()
536
536
537 self._tref = tmplspec.ref
537 self._tref = tmplspec.ref
538 self._parts = {
538 self._parts = {
539 b'header': b'',
539 b'header': b'',
540 b'footer': b'',
540 b'footer': b'',
541 tmplspec.ref: tmplspec.ref,
541 tmplspec.ref: tmplspec.ref,
542 b'docheader': b'',
542 b'docheader': b'',
543 b'docfooter': b'',
543 b'docfooter': b'',
544 b'separator': b'',
544 b'separator': b'',
545 }
545 }
546 if tmplspec.mapfile:
546 if tmplspec.mapfile:
547 # find correct templates for current mode, for backward
547 # find correct templates for current mode, for backward
548 # compatibility with 'log -v/-q/--debug' using a mapfile
548 # compatibility with 'log -v/-q/--debug' using a mapfile
549 tmplmodes = [
549 tmplmodes = [
550 (True, b''),
550 (True, b''),
551 (self.ui.verbose, b'_verbose'),
551 (self.ui.verbose, b'_verbose'),
552 (self.ui.quiet, b'_quiet'),
552 (self.ui.quiet, b'_quiet'),
553 (self.ui.debugflag, b'_debug'),
553 (self.ui.debugflag, b'_debug'),
554 ]
554 ]
555 for mode, postfix in tmplmodes:
555 for mode, postfix in tmplmodes:
556 for t in self._parts:
556 for t in self._parts:
557 cur = t + postfix
557 cur = t + postfix
558 if mode and cur in self.t:
558 if mode and cur in self.t:
559 self._parts[t] = cur
559 self._parts[t] = cur
560 else:
560 else:
561 partnames = [p for p in self._parts.keys() if p != tmplspec.ref]
561 partnames = [p for p in self._parts.keys() if p != tmplspec.ref]
562 m = formatter.templatepartsmap(tmplspec, self.t, partnames)
562 m = formatter.templatepartsmap(tmplspec, self.t, partnames)
563 self._parts.update(m)
563 self._parts.update(m)
564
564
565 if self._parts[b'docheader']:
565 if self._parts[b'docheader']:
566 self.ui.write(self.t.render(self._parts[b'docheader'], {}))
566 self.ui.write(self.t.render(self._parts[b'docheader'], {}))
567
567
568 def close(self):
568 def close(self):
569 if self._parts[b'docfooter']:
569 if self._parts[b'docfooter']:
570 if not self.footer:
570 if not self.footer:
571 self.footer = b""
571 self.footer = b""
572 self.footer += self.t.render(self._parts[b'docfooter'], {})
572 self.footer += self.t.render(self._parts[b'docfooter'], {})
573 return super(changesettemplater, self).close()
573 return super(changesettemplater, self).close()
574
574
575 def _show(self, ctx, copies, props):
575 def _show(self, ctx, copies, props):
576 '''show a single changeset or file revision'''
576 '''show a single changeset or file revision'''
577 props = props.copy()
577 props = props.copy()
578 props[b'ctx'] = ctx
578 props[b'ctx'] = ctx
579 props[b'index'] = index = next(self._counter)
579 props[b'index'] = index = next(self._counter)
580 props[b'revcache'] = {b'copies': copies}
580 props[b'revcache'] = {b'copies': copies}
581 graphwidth = props.get(b'graphwidth', 0)
581 graphwidth = props.get(b'graphwidth', 0)
582
582
583 # write separator, which wouldn't work well with the header part below
583 # write separator, which wouldn't work well with the header part below
584 # since there's inherently a conflict between header (across items) and
584 # since there's inherently a conflict between header (across items) and
585 # separator (per item)
585 # separator (per item)
586 if self._parts[b'separator'] and index > 0:
586 if self._parts[b'separator'] and index > 0:
587 self.ui.write(self.t.render(self._parts[b'separator'], {}))
587 self.ui.write(self.t.render(self._parts[b'separator'], {}))
588
588
589 # write header
589 # write header
590 if self._parts[b'header']:
590 if self._parts[b'header']:
591 h = self.t.render(self._parts[b'header'], props)
591 h = self.t.render(self._parts[b'header'], props)
592 if self.buffered:
592 if self.buffered:
593 self.header[ctx.rev()] = h
593 self.header[ctx.rev()] = h
594 else:
594 else:
595 if self.lastheader != h:
595 if self.lastheader != h:
596 self.lastheader = h
596 self.lastheader = h
597 self.ui.write(h)
597 self.ui.write(h)
598
598
599 # write changeset metadata, then patch if requested
599 # write changeset metadata, then patch if requested
600 key = self._parts[self._tref]
600 key = self._parts[self._tref]
601 self.ui.write(self.t.render(key, props))
601 self.ui.write(self.t.render(key, props))
602 self._exthook(ctx)
602 self._exthook(ctx)
603 self._showpatch(ctx, graphwidth)
603 self._showpatch(ctx, graphwidth)
604
604
605 if self._parts[b'footer']:
605 if self._parts[b'footer']:
606 if not self.footer:
606 if not self.footer:
607 self.footer = self.t.render(self._parts[b'footer'], props)
607 self.footer = self.t.render(self._parts[b'footer'], props)
608
608
609
609
610 def templatespec(tmpl, mapfile):
610 def templatespec(tmpl, mapfile):
611 assert not (tmpl and mapfile)
611 assert not (tmpl and mapfile)
612 if mapfile:
612 if mapfile:
613 return formatter.mapfile_templatespec(b'changeset', mapfile)
613 return formatter.mapfile_templatespec(b'changeset', mapfile)
614 else:
614 else:
615 return formatter.literal_templatespec(tmpl)
615 return formatter.literal_templatespec(tmpl)
616
616
617
617
618 def _lookuptemplate(ui, tmpl, style):
618 def _lookuptemplate(ui, tmpl, style):
619 """Find the template matching the given template spec or style
619 """Find the template matching the given template spec or style
620
620
621 See formatter.lookuptemplate() for details.
621 See formatter.lookuptemplate() for details.
622 """
622 """
623
623
624 # ui settings
624 # ui settings
625 if not tmpl and not style: # template are stronger than style
625 if not tmpl and not style: # template are stronger than style
626 tmpl = ui.config(b'command-templates', b'log')
626 tmpl = ui.config(b'command-templates', b'log')
627 if tmpl:
627 if tmpl:
628 return formatter.literal_templatespec(templater.unquotestring(tmpl))
628 return formatter.literal_templatespec(templater.unquotestring(tmpl))
629 else:
629 else:
630 style = util.expandpath(ui.config(b'ui', b'style'))
630 style = util.expandpath(ui.config(b'ui', b'style'))
631
631
632 if not tmpl and style:
632 if not tmpl and style:
633 mapfile = style
633 mapfile = style
634 fp = None
634 fp = None
635 if not os.path.split(mapfile)[0]:
635 if not os.path.split(mapfile)[0]:
636 (mapname, fp) = templater.try_open_template(
636 (mapname, fp) = templater.try_open_template(
637 b'map-cmdline.' + mapfile
637 b'map-cmdline.' + mapfile
638 ) or templater.try_open_template(mapfile)
638 ) or templater.try_open_template(mapfile)
639 if mapname:
639 if mapname:
640 mapfile = mapname
640 mapfile = mapname
641 return formatter.mapfile_templatespec(b'changeset', mapfile, fp)
641 return formatter.mapfile_templatespec(b'changeset', mapfile, fp)
642
642
643 return formatter.lookuptemplate(ui, b'changeset', tmpl)
643 return formatter.lookuptemplate(ui, b'changeset', tmpl)
644
644
645
645
646 def maketemplater(ui, repo, tmpl, buffered=False):
646 def maketemplater(ui, repo, tmpl, buffered=False):
647 """Create a changesettemplater from a literal template 'tmpl'
647 """Create a changesettemplater from a literal template 'tmpl'
648 byte-string."""
648 byte-string."""
649 spec = formatter.literal_templatespec(tmpl)
649 spec = formatter.literal_templatespec(tmpl)
650 return changesettemplater(ui, repo, spec, buffered=buffered)
650 return changesettemplater(ui, repo, spec, buffered=buffered)
651
651
652
652
653 def changesetdisplayer(ui, repo, opts, differ=None, buffered=False):
653 def changesetdisplayer(ui, repo, opts, differ=None, buffered=False):
654 """show one changeset using template or regular display.
654 """show one changeset using template or regular display.
655
655
656 Display format will be the first non-empty hit of:
656 Display format will be the first non-empty hit of:
657 1. option 'template'
657 1. option 'template'
658 2. option 'style'
658 2. option 'style'
659 3. [command-templates] setting 'log'
659 3. [command-templates] setting 'log'
660 4. [ui] setting 'style'
660 4. [ui] setting 'style'
661 If all of these values are either the unset or the empty string,
661 If all of these values are either the unset or the empty string,
662 regular display via changesetprinter() is done.
662 regular display via changesetprinter() is done.
663 """
663 """
664 postargs = (differ, opts, buffered)
664 postargs = (differ, opts, buffered)
665 spec = _lookuptemplate(ui, opts.get(b'template'), opts.get(b'style'))
665 spec = _lookuptemplate(ui, opts.get(b'template'), opts.get(b'style'))
666
666
667 # machine-readable formats have slightly different keyword set than
667 # machine-readable formats have slightly different keyword set than
668 # plain templates, which are handled by changesetformatter.
668 # plain templates, which are handled by changesetformatter.
669 # note that {b'pickle', b'debug'} can also be added to the list if needed.
669 # note that {b'pickle', b'debug'} can also be added to the list if needed.
670 if spec.ref in {b'cbor', b'json'}:
670 if spec.ref in {b'cbor', b'json'}:
671 fm = ui.formatter(b'log', opts)
671 fm = ui.formatter(b'log', opts)
672 return changesetformatter(ui, repo, fm, *postargs)
672 return changesetformatter(ui, repo, fm, *postargs)
673
673
674 if not spec.ref and not spec.tmpl and not spec.mapfile:
674 if not spec.ref and not spec.tmpl and not spec.mapfile:
675 return changesetprinter(ui, repo, *postargs)
675 return changesetprinter(ui, repo, *postargs)
676
676
677 return changesettemplater(ui, repo, spec, *postargs)
677 return changesettemplater(ui, repo, spec, *postargs)
678
678
679
679
680 @attr.s
680 @attr.s
681 class walkopts(object):
681 class walkopts(object):
682 """Options to configure a set of revisions and file matcher factory
682 """Options to configure a set of revisions and file matcher factory
683 to scan revision/file history
683 to scan revision/file history
684 """
684 """
685
685
686 # raw command-line parameters, which a matcher will be built from
686 # raw command-line parameters, which a matcher will be built from
687 pats = attr.ib() # type: List[bytes]
687 pats = attr.ib() # type: List[bytes]
688 opts = attr.ib() # type: Dict[bytes, Any]
688 opts = attr.ib() # type: Dict[bytes, Any]
689
689
690 # a list of revset expressions to be traversed; if follow, it specifies
690 # a list of revset expressions to be traversed; if follow, it specifies
691 # the start revisions
691 # the start revisions
692 revspec = attr.ib() # type: List[bytes]
692 revspec = attr.ib() # type: List[bytes]
693
693
694 # miscellaneous queries to filter revisions (see "hg help log" for details)
694 # miscellaneous queries to filter revisions (see "hg help log" for details)
695 branches = attr.ib(default=attr.Factory(list)) # type: List[bytes]
695 branches = attr.ib(default=attr.Factory(list)) # type: List[bytes]
696 date = attr.ib(default=None) # type: Optional[bytes]
696 date = attr.ib(default=None) # type: Optional[bytes]
697 keywords = attr.ib(default=attr.Factory(list)) # type: List[bytes]
697 keywords = attr.ib(default=attr.Factory(list)) # type: List[bytes]
698 no_merges = attr.ib(default=False) # type: bool
698 no_merges = attr.ib(default=False) # type: bool
699 only_merges = attr.ib(default=False) # type: bool
699 only_merges = attr.ib(default=False) # type: bool
700 prune_ancestors = attr.ib(default=attr.Factory(list)) # type: List[bytes]
700 prune_ancestors = attr.ib(default=attr.Factory(list)) # type: List[bytes]
701 users = attr.ib(default=attr.Factory(list)) # type: List[bytes]
701 users = attr.ib(default=attr.Factory(list)) # type: List[bytes]
702
702
703 # miscellaneous matcher arguments
703 # miscellaneous matcher arguments
704 include_pats = attr.ib(default=attr.Factory(list)) # type: List[bytes]
704 include_pats = attr.ib(default=attr.Factory(list)) # type: List[bytes]
705 exclude_pats = attr.ib(default=attr.Factory(list)) # type: List[bytes]
705 exclude_pats = attr.ib(default=attr.Factory(list)) # type: List[bytes]
706
706
707 # 0: no follow, 1: follow first, 2: follow both parents
707 # 0: no follow, 1: follow first, 2: follow both parents
708 follow = attr.ib(default=0) # type: int
708 follow = attr.ib(default=0) # type: int
709
709
710 # do not attempt filelog-based traversal, which may be fast but cannot
710 # do not attempt filelog-based traversal, which may be fast but cannot
711 # include revisions where files were removed
711 # include revisions where files were removed
712 force_changelog_traversal = attr.ib(default=False) # type: bool
712 force_changelog_traversal = attr.ib(default=False) # type: bool
713
713
714 # filter revisions by file patterns, which should be disabled only if
714 # filter revisions by file patterns, which should be disabled only if
715 # you want to include revisions where files were unmodified
715 # you want to include revisions where files were unmodified
716 filter_revisions_by_pats = attr.ib(default=True) # type: bool
716 filter_revisions_by_pats = attr.ib(default=True) # type: bool
717
717
718 # sort revisions prior to traversal: 'desc', 'topo', or None
718 # sort revisions prior to traversal: 'desc', 'topo', or None
719 sort_revisions = attr.ib(default=None) # type: Optional[bytes]
719 sort_revisions = attr.ib(default=None) # type: Optional[bytes]
720
720
721 # limit number of changes displayed; None means unlimited
721 # limit number of changes displayed; None means unlimited
722 limit = attr.ib(default=None) # type: Optional[int]
722 limit = attr.ib(default=None) # type: Optional[int]
723
723
724
724
725 def parseopts(ui, pats, opts):
725 def parseopts(ui, pats, opts):
726 # type: (Any, List[bytes], Dict[bytes, Any]) -> walkopts
726 # type: (Any, List[bytes], Dict[bytes, Any]) -> walkopts
727 """Parse log command options into walkopts
727 """Parse log command options into walkopts
728
728
729 The returned walkopts will be passed in to getrevs() or makewalker().
729 The returned walkopts will be passed in to getrevs() or makewalker().
730 """
730 """
731 if opts.get(b'follow_first'):
731 if opts.get(b'follow_first'):
732 follow = 1
732 follow = 1
733 elif opts.get(b'follow'):
733 elif opts.get(b'follow'):
734 follow = 2
734 follow = 2
735 else:
735 else:
736 follow = 0
736 follow = 0
737
737
738 if opts.get(b'graph'):
738 if opts.get(b'graph'):
739 if ui.configbool(b'experimental', b'log.topo'):
739 if ui.configbool(b'experimental', b'log.topo'):
740 sort_revisions = b'topo'
740 sort_revisions = b'topo'
741 else:
741 else:
742 sort_revisions = b'desc'
742 sort_revisions = b'desc'
743 else:
743 else:
744 sort_revisions = None
744 sort_revisions = None
745
745
746 return walkopts(
746 return walkopts(
747 pats=pats,
747 pats=pats,
748 opts=opts,
748 opts=opts,
749 revspec=opts.get(b'rev', []),
749 revspec=opts.get(b'rev', []),
750 # branch and only_branch are really aliases and must be handled at
750 # branch and only_branch are really aliases and must be handled at
751 # the same time
751 # the same time
752 branches=opts.get(b'branch', []) + opts.get(b'only_branch', []),
752 branches=opts.get(b'branch', []) + opts.get(b'only_branch', []),
753 date=opts.get(b'date'),
753 date=opts.get(b'date'),
754 keywords=opts.get(b'keyword', []),
754 keywords=opts.get(b'keyword', []),
755 no_merges=bool(opts.get(b'no_merges')),
755 no_merges=bool(opts.get(b'no_merges')),
756 only_merges=bool(opts.get(b'only_merges')),
756 only_merges=bool(opts.get(b'only_merges')),
757 prune_ancestors=opts.get(b'prune', []),
757 prune_ancestors=opts.get(b'prune', []),
758 users=opts.get(b'user', []),
758 users=opts.get(b'user', []),
759 include_pats=opts.get(b'include', []),
759 include_pats=opts.get(b'include', []),
760 exclude_pats=opts.get(b'exclude', []),
760 exclude_pats=opts.get(b'exclude', []),
761 follow=follow,
761 follow=follow,
762 force_changelog_traversal=bool(opts.get(b'removed')),
762 force_changelog_traversal=bool(opts.get(b'removed')),
763 sort_revisions=sort_revisions,
763 sort_revisions=sort_revisions,
764 limit=getlimit(opts),
764 limit=getlimit(opts),
765 )
765 )
766
766
767
767
768 def _makematcher(repo, revs, wopts):
768 def _makematcher(repo, revs, wopts):
769 """Build matcher and expanded patterns from log options
769 """Build matcher and expanded patterns from log options
770
770
771 If --follow, revs are the revisions to follow from.
771 If --follow, revs are the revisions to follow from.
772
772
773 Returns (match, pats, slowpath) where
773 Returns (match, pats, slowpath) where
774 - match: a matcher built from the given pats and -I/-X opts
774 - match: a matcher built from the given pats and -I/-X opts
775 - pats: patterns used (globs are expanded on Windows)
775 - pats: patterns used (globs are expanded on Windows)
776 - slowpath: True if patterns aren't as simple as scanning filelogs
776 - slowpath: True if patterns aren't as simple as scanning filelogs
777 """
777 """
778 # pats/include/exclude are passed to match.match() directly in
778 # pats/include/exclude are passed to match.match() directly in
779 # _matchfiles() revset, but a log-like command should build its matcher
779 # _matchfiles() revset, but a log-like command should build its matcher
780 # with scmutil.match(). The difference is input pats are globbed on
780 # with scmutil.match(). The difference is input pats are globbed on
781 # platforms without shell expansion (windows).
781 # platforms without shell expansion (windows).
782 wctx = repo[None]
782 wctx = repo[None]
783 match, pats = scmutil.matchandpats(wctx, wopts.pats, wopts.opts)
783 match, pats = scmutil.matchandpats(wctx, wopts.pats, wopts.opts)
784 slowpath = match.anypats() or (
784 slowpath = match.anypats() or (
785 not match.always() and wopts.force_changelog_traversal
785 not match.always() and wopts.force_changelog_traversal
786 )
786 )
787 if not slowpath:
787 if not slowpath:
788 if wopts.follow and wopts.revspec:
788 if wopts.follow and wopts.revspec:
789 # There may be the case that a path doesn't exist in some (but
789 # There may be the case that a path doesn't exist in some (but
790 # not all) of the specified start revisions, but let's consider
790 # not all) of the specified start revisions, but let's consider
791 # the path is valid. Missing files will be warned by the matcher.
791 # the path is valid. Missing files will be warned by the matcher.
792 startctxs = [repo[r] for r in revs]
792 startctxs = [repo[r] for r in revs]
793 for f in match.files():
793 for f in match.files():
794 found = False
794 found = False
795 for c in startctxs:
795 for c in startctxs:
796 if f in c:
796 if f in c:
797 found = True
797 found = True
798 elif c.hasdir(f):
798 elif c.hasdir(f):
799 # If a directory exists in any of the start revisions,
799 # If a directory exists in any of the start revisions,
800 # take the slow path.
800 # take the slow path.
801 found = slowpath = True
801 found = slowpath = True
802 if not found:
802 if not found:
803 raise error.Abort(
803 raise error.Abort(
804 _(
804 _(
805 b'cannot follow file not in any of the specified '
805 b'cannot follow file not in any of the specified '
806 b'revisions: "%s"'
806 b'revisions: "%s"'
807 )
807 )
808 % f
808 % f
809 )
809 )
810 elif wopts.follow:
810 elif wopts.follow:
811 for f in match.files():
811 for f in match.files():
812 if f not in wctx:
812 if f not in wctx:
813 # If the file exists, it may be a directory, so let it
813 # If the file exists, it may be a directory, so let it
814 # take the slow path.
814 # take the slow path.
815 if os.path.exists(repo.wjoin(f)):
815 if os.path.exists(repo.wjoin(f)):
816 slowpath = True
816 slowpath = True
817 continue
817 continue
818 else:
818 else:
819 raise error.Abort(
819 raise error.Abort(
820 _(
820 _(
821 b'cannot follow file not in parent '
821 b'cannot follow file not in parent '
822 b'revision: "%s"'
822 b'revision: "%s"'
823 )
823 )
824 % f
824 % f
825 )
825 )
826 filelog = repo.file(f)
826 filelog = repo.file(f)
827 if not filelog:
827 if not filelog:
828 # A file exists in wdir but not in history, which means
828 # A file exists in wdir but not in history, which means
829 # the file isn't committed yet.
829 # the file isn't committed yet.
830 raise error.Abort(
830 raise error.Abort(
831 _(b'cannot follow nonexistent file: "%s"') % f
831 _(b'cannot follow nonexistent file: "%s"') % f
832 )
832 )
833 else:
833 else:
834 for f in match.files():
834 for f in match.files():
835 filelog = repo.file(f)
835 filelog = repo.file(f)
836 if not filelog:
836 if not filelog:
837 # A zero count may be a directory or deleted file, so
837 # A zero count may be a directory or deleted file, so
838 # try to find matching entries on the slow path.
838 # try to find matching entries on the slow path.
839 slowpath = True
839 slowpath = True
840
840
841 # We decided to fall back to the slowpath because at least one
841 # We decided to fall back to the slowpath because at least one
842 # of the paths was not a file. Check to see if at least one of them
842 # of the paths was not a file. Check to see if at least one of them
843 # existed in history - in that case, we'll continue down the
843 # existed in history - in that case, we'll continue down the
844 # slowpath; otherwise, we can turn off the slowpath
844 # slowpath; otherwise, we can turn off the slowpath
845 if slowpath:
845 if slowpath:
846 for path in match.files():
846 for path in match.files():
847 if path == b'.' or path in repo.store:
847 if path == b'.' or path in repo.store:
848 break
848 break
849 else:
849 else:
850 slowpath = False
850 slowpath = False
851
851
852 return match, pats, slowpath
852 return match, pats, slowpath
853
853
854
854
855 def _fileancestors(repo, revs, match, followfirst):
855 def _fileancestors(repo, revs, match, followfirst):
856 fctxs = []
856 fctxs = []
857 for r in revs:
857 for r in revs:
858 ctx = repo[r]
858 ctx = repo[r]
859 fctxs.extend(ctx[f].introfilectx() for f in ctx.walk(match))
859 fctxs.extend(ctx[f].introfilectx() for f in ctx.walk(match))
860
860
861 # When displaying a revision with --patch --follow FILE, we have
861 # When displaying a revision with --patch --follow FILE, we have
862 # to know which file of the revision must be diffed. With
862 # to know which file of the revision must be diffed. With
863 # --follow, we want the names of the ancestors of FILE in the
863 # --follow, we want the names of the ancestors of FILE in the
864 # revision, stored in "fcache". "fcache" is populated as a side effect
864 # revision, stored in "fcache". "fcache" is populated as a side effect
865 # of the graph traversal.
865 # of the graph traversal.
866 fcache = {}
866 fcache = {}
867
867
868 def filematcher(ctx):
868 def filematcher(ctx):
869 return scmutil.matchfiles(repo, fcache.get(scmutil.intrev(ctx), []))
869 return scmutil.matchfiles(repo, fcache.get(scmutil.intrev(ctx), []))
870
870
871 def revgen():
871 def revgen():
872 for rev, cs in dagop.filectxancestors(fctxs, followfirst=followfirst):
872 for rev, cs in dagop.filectxancestors(fctxs, followfirst=followfirst):
873 fcache[rev] = [c.path() for c in cs]
873 fcache[rev] = [c.path() for c in cs]
874 yield rev
874 yield rev
875
875
876 return smartset.generatorset(revgen(), iterasc=False), filematcher
876 return smartset.generatorset(revgen(), iterasc=False), filematcher
877
877
878
878
879 def _makenofollowfilematcher(repo, pats, opts):
879 def _makenofollowfilematcher(repo, pats, opts):
880 '''hook for extensions to override the filematcher for non-follow cases'''
880 '''hook for extensions to override the filematcher for non-follow cases'''
881 return None
881 return None
882
882
883
883
884 _opt2logrevset = {
884 _opt2logrevset = {
885 b'no_merges': (b'not merge()', None),
885 b'no_merges': (b'not merge()', None),
886 b'only_merges': (b'merge()', None),
886 b'only_merges': (b'merge()', None),
887 b'_matchfiles': (None, b'_matchfiles(%ps)'),
887 b'_matchfiles': (None, b'_matchfiles(%ps)'),
888 b'date': (b'date(%s)', None),
888 b'date': (b'date(%s)', None),
889 b'branch': (b'branch(%s)', b'%lr'),
889 b'branch': (b'branch(%s)', b'%lr'),
890 b'_patslog': (b'filelog(%s)', b'%lr'),
890 b'_patslog': (b'filelog(%s)', b'%lr'),
891 b'keyword': (b'keyword(%s)', b'%lr'),
891 b'keyword': (b'keyword(%s)', b'%lr'),
892 b'prune': (b'ancestors(%s)', b'not %lr'),
892 b'prune': (b'ancestors(%s)', b'not %lr'),
893 b'user': (b'user(%s)', b'%lr'),
893 b'user': (b'user(%s)', b'%lr'),
894 }
894 }
895
895
896
896
897 def _makerevset(repo, wopts, slowpath):
897 def _makerevset(repo, wopts, slowpath):
898 """Return a revset string built from log options and file patterns"""
898 """Return a revset string built from log options and file patterns"""
899 opts = {
899 opts = {
900 b'branch': [repo.lookupbranch(b) for b in wopts.branches],
900 b'branch': [repo.lookupbranch(b) for b in wopts.branches],
901 b'date': wopts.date,
901 b'date': wopts.date,
902 b'keyword': wopts.keywords,
902 b'keyword': wopts.keywords,
903 b'no_merges': wopts.no_merges,
903 b'no_merges': wopts.no_merges,
904 b'only_merges': wopts.only_merges,
904 b'only_merges': wopts.only_merges,
905 b'prune': wopts.prune_ancestors,
905 b'prune': wopts.prune_ancestors,
906 b'user': wopts.users,
906 b'user': wopts.users,
907 }
907 }
908
908
909 if wopts.filter_revisions_by_pats and slowpath:
909 if wopts.filter_revisions_by_pats and slowpath:
910 # pats/include/exclude cannot be represented as separate
910 # pats/include/exclude cannot be represented as separate
911 # revset expressions as their filtering logic applies at file
911 # revset expressions as their filtering logic applies at file
912 # level. For instance "-I a -X b" matches a revision touching
912 # level. For instance "-I a -X b" matches a revision touching
913 # "a" and "b" while "file(a) and not file(b)" does
913 # "a" and "b" while "file(a) and not file(b)" does
914 # not. Besides, filesets are evaluated against the working
914 # not. Besides, filesets are evaluated against the working
915 # directory.
915 # directory.
916 matchargs = [b'r:', b'd:relpath']
916 matchargs = [b'r:', b'd:relpath']
917 for p in wopts.pats:
917 for p in wopts.pats:
918 matchargs.append(b'p:' + p)
918 matchargs.append(b'p:' + p)
919 for p in wopts.include_pats:
919 for p in wopts.include_pats:
920 matchargs.append(b'i:' + p)
920 matchargs.append(b'i:' + p)
921 for p in wopts.exclude_pats:
921 for p in wopts.exclude_pats:
922 matchargs.append(b'x:' + p)
922 matchargs.append(b'x:' + p)
923 opts[b'_matchfiles'] = matchargs
923 opts[b'_matchfiles'] = matchargs
924 elif wopts.filter_revisions_by_pats and not wopts.follow:
924 elif wopts.filter_revisions_by_pats and not wopts.follow:
925 opts[b'_patslog'] = list(wopts.pats)
925 opts[b'_patslog'] = list(wopts.pats)
926
926
927 expr = []
927 expr = []
928 for op, val in sorted(pycompat.iteritems(opts)):
928 for op, val in sorted(pycompat.iteritems(opts)):
929 if not val:
929 if not val:
930 continue
930 continue
931 revop, listop = _opt2logrevset[op]
931 revop, listop = _opt2logrevset[op]
932 if revop and b'%' not in revop:
932 if revop and b'%' not in revop:
933 expr.append(revop)
933 expr.append(revop)
934 elif not listop:
934 elif not listop:
935 expr.append(revsetlang.formatspec(revop, val))
935 expr.append(revsetlang.formatspec(revop, val))
936 else:
936 else:
937 if revop:
937 if revop:
938 val = [revsetlang.formatspec(revop, v) for v in val]
938 val = [revsetlang.formatspec(revop, v) for v in val]
939 expr.append(revsetlang.formatspec(listop, val))
939 expr.append(revsetlang.formatspec(listop, val))
940
940
941 if expr:
941 if expr:
942 expr = b'(' + b' and '.join(expr) + b')'
942 expr = b'(' + b' and '.join(expr) + b')'
943 else:
943 else:
944 expr = None
944 expr = None
945 return expr
945 return expr
946
946
947
947
948 def _initialrevs(repo, wopts):
948 def _initialrevs(repo, wopts):
949 """Return the initial set of revisions to be filtered or followed"""
949 """Return the initial set of revisions to be filtered or followed"""
950 if wopts.revspec:
950 if wopts.revspec:
951 revs = scmutil.revrange(repo, wopts.revspec)
951 revs = scmutil.revrange(repo, wopts.revspec)
952 elif wopts.follow and repo.dirstate.p1() == nullid:
952 elif wopts.follow and repo.dirstate.p1() == nullid:
953 revs = smartset.baseset()
953 revs = smartset.baseset()
954 elif wopts.follow:
954 elif wopts.follow:
955 revs = repo.revs(b'.')
955 revs = repo.revs(b'.')
956 else:
956 else:
957 revs = smartset.spanset(repo)
957 revs = smartset.spanset(repo)
958 revs.reverse()
958 revs.reverse()
959 return revs
959 return revs
960
960
961
961
962 def makewalker(repo, wopts):
962 def makewalker(repo, wopts):
963 # type: (Any, walkopts) -> Tuple[smartset.abstractsmartset, Optional[Callable[[Any], matchmod.basematcher]]]
963 # type: (Any, walkopts) -> Tuple[smartset.abstractsmartset, Optional[Callable[[Any], matchmod.basematcher]]]
964 """Build (revs, makefilematcher) to scan revision/file history
964 """Build (revs, makefilematcher) to scan revision/file history
965
965
966 - revs is the smartset to be traversed.
966 - revs is the smartset to be traversed.
967 - makefilematcher is a function to map ctx to a matcher for that revision
967 - makefilematcher is a function to map ctx to a matcher for that revision
968 """
968 """
969 revs = _initialrevs(repo, wopts)
969 revs = _initialrevs(repo, wopts)
970 if not revs:
970 if not revs:
971 return smartset.baseset(), None
971 return smartset.baseset(), None
972 # TODO: might want to merge slowpath with wopts.force_changelog_traversal
972 # TODO: might want to merge slowpath with wopts.force_changelog_traversal
973 match, pats, slowpath = _makematcher(repo, revs, wopts)
973 match, pats, slowpath = _makematcher(repo, revs, wopts)
974 wopts = attr.evolve(wopts, pats=pats)
974 wopts = attr.evolve(wopts, pats=pats)
975
975
976 filematcher = None
976 filematcher = None
977 if wopts.follow:
977 if wopts.follow:
978 if slowpath or match.always():
978 if slowpath or match.always():
979 revs = dagop.revancestors(repo, revs, followfirst=wopts.follow == 1)
979 revs = dagop.revancestors(repo, revs, followfirst=wopts.follow == 1)
980 else:
980 else:
981 assert not wopts.force_changelog_traversal
981 assert not wopts.force_changelog_traversal
982 revs, filematcher = _fileancestors(
982 revs, filematcher = _fileancestors(
983 repo, revs, match, followfirst=wopts.follow == 1
983 repo, revs, match, followfirst=wopts.follow == 1
984 )
984 )
985 revs.reverse()
985 revs.reverse()
986 if filematcher is None:
986 if filematcher is None:
987 filematcher = _makenofollowfilematcher(repo, wopts.pats, wopts.opts)
987 filematcher = _makenofollowfilematcher(repo, wopts.pats, wopts.opts)
988 if filematcher is None:
988 if filematcher is None:
989
989
990 def filematcher(ctx):
990 def filematcher(ctx):
991 return match
991 return match
992
992
993 expr = _makerevset(repo, wopts, slowpath)
993 expr = _makerevset(repo, wopts, slowpath)
994 if wopts.sort_revisions:
994 if wopts.sort_revisions:
995 assert wopts.sort_revisions in {b'topo', b'desc'}
995 assert wopts.sort_revisions in {b'topo', b'desc'}
996 if wopts.sort_revisions == b'topo':
996 if wopts.sort_revisions == b'topo':
997 if not revs.istopo():
997 if not revs.istopo():
998 revs = dagop.toposort(revs, repo.changelog.parentrevs)
998 revs = dagop.toposort(revs, repo.changelog.parentrevs)
999 # TODO: try to iterate the set lazily
999 # TODO: try to iterate the set lazily
1000 revs = revset.baseset(list(revs), istopo=True)
1000 revs = revset.baseset(list(revs), istopo=True)
1001 elif not (revs.isdescending() or revs.istopo()):
1001 elif not (revs.isdescending() or revs.istopo()):
1002 # User-specified revs might be unsorted
1002 # User-specified revs might be unsorted
1003 revs.sort(reverse=True)
1003 revs.sort(reverse=True)
1004 if expr:
1004 if expr:
1005 matcher = revset.match(None, expr)
1005 matcher = revset.match(None, expr)
1006 revs = matcher(repo, revs)
1006 revs = matcher(repo, revs)
1007 if wopts.limit is not None:
1007 if wopts.limit is not None:
1008 revs = revs.slice(0, wopts.limit)
1008 revs = revs.slice(0, wopts.limit)
1009
1009
1010 return revs, filematcher
1010 return revs, filematcher
1011
1011
1012
1012
1013 def getrevs(repo, wopts):
1013 def getrevs(repo, wopts):
1014 # type: (Any, walkopts) -> Tuple[smartset.abstractsmartset, Optional[changesetdiffer]]
1014 # type: (Any, walkopts) -> Tuple[smartset.abstractsmartset, Optional[changesetdiffer]]
1015 """Return (revs, differ) where revs is a smartset
1015 """Return (revs, differ) where revs is a smartset
1016
1016
1017 differ is a changesetdiffer with pre-configured file matcher.
1017 differ is a changesetdiffer with pre-configured file matcher.
1018 """
1018 """
1019 revs, filematcher = makewalker(repo, wopts)
1019 revs, filematcher = makewalker(repo, wopts)
1020 if not revs:
1020 if not revs:
1021 return revs, None
1021 return revs, None
1022 differ = changesetdiffer()
1022 differ = changesetdiffer()
1023 differ._makefilematcher = filematcher
1023 differ._makefilematcher = filematcher
1024 return revs, differ
1024 return revs, differ
1025
1025
1026
1026
1027 def get_bookmark_revs(repo, bookmark, walk_opts):
1028 # type: (Any, bookmark, walk_opts) -> Tuple[smartset.abstractsmartset, Optional[changesetdiffer]]
1029 """Return (revs, differ) where revs is a smartset
1030
1031 differ is a changesetdiffer with pre-configured file matcher.
1032 """
1033 revs, filematcher = makewalker(repo, walk_opts)
1034 if not revs:
1035 return revs, None
1036 differ = changesetdiffer()
1037 differ._makefilematcher = filematcher
1038
1039 if bookmark:
1040 if bookmark not in repo._bookmarks:
1041 raise error.Abort(_(b"bookmark '%s' not found") % bookmark)
1042 revs = scmutil.bookmarkrevs(repo, bookmark)
1043
1044 return revs, differ
1045
1046
1027 def _parselinerangeopt(repo, opts):
1047 def _parselinerangeopt(repo, opts):
1028 """Parse --line-range log option and return a list of tuples (filename,
1048 """Parse --line-range log option and return a list of tuples (filename,
1029 (fromline, toline)).
1049 (fromline, toline)).
1030 """
1050 """
1031 linerangebyfname = []
1051 linerangebyfname = []
1032 for pat in opts.get(b'line_range', []):
1052 for pat in opts.get(b'line_range', []):
1033 try:
1053 try:
1034 pat, linerange = pat.rsplit(b',', 1)
1054 pat, linerange = pat.rsplit(b',', 1)
1035 except ValueError:
1055 except ValueError:
1036 raise error.Abort(_(b'malformatted line-range pattern %s') % pat)
1056 raise error.Abort(_(b'malformatted line-range pattern %s') % pat)
1037 try:
1057 try:
1038 fromline, toline = map(int, linerange.split(b':'))
1058 fromline, toline = map(int, linerange.split(b':'))
1039 except ValueError:
1059 except ValueError:
1040 raise error.Abort(_(b"invalid line range for %s") % pat)
1060 raise error.Abort(_(b"invalid line range for %s") % pat)
1041 msg = _(b"line range pattern '%s' must match exactly one file") % pat
1061 msg = _(b"line range pattern '%s' must match exactly one file") % pat
1042 fname = scmutil.parsefollowlinespattern(repo, None, pat, msg)
1062 fname = scmutil.parsefollowlinespattern(repo, None, pat, msg)
1043 linerangebyfname.append(
1063 linerangebyfname.append(
1044 (fname, util.processlinerange(fromline, toline))
1064 (fname, util.processlinerange(fromline, toline))
1045 )
1065 )
1046 return linerangebyfname
1066 return linerangebyfname
1047
1067
1048
1068
1049 def getlinerangerevs(repo, userrevs, opts):
1069 def getlinerangerevs(repo, userrevs, opts):
1050 """Return (revs, differ).
1070 """Return (revs, differ).
1051
1071
1052 "revs" are revisions obtained by processing "line-range" log options and
1072 "revs" are revisions obtained by processing "line-range" log options and
1053 walking block ancestors of each specified file/line-range.
1073 walking block ancestors of each specified file/line-range.
1054
1074
1055 "differ" is a changesetdiffer with pre-configured file matcher and hunks
1075 "differ" is a changesetdiffer with pre-configured file matcher and hunks
1056 filter.
1076 filter.
1057 """
1077 """
1058 wctx = repo[None]
1078 wctx = repo[None]
1059
1079
1060 # Two-levels map of "rev -> file ctx -> [line range]".
1080 # Two-levels map of "rev -> file ctx -> [line range]".
1061 linerangesbyrev = {}
1081 linerangesbyrev = {}
1062 for fname, (fromline, toline) in _parselinerangeopt(repo, opts):
1082 for fname, (fromline, toline) in _parselinerangeopt(repo, opts):
1063 if fname not in wctx:
1083 if fname not in wctx:
1064 raise error.Abort(
1084 raise error.Abort(
1065 _(b'cannot follow file not in parent revision: "%s"') % fname
1085 _(b'cannot follow file not in parent revision: "%s"') % fname
1066 )
1086 )
1067 fctx = wctx.filectx(fname)
1087 fctx = wctx.filectx(fname)
1068 for fctx, linerange in dagop.blockancestors(fctx, fromline, toline):
1088 for fctx, linerange in dagop.blockancestors(fctx, fromline, toline):
1069 rev = fctx.introrev()
1089 rev = fctx.introrev()
1070 if rev is None:
1090 if rev is None:
1071 rev = wdirrev
1091 rev = wdirrev
1072 if rev not in userrevs:
1092 if rev not in userrevs:
1073 continue
1093 continue
1074 linerangesbyrev.setdefault(rev, {}).setdefault(
1094 linerangesbyrev.setdefault(rev, {}).setdefault(
1075 fctx.path(), []
1095 fctx.path(), []
1076 ).append(linerange)
1096 ).append(linerange)
1077
1097
1078 def nofilterhunksfn(fctx, hunks):
1098 def nofilterhunksfn(fctx, hunks):
1079 return hunks
1099 return hunks
1080
1100
1081 def hunksfilter(ctx):
1101 def hunksfilter(ctx):
1082 fctxlineranges = linerangesbyrev.get(scmutil.intrev(ctx))
1102 fctxlineranges = linerangesbyrev.get(scmutil.intrev(ctx))
1083 if fctxlineranges is None:
1103 if fctxlineranges is None:
1084 return nofilterhunksfn
1104 return nofilterhunksfn
1085
1105
1086 def filterfn(fctx, hunks):
1106 def filterfn(fctx, hunks):
1087 lineranges = fctxlineranges.get(fctx.path())
1107 lineranges = fctxlineranges.get(fctx.path())
1088 if lineranges is not None:
1108 if lineranges is not None:
1089 for hr, lines in hunks:
1109 for hr, lines in hunks:
1090 if hr is None: # binary
1110 if hr is None: # binary
1091 yield hr, lines
1111 yield hr, lines
1092 continue
1112 continue
1093 if any(mdiff.hunkinrange(hr[2:], lr) for lr in lineranges):
1113 if any(mdiff.hunkinrange(hr[2:], lr) for lr in lineranges):
1094 yield hr, lines
1114 yield hr, lines
1095 else:
1115 else:
1096 for hunk in hunks:
1116 for hunk in hunks:
1097 yield hunk
1117 yield hunk
1098
1118
1099 return filterfn
1119 return filterfn
1100
1120
1101 def filematcher(ctx):
1121 def filematcher(ctx):
1102 files = list(linerangesbyrev.get(scmutil.intrev(ctx), []))
1122 files = list(linerangesbyrev.get(scmutil.intrev(ctx), []))
1103 return scmutil.matchfiles(repo, files)
1123 return scmutil.matchfiles(repo, files)
1104
1124
1105 revs = sorted(linerangesbyrev, reverse=True)
1125 revs = sorted(linerangesbyrev, reverse=True)
1106
1126
1107 differ = changesetdiffer()
1127 differ = changesetdiffer()
1108 differ._makefilematcher = filematcher
1128 differ._makefilematcher = filematcher
1109 differ._makehunksfilter = hunksfilter
1129 differ._makehunksfilter = hunksfilter
1110 return smartset.baseset(revs), differ
1130 return smartset.baseset(revs), differ
1111
1131
1112
1132
1113 def _graphnodeformatter(ui, displayer):
1133 def _graphnodeformatter(ui, displayer):
1114 spec = ui.config(b'command-templates', b'graphnode')
1134 spec = ui.config(b'command-templates', b'graphnode')
1115 if not spec:
1135 if not spec:
1116 return templatekw.getgraphnode # fast path for "{graphnode}"
1136 return templatekw.getgraphnode # fast path for "{graphnode}"
1117
1137
1118 spec = templater.unquotestring(spec)
1138 spec = templater.unquotestring(spec)
1119 if isinstance(displayer, changesettemplater):
1139 if isinstance(displayer, changesettemplater):
1120 # reuse cache of slow templates
1140 # reuse cache of slow templates
1121 tres = displayer._tresources
1141 tres = displayer._tresources
1122 else:
1142 else:
1123 tres = formatter.templateresources(ui)
1143 tres = formatter.templateresources(ui)
1124 templ = formatter.maketemplater(
1144 templ = formatter.maketemplater(
1125 ui, spec, defaults=templatekw.keywords, resources=tres
1145 ui, spec, defaults=templatekw.keywords, resources=tres
1126 )
1146 )
1127
1147
1128 def formatnode(repo, ctx, cache):
1148 def formatnode(repo, ctx, cache):
1129 props = {b'ctx': ctx, b'repo': repo}
1149 props = {b'ctx': ctx, b'repo': repo}
1130 return templ.renderdefault(props)
1150 return templ.renderdefault(props)
1131
1151
1132 return formatnode
1152 return formatnode
1133
1153
1134
1154
1135 def displaygraph(ui, repo, dag, displayer, edgefn, getcopies=None, props=None):
1155 def displaygraph(ui, repo, dag, displayer, edgefn, getcopies=None, props=None):
1136 props = props or {}
1156 props = props or {}
1137 formatnode = _graphnodeformatter(ui, displayer)
1157 formatnode = _graphnodeformatter(ui, displayer)
1138 state = graphmod.asciistate()
1158 state = graphmod.asciistate()
1139 styles = state.styles
1159 styles = state.styles
1140
1160
1141 # only set graph styling if HGPLAIN is not set.
1161 # only set graph styling if HGPLAIN is not set.
1142 if ui.plain(b'graph'):
1162 if ui.plain(b'graph'):
1143 # set all edge styles to |, the default pre-3.8 behaviour
1163 # set all edge styles to |, the default pre-3.8 behaviour
1144 styles.update(dict.fromkeys(styles, b'|'))
1164 styles.update(dict.fromkeys(styles, b'|'))
1145 else:
1165 else:
1146 edgetypes = {
1166 edgetypes = {
1147 b'parent': graphmod.PARENT,
1167 b'parent': graphmod.PARENT,
1148 b'grandparent': graphmod.GRANDPARENT,
1168 b'grandparent': graphmod.GRANDPARENT,
1149 b'missing': graphmod.MISSINGPARENT,
1169 b'missing': graphmod.MISSINGPARENT,
1150 }
1170 }
1151 for name, key in edgetypes.items():
1171 for name, key in edgetypes.items():
1152 # experimental config: experimental.graphstyle.*
1172 # experimental config: experimental.graphstyle.*
1153 styles[key] = ui.config(
1173 styles[key] = ui.config(
1154 b'experimental', b'graphstyle.%s' % name, styles[key]
1174 b'experimental', b'graphstyle.%s' % name, styles[key]
1155 )
1175 )
1156 if not styles[key]:
1176 if not styles[key]:
1157 styles[key] = None
1177 styles[key] = None
1158
1178
1159 # experimental config: experimental.graphshorten
1179 # experimental config: experimental.graphshorten
1160 state.graphshorten = ui.configbool(b'experimental', b'graphshorten')
1180 state.graphshorten = ui.configbool(b'experimental', b'graphshorten')
1161
1181
1162 formatnode_cache = {}
1182 formatnode_cache = {}
1163 for rev, type, ctx, parents in dag:
1183 for rev, type, ctx, parents in dag:
1164 char = formatnode(repo, ctx, formatnode_cache)
1184 char = formatnode(repo, ctx, formatnode_cache)
1165 copies = getcopies(ctx) if getcopies else None
1185 copies = getcopies(ctx) if getcopies else None
1166 edges = edgefn(type, char, state, rev, parents)
1186 edges = edgefn(type, char, state, rev, parents)
1167 firstedge = next(edges)
1187 firstedge = next(edges)
1168 width = firstedge[2]
1188 width = firstedge[2]
1169 displayer.show(
1189 displayer.show(
1170 ctx, copies=copies, graphwidth=width, **pycompat.strkwargs(props)
1190 ctx, copies=copies, graphwidth=width, **pycompat.strkwargs(props)
1171 )
1191 )
1172 lines = displayer.hunk.pop(rev).split(b'\n')
1192 lines = displayer.hunk.pop(rev).split(b'\n')
1173 if not lines[-1]:
1193 if not lines[-1]:
1174 del lines[-1]
1194 del lines[-1]
1175 displayer.flush(ctx)
1195 displayer.flush(ctx)
1176 for type, char, width, coldata in itertools.chain([firstedge], edges):
1196 for type, char, width, coldata in itertools.chain([firstedge], edges):
1177 graphmod.ascii(ui, state, type, char, lines, coldata)
1197 graphmod.ascii(ui, state, type, char, lines, coldata)
1178 lines = []
1198 lines = []
1179 displayer.close()
1199 displayer.close()
1180
1200
1181
1201
1182 def displaygraphrevs(ui, repo, revs, displayer, getrenamed):
1202 def displaygraphrevs(ui, repo, revs, displayer, getrenamed):
1183 revdag = graphmod.dagwalker(repo, revs)
1203 revdag = graphmod.dagwalker(repo, revs)
1184 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed)
1204 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed)
1185
1205
1186
1206
1187 def displayrevs(ui, repo, revs, displayer, getcopies):
1207 def displayrevs(ui, repo, revs, displayer, getcopies):
1188 for rev in revs:
1208 for rev in revs:
1189 ctx = repo[rev]
1209 ctx = repo[rev]
1190 copies = getcopies(ctx) if getcopies else None
1210 copies = getcopies(ctx) if getcopies else None
1191 displayer.show(ctx, copies=copies)
1211 displayer.show(ctx, copies=copies)
1192 displayer.flush(ctx)
1212 displayer.flush(ctx)
1193 displayer.close()
1213 displayer.close()
1194
1214
1195
1215
1196 def checkunsupportedgraphflags(pats, opts):
1216 def checkunsupportedgraphflags(pats, opts):
1197 for op in [b"newest_first"]:
1217 for op in [b"newest_first"]:
1198 if op in opts and opts[op]:
1218 if op in opts and opts[op]:
1199 raise error.Abort(
1219 raise error.Abort(
1200 _(b"-G/--graph option is incompatible with --%s")
1220 _(b"-G/--graph option is incompatible with --%s")
1201 % op.replace(b"_", b"-")
1221 % op.replace(b"_", b"-")
1202 )
1222 )
1203
1223
1204
1224
1205 def graphrevs(repo, nodes, opts):
1225 def graphrevs(repo, nodes, opts):
1206 limit = getlimit(opts)
1226 limit = getlimit(opts)
1207 nodes.reverse()
1227 nodes.reverse()
1208 if limit is not None:
1228 if limit is not None:
1209 nodes = nodes[:limit]
1229 nodes = nodes[:limit]
1210 return graphmod.nodes(repo, nodes)
1230 return graphmod.nodes(repo, nodes)
@@ -1,439 +1,439 b''
1 Show all commands except debug commands
1 Show all commands except debug commands
2 $ hg debugcomplete
2 $ hg debugcomplete
3 abort
3 abort
4 add
4 add
5 addremove
5 addremove
6 annotate
6 annotate
7 archive
7 archive
8 backout
8 backout
9 bisect
9 bisect
10 bookmarks
10 bookmarks
11 branch
11 branch
12 branches
12 branches
13 bundle
13 bundle
14 cat
14 cat
15 clone
15 clone
16 commit
16 commit
17 config
17 config
18 continue
18 continue
19 copy
19 copy
20 diff
20 diff
21 export
21 export
22 files
22 files
23 forget
23 forget
24 graft
24 graft
25 grep
25 grep
26 heads
26 heads
27 help
27 help
28 identify
28 identify
29 import
29 import
30 incoming
30 incoming
31 init
31 init
32 locate
32 locate
33 log
33 log
34 manifest
34 manifest
35 merge
35 merge
36 outgoing
36 outgoing
37 parents
37 parents
38 paths
38 paths
39 phase
39 phase
40 pull
40 pull
41 push
41 push
42 recover
42 recover
43 remove
43 remove
44 rename
44 rename
45 resolve
45 resolve
46 revert
46 revert
47 rollback
47 rollback
48 root
48 root
49 serve
49 serve
50 shelve
50 shelve
51 status
51 status
52 summary
52 summary
53 tag
53 tag
54 tags
54 tags
55 tip
55 tip
56 unbundle
56 unbundle
57 unshelve
57 unshelve
58 update
58 update
59 verify
59 verify
60 version
60 version
61
61
62 Show all commands that start with "a"
62 Show all commands that start with "a"
63 $ hg debugcomplete a
63 $ hg debugcomplete a
64 abort
64 abort
65 add
65 add
66 addremove
66 addremove
67 annotate
67 annotate
68 archive
68 archive
69
69
70 Do not show debug commands if there are other candidates
70 Do not show debug commands if there are other candidates
71 $ hg debugcomplete d
71 $ hg debugcomplete d
72 diff
72 diff
73
73
74 Show debug commands if there are no other candidates
74 Show debug commands if there are no other candidates
75 $ hg debugcomplete debug
75 $ hg debugcomplete debug
76 debugancestor
76 debugancestor
77 debugantivirusrunning
77 debugantivirusrunning
78 debugapplystreamclonebundle
78 debugapplystreamclonebundle
79 debugbackupbundle
79 debugbackupbundle
80 debugbuilddag
80 debugbuilddag
81 debugbundle
81 debugbundle
82 debugcapabilities
82 debugcapabilities
83 debugchangedfiles
83 debugchangedfiles
84 debugcheckstate
84 debugcheckstate
85 debugcolor
85 debugcolor
86 debugcommands
86 debugcommands
87 debugcomplete
87 debugcomplete
88 debugconfig
88 debugconfig
89 debugcreatestreamclonebundle
89 debugcreatestreamclonebundle
90 debugdag
90 debugdag
91 debugdata
91 debugdata
92 debugdate
92 debugdate
93 debugdeltachain
93 debugdeltachain
94 debugdirstate
94 debugdirstate
95 debugdiscovery
95 debugdiscovery
96 debugdownload
96 debugdownload
97 debugextensions
97 debugextensions
98 debugfileset
98 debugfileset
99 debugformat
99 debugformat
100 debugfsinfo
100 debugfsinfo
101 debuggetbundle
101 debuggetbundle
102 debugignore
102 debugignore
103 debugindex
103 debugindex
104 debugindexdot
104 debugindexdot
105 debugindexstats
105 debugindexstats
106 debuginstall
106 debuginstall
107 debugknown
107 debugknown
108 debuglabelcomplete
108 debuglabelcomplete
109 debuglocks
109 debuglocks
110 debugmanifestfulltextcache
110 debugmanifestfulltextcache
111 debugmergestate
111 debugmergestate
112 debugnamecomplete
112 debugnamecomplete
113 debugnodemap
113 debugnodemap
114 debugobsolete
114 debugobsolete
115 debugp1copies
115 debugp1copies
116 debugp2copies
116 debugp2copies
117 debugpathcomplete
117 debugpathcomplete
118 debugpathcopies
118 debugpathcopies
119 debugpeer
119 debugpeer
120 debugpickmergetool
120 debugpickmergetool
121 debugpushkey
121 debugpushkey
122 debugpvec
122 debugpvec
123 debugrebuilddirstate
123 debugrebuilddirstate
124 debugrebuildfncache
124 debugrebuildfncache
125 debugrename
125 debugrename
126 debugrequires
126 debugrequires
127 debugrevlog
127 debugrevlog
128 debugrevlogindex
128 debugrevlogindex
129 debugrevspec
129 debugrevspec
130 debugserve
130 debugserve
131 debugsetparents
131 debugsetparents
132 debugsidedata
132 debugsidedata
133 debugssl
133 debugssl
134 debugstrip
134 debugstrip
135 debugsub
135 debugsub
136 debugsuccessorssets
136 debugsuccessorssets
137 debugtagscache
137 debugtagscache
138 debugtemplate
138 debugtemplate
139 debuguigetpass
139 debuguigetpass
140 debuguiprompt
140 debuguiprompt
141 debugupdatecaches
141 debugupdatecaches
142 debugupgraderepo
142 debugupgraderepo
143 debugwalk
143 debugwalk
144 debugwhyunstable
144 debugwhyunstable
145 debugwireargs
145 debugwireargs
146 debugwireproto
146 debugwireproto
147
147
148 Do not show the alias of a debug command if there are other candidates
148 Do not show the alias of a debug command if there are other candidates
149 (this should hide rawcommit)
149 (this should hide rawcommit)
150 $ hg debugcomplete r
150 $ hg debugcomplete r
151 recover
151 recover
152 remove
152 remove
153 rename
153 rename
154 resolve
154 resolve
155 revert
155 revert
156 rollback
156 rollback
157 root
157 root
158 Show the alias of a debug command if there are no other candidates
158 Show the alias of a debug command if there are no other candidates
159 $ hg debugcomplete rawc
159 $ hg debugcomplete rawc
160
160
161
161
162 Show the global options
162 Show the global options
163 $ hg debugcomplete --options | sort
163 $ hg debugcomplete --options | sort
164 --color
164 --color
165 --config
165 --config
166 --cwd
166 --cwd
167 --debug
167 --debug
168 --debugger
168 --debugger
169 --encoding
169 --encoding
170 --encodingmode
170 --encodingmode
171 --help
171 --help
172 --hidden
172 --hidden
173 --noninteractive
173 --noninteractive
174 --pager
174 --pager
175 --profile
175 --profile
176 --quiet
176 --quiet
177 --repository
177 --repository
178 --time
178 --time
179 --traceback
179 --traceback
180 --verbose
180 --verbose
181 --version
181 --version
182 -R
182 -R
183 -h
183 -h
184 -q
184 -q
185 -v
185 -v
186 -y
186 -y
187
187
188 Show the options for the "serve" command
188 Show the options for the "serve" command
189 $ hg debugcomplete --options serve | sort
189 $ hg debugcomplete --options serve | sort
190 --accesslog
190 --accesslog
191 --address
191 --address
192 --certificate
192 --certificate
193 --cmdserver
193 --cmdserver
194 --color
194 --color
195 --config
195 --config
196 --cwd
196 --cwd
197 --daemon
197 --daemon
198 --daemon-postexec
198 --daemon-postexec
199 --debug
199 --debug
200 --debugger
200 --debugger
201 --encoding
201 --encoding
202 --encodingmode
202 --encodingmode
203 --errorlog
203 --errorlog
204 --help
204 --help
205 --hidden
205 --hidden
206 --ipv6
206 --ipv6
207 --name
207 --name
208 --noninteractive
208 --noninteractive
209 --pager
209 --pager
210 --pid-file
210 --pid-file
211 --port
211 --port
212 --prefix
212 --prefix
213 --print-url
213 --print-url
214 --profile
214 --profile
215 --quiet
215 --quiet
216 --repository
216 --repository
217 --stdio
217 --stdio
218 --style
218 --style
219 --subrepos
219 --subrepos
220 --templates
220 --templates
221 --time
221 --time
222 --traceback
222 --traceback
223 --verbose
223 --verbose
224 --version
224 --version
225 --web-conf
225 --web-conf
226 -6
226 -6
227 -A
227 -A
228 -E
228 -E
229 -R
229 -R
230 -S
230 -S
231 -a
231 -a
232 -d
232 -d
233 -h
233 -h
234 -n
234 -n
235 -p
235 -p
236 -q
236 -q
237 -t
237 -t
238 -v
238 -v
239 -y
239 -y
240
240
241 Show an error if we use --options with an ambiguous abbreviation
241 Show an error if we use --options with an ambiguous abbreviation
242 $ hg debugcomplete --options s
242 $ hg debugcomplete --options s
243 hg: command 's' is ambiguous:
243 hg: command 's' is ambiguous:
244 serve shelve showconfig status summary
244 serve shelve showconfig status summary
245 [255]
245 [255]
246
246
247 Show all commands + options
247 Show all commands + options
248 $ hg debugcommands
248 $ hg debugcommands
249 abort: dry-run
249 abort: dry-run
250 add: include, exclude, subrepos, dry-run
250 add: include, exclude, subrepos, dry-run
251 addremove: similarity, subrepos, include, exclude, dry-run
251 addremove: similarity, subrepos, include, exclude, dry-run
252 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, skip, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, include, exclude, template
252 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, skip, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, include, exclude, template
253 archive: no-decode, prefix, rev, type, subrepos, include, exclude
253 archive: no-decode, prefix, rev, type, subrepos, include, exclude
254 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
254 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
255 bisect: reset, good, bad, skip, extend, command, noupdate
255 bisect: reset, good, bad, skip, extend, command, noupdate
256 bookmarks: force, rev, delete, rename, inactive, list, template
256 bookmarks: force, rev, delete, rename, inactive, list, template
257 branch: force, clean, rev
257 branch: force, clean, rev
258 branches: active, closed, rev, template
258 branches: active, closed, rev, template
259 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
259 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
260 cat: output, rev, decode, include, exclude, template
260 cat: output, rev, decode, include, exclude, template
261 clone: noupdate, updaterev, rev, branch, pull, uncompressed, stream, ssh, remotecmd, insecure
261 clone: noupdate, updaterev, rev, branch, pull, uncompressed, stream, ssh, remotecmd, insecure
262 commit: addremove, close-branch, amend, secret, edit, force-close-branch, interactive, include, exclude, message, logfile, date, user, subrepos
262 commit: addremove, close-branch, amend, secret, edit, force-close-branch, interactive, include, exclude, message, logfile, date, user, subrepos
263 config: untrusted, edit, local, shared, non-shared, global, template
263 config: untrusted, edit, local, shared, non-shared, global, template
264 continue: dry-run
264 continue: dry-run
265 copy: forget, after, at-rev, force, include, exclude, dry-run
265 copy: forget, after, at-rev, force, include, exclude, dry-run
266 debugancestor:
266 debugancestor:
267 debugantivirusrunning:
267 debugantivirusrunning:
268 debugapplystreamclonebundle:
268 debugapplystreamclonebundle:
269 debugbackupbundle: recover, patch, git, limit, no-merges, stat, graph, style, template
269 debugbackupbundle: recover, patch, git, limit, no-merges, stat, graph, style, template
270 debugbuilddag: mergeable-file, overwritten-file, new-file
270 debugbuilddag: mergeable-file, overwritten-file, new-file
271 debugbundle: all, part-type, spec
271 debugbundle: all, part-type, spec
272 debugcapabilities:
272 debugcapabilities:
273 debugchangedfiles:
273 debugchangedfiles:
274 debugcheckstate:
274 debugcheckstate:
275 debugcolor: style
275 debugcolor: style
276 debugcommands:
276 debugcommands:
277 debugcomplete: options
277 debugcomplete: options
278 debugcreatestreamclonebundle:
278 debugcreatestreamclonebundle:
279 debugdag: tags, branches, dots, spaces
279 debugdag: tags, branches, dots, spaces
280 debugdata: changelog, manifest, dir
280 debugdata: changelog, manifest, dir
281 debugdate: extended
281 debugdate: extended
282 debugdeltachain: changelog, manifest, dir, template
282 debugdeltachain: changelog, manifest, dir, template
283 debugdirstate: nodates, dates, datesort
283 debugdirstate: nodates, dates, datesort
284 debugdiscovery: old, nonheads, rev, seed, ssh, remotecmd, insecure
284 debugdiscovery: old, nonheads, rev, seed, ssh, remotecmd, insecure
285 debugdownload: output
285 debugdownload: output
286 debugextensions: template
286 debugextensions: template
287 debugfileset: rev, all-files, show-matcher, show-stage
287 debugfileset: rev, all-files, show-matcher, show-stage
288 debugformat: template
288 debugformat: template
289 debugfsinfo:
289 debugfsinfo:
290 debuggetbundle: head, common, type
290 debuggetbundle: head, common, type
291 debugignore:
291 debugignore:
292 debugindex: changelog, manifest, dir, template
292 debugindex: changelog, manifest, dir, template
293 debugindexdot: changelog, manifest, dir
293 debugindexdot: changelog, manifest, dir
294 debugindexstats:
294 debugindexstats:
295 debuginstall: template
295 debuginstall: template
296 debugknown:
296 debugknown:
297 debuglabelcomplete:
297 debuglabelcomplete:
298 debuglocks: force-lock, force-wlock, set-lock, set-wlock
298 debuglocks: force-lock, force-wlock, set-lock, set-wlock
299 debugmanifestfulltextcache: clear, add
299 debugmanifestfulltextcache: clear, add
300 debugmergestate: style, template
300 debugmergestate: style, template
301 debugnamecomplete:
301 debugnamecomplete:
302 debugnodemap: dump-new, dump-disk, check, metadata
302 debugnodemap: dump-new, dump-disk, check, metadata
303 debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template
303 debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template
304 debugp1copies: rev
304 debugp1copies: rev
305 debugp2copies: rev
305 debugp2copies: rev
306 debugpathcomplete: full, normal, added, removed
306 debugpathcomplete: full, normal, added, removed
307 debugpathcopies: include, exclude
307 debugpathcopies: include, exclude
308 debugpeer:
308 debugpeer:
309 debugpickmergetool: rev, changedelete, include, exclude, tool
309 debugpickmergetool: rev, changedelete, include, exclude, tool
310 debugpushkey:
310 debugpushkey:
311 debugpvec:
311 debugpvec:
312 debugrebuilddirstate: rev, minimal
312 debugrebuilddirstate: rev, minimal
313 debugrebuildfncache:
313 debugrebuildfncache:
314 debugrename: rev
314 debugrename: rev
315 debugrequires:
315 debugrequires:
316 debugrevlog: changelog, manifest, dir, dump
316 debugrevlog: changelog, manifest, dir, dump
317 debugrevlogindex: changelog, manifest, dir, format
317 debugrevlogindex: changelog, manifest, dir, format
318 debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized
318 debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized
319 debugserve: sshstdio, logiofd, logiofile
319 debugserve: sshstdio, logiofd, logiofile
320 debugsetparents:
320 debugsetparents:
321 debugsidedata: changelog, manifest, dir
321 debugsidedata: changelog, manifest, dir
322 debugssl:
322 debugssl:
323 debugstrip: rev, force, no-backup, nobackup, , keep, bookmark, soft
323 debugstrip: rev, force, no-backup, nobackup, , keep, bookmark, soft
324 debugsub: rev
324 debugsub: rev
325 debugsuccessorssets: closest
325 debugsuccessorssets: closest
326 debugtagscache:
326 debugtagscache:
327 debugtemplate: rev, define
327 debugtemplate: rev, define
328 debuguigetpass: prompt
328 debuguigetpass: prompt
329 debuguiprompt: prompt
329 debuguiprompt: prompt
330 debugupdatecaches:
330 debugupdatecaches:
331 debugupgraderepo: optimize, run, backup, changelog, manifest
331 debugupgraderepo: optimize, run, backup, changelog, manifest
332 debugwalk: include, exclude
332 debugwalk: include, exclude
333 debugwhyunstable:
333 debugwhyunstable:
334 debugwireargs: three, four, five, ssh, remotecmd, insecure
334 debugwireargs: three, four, five, ssh, remotecmd, insecure
335 debugwireproto: localssh, peer, noreadstderr, nologhandshake, ssh, remotecmd, insecure
335 debugwireproto: localssh, peer, noreadstderr, nologhandshake, ssh, remotecmd, insecure
336 diff: rev, change, text, git, binary, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, unified, stat, root, include, exclude, subrepos
336 diff: rev, change, text, git, binary, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, unified, stat, root, include, exclude, subrepos
337 export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template
337 export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template
338 files: rev, print0, include, exclude, template, subrepos
338 files: rev, print0, include, exclude, template, subrepos
339 forget: interactive, include, exclude, dry-run
339 forget: interactive, include, exclude, dry-run
340 graft: rev, base, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run
340 graft: rev, base, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run
341 grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude
341 grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude
342 heads: rev, topo, active, closed, style, template
342 heads: rev, topo, active, closed, style, template
343 help: extension, command, keyword, system
343 help: extension, command, keyword, system
344 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure, template
344 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure, template
345 import: strip, base, secret, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
345 import: strip, base, secret, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
346 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
346 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
347 init: ssh, remotecmd, insecure
347 init: ssh, remotecmd, insecure
348 locate: rev, print0, fullpath, include, exclude
348 locate: rev, print0, fullpath, include, exclude
349 log: follow, follow-first, date, copies, keyword, rev, line-range, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
349 log: follow, follow-first, date, copies, keyword, rev, line-range, removed, only-merges, user, only-branch, branch, bookmark, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
350 manifest: rev, all, template
350 manifest: rev, all, template
351 merge: force, rev, preview, abort, tool
351 merge: force, rev, preview, abort, tool
352 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
352 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
353 parents: rev, style, template
353 parents: rev, style, template
354 paths: template
354 paths: template
355 phase: public, draft, secret, force, rev
355 phase: public, draft, secret, force, rev
356 pull: update, force, confirm, rev, bookmark, branch, ssh, remotecmd, insecure
356 pull: update, force, confirm, rev, bookmark, branch, ssh, remotecmd, insecure
357 push: force, rev, bookmark, all-bookmarks, branch, new-branch, pushvars, publish, ssh, remotecmd, insecure
357 push: force, rev, bookmark, all-bookmarks, branch, new-branch, pushvars, publish, ssh, remotecmd, insecure
358 recover: verify
358 recover: verify
359 remove: after, force, subrepos, include, exclude, dry-run
359 remove: after, force, subrepos, include, exclude, dry-run
360 rename: after, at-rev, force, include, exclude, dry-run
360 rename: after, at-rev, force, include, exclude, dry-run
361 resolve: all, list, mark, unmark, no-status, re-merge, tool, include, exclude, template
361 resolve: all, list, mark, unmark, no-status, re-merge, tool, include, exclude, template
362 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
362 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
363 rollback: dry-run, force
363 rollback: dry-run, force
364 root: template
364 root: template
365 serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, print-url, subrepos
365 serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, print-url, subrepos
366 shelve: addremove, unknown, cleanup, date, delete, edit, keep, list, message, name, patch, interactive, stat, include, exclude
366 shelve: addremove, unknown, cleanup, date, delete, edit, keep, list, message, name, patch, interactive, stat, include, exclude
367 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
367 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
368 summary: remote
368 summary: remote
369 tag: force, local, rev, remove, edit, message, date, user
369 tag: force, local, rev, remove, edit, message, date, user
370 tags: template
370 tags: template
371 tip: patch, git, style, template
371 tip: patch, git, style, template
372 unbundle: update
372 unbundle: update
373 unshelve: abort, continue, interactive, keep, name, tool, date
373 unshelve: abort, continue, interactive, keep, name, tool, date
374 update: clean, check, merge, date, rev, tool
374 update: clean, check, merge, date, rev, tool
375 verify: full
375 verify: full
376 version: template
376 version: template
377
377
378 $ hg init a
378 $ hg init a
379 $ cd a
379 $ cd a
380 $ echo fee > fee
380 $ echo fee > fee
381 $ hg ci -q -Amfee
381 $ hg ci -q -Amfee
382 $ hg tag fee
382 $ hg tag fee
383 $ mkdir fie
383 $ mkdir fie
384 $ echo dead > fie/dead
384 $ echo dead > fie/dead
385 $ echo live > fie/live
385 $ echo live > fie/live
386 $ hg bookmark fo
386 $ hg bookmark fo
387 $ hg branch -q fie
387 $ hg branch -q fie
388 $ hg ci -q -Amfie
388 $ hg ci -q -Amfie
389 $ echo fo > fo
389 $ echo fo > fo
390 $ hg branch -qf default
390 $ hg branch -qf default
391 $ hg ci -q -Amfo
391 $ hg ci -q -Amfo
392 $ echo Fum > Fum
392 $ echo Fum > Fum
393 $ hg ci -q -AmFum
393 $ hg ci -q -AmFum
394 $ hg bookmark Fum
394 $ hg bookmark Fum
395
395
396 Test debugpathcomplete
396 Test debugpathcomplete
397
397
398 $ hg debugpathcomplete f
398 $ hg debugpathcomplete f
399 fee
399 fee
400 fie
400 fie
401 fo
401 fo
402 $ hg debugpathcomplete -f f
402 $ hg debugpathcomplete -f f
403 fee
403 fee
404 fie/dead
404 fie/dead
405 fie/live
405 fie/live
406 fo
406 fo
407
407
408 $ hg rm Fum
408 $ hg rm Fum
409 $ hg debugpathcomplete -r F
409 $ hg debugpathcomplete -r F
410 Fum
410 Fum
411
411
412 Test debugnamecomplete
412 Test debugnamecomplete
413
413
414 $ hg debugnamecomplete
414 $ hg debugnamecomplete
415 Fum
415 Fum
416 default
416 default
417 fee
417 fee
418 fie
418 fie
419 fo
419 fo
420 tip
420 tip
421 $ hg debugnamecomplete f
421 $ hg debugnamecomplete f
422 fee
422 fee
423 fie
423 fie
424 fo
424 fo
425
425
426 Test debuglabelcomplete, a deprecated name for debugnamecomplete that is still
426 Test debuglabelcomplete, a deprecated name for debugnamecomplete that is still
427 used for completions in some shells.
427 used for completions in some shells.
428
428
429 $ hg debuglabelcomplete
429 $ hg debuglabelcomplete
430 Fum
430 Fum
431 default
431 default
432 fee
432 fee
433 fie
433 fie
434 fo
434 fo
435 tip
435 tip
436 $ hg debuglabelcomplete f
436 $ hg debuglabelcomplete f
437 fee
437 fee
438 fie
438 fie
439 fo
439 fo
General Comments 0
You need to be logged in to leave comments. Login now