##// END OF EJS Templates
graphlog: extract revset/support functions into cmdutil
Patrick Mezard -
r17180:ae062916 default
parent child Browse files
Show More
@@ -15,7 +15,7 b' revision graph is also shown.'
15 from mercurial.cmdutil import show_changeset
15 from mercurial.cmdutil import show_changeset
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 from mercurial import cmdutil, commands, extensions, scmutil
17 from mercurial import cmdutil, commands, extensions, scmutil
18 from mercurial import hg, util, graphmod, templatekw, revset
18 from mercurial import hg, util, graphmod, templatekw
19
19
20 cmdtable = {}
20 cmdtable = {}
21 command = cmdutil.command(cmdtable)
21 command = cmdutil.command(cmdtable)
@@ -27,244 +27,6 b' def _checkunsupportedflags(pats, opts):'
27 raise util.Abort(_("-G/--graph option is incompatible with --%s")
27 raise util.Abort(_("-G/--graph option is incompatible with --%s")
28 % op.replace("_", "-"))
28 % op.replace("_", "-"))
29
29
30 def _makefilematcher(repo, pats, followfirst):
31 # When displaying a revision with --patch --follow FILE, we have
32 # to know which file of the revision must be diffed. With
33 # --follow, we want the names of the ancestors of FILE in the
34 # revision, stored in "fcache". "fcache" is populated by
35 # reproducing the graph traversal already done by --follow revset
36 # and relating linkrevs to file names (which is not "correct" but
37 # good enough).
38 fcache = {}
39 fcacheready = [False]
40 pctx = repo['.']
41 wctx = repo[None]
42
43 def populate():
44 for fn in pats:
45 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
46 for c in i:
47 fcache.setdefault(c.linkrev(), set()).add(c.path())
48
49 def filematcher(rev):
50 if not fcacheready[0]:
51 # Lazy initialization
52 fcacheready[0] = True
53 populate()
54 return scmutil.match(wctx, fcache.get(rev, []), default='path')
55
56 return filematcher
57
58 def _makelogrevset(repo, pats, opts, revs):
59 """Return (expr, filematcher) where expr is a revset string built
60 from log options and file patterns or None. If --stat or --patch
61 are not passed filematcher is None. Otherwise it is a callable
62 taking a revision number and returning a match objects filtering
63 the files to be detailed when displaying the revision.
64 """
65 opt2revset = {
66 'no_merges': ('not merge()', None),
67 'only_merges': ('merge()', None),
68 '_ancestors': ('ancestors(%(val)s)', None),
69 '_fancestors': ('_firstancestors(%(val)s)', None),
70 '_descendants': ('descendants(%(val)s)', None),
71 '_fdescendants': ('_firstdescendants(%(val)s)', None),
72 '_matchfiles': ('_matchfiles(%(val)s)', None),
73 'date': ('date(%(val)r)', None),
74 'branch': ('branch(%(val)r)', ' or '),
75 '_patslog': ('filelog(%(val)r)', ' or '),
76 '_patsfollow': ('follow(%(val)r)', ' or '),
77 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
78 'keyword': ('keyword(%(val)r)', ' or '),
79 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
80 'user': ('user(%(val)r)', ' or '),
81 }
82
83 opts = dict(opts)
84 # follow or not follow?
85 follow = opts.get('follow') or opts.get('follow_first')
86 followfirst = opts.get('follow_first') and 1 or 0
87 # --follow with FILE behaviour depends on revs...
88 startrev = revs[0]
89 followdescendants = (len(revs) > 1 and revs[0] < revs[1]) and 1 or 0
90
91 # branch and only_branch are really aliases and must be handled at
92 # the same time
93 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
94 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
95 # pats/include/exclude are passed to match.match() directly in
96 # _matchfile() revset but walkchangerevs() builds its matcher with
97 # scmutil.match(). The difference is input pats are globbed on
98 # platforms without shell expansion (windows).
99 pctx = repo[None]
100 match, pats = scmutil.matchandpats(pctx, pats, opts)
101 slowpath = match.anypats() or (match.files() and opts.get('removed'))
102 if not slowpath:
103 for f in match.files():
104 if follow and f not in pctx:
105 raise util.Abort(_('cannot follow file not in parent '
106 'revision: "%s"') % f)
107 filelog = repo.file(f)
108 if not len(filelog):
109 # A zero count may be a directory or deleted file, so
110 # try to find matching entries on the slow path.
111 if follow:
112 raise util.Abort(
113 _('cannot follow nonexistent file: "%s"') % f)
114 slowpath = True
115 if slowpath:
116 # See cmdutil.walkchangerevs() slow path.
117 #
118 if follow:
119 raise util.Abort(_('can only follow copies/renames for explicit '
120 'filenames'))
121 # pats/include/exclude cannot be represented as separate
122 # revset expressions as their filtering logic applies at file
123 # level. For instance "-I a -X a" matches a revision touching
124 # "a" and "b" while "file(a) and not file(b)" does
125 # not. Besides, filesets are evaluated against the working
126 # directory.
127 matchargs = ['r:', 'd:relpath']
128 for p in pats:
129 matchargs.append('p:' + p)
130 for p in opts.get('include', []):
131 matchargs.append('i:' + p)
132 for p in opts.get('exclude', []):
133 matchargs.append('x:' + p)
134 matchargs = ','.join(('%r' % p) for p in matchargs)
135 opts['_matchfiles'] = matchargs
136 else:
137 if follow:
138 fpats = ('_patsfollow', '_patsfollowfirst')
139 fnopats = (('_ancestors', '_fancestors'),
140 ('_descendants', '_fdescendants'))
141 if pats:
142 # follow() revset inteprets its file argument as a
143 # manifest entry, so use match.files(), not pats.
144 opts[fpats[followfirst]] = list(match.files())
145 else:
146 opts[fnopats[followdescendants][followfirst]] = str(startrev)
147 else:
148 opts['_patslog'] = list(pats)
149
150 filematcher = None
151 if opts.get('patch') or opts.get('stat'):
152 if follow:
153 filematcher = _makefilematcher(repo, pats, followfirst)
154 else:
155 filematcher = lambda rev: match
156
157 expr = []
158 for op, val in opts.iteritems():
159 if not val:
160 continue
161 if op not in opt2revset:
162 continue
163 revop, andor = opt2revset[op]
164 if '%(val)' not in revop:
165 expr.append(revop)
166 else:
167 if not isinstance(val, list):
168 e = revop % {'val': val}
169 else:
170 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
171 expr.append(e)
172
173 if expr:
174 expr = '(' + ' and '.join(expr) + ')'
175 else:
176 expr = None
177 return expr, filematcher
178
179 def getlogrevs(repo, pats, opts):
180 """Return (revs, expr, filematcher) where revs is an iterable of
181 revision numbers, expr is a revset string built from log options
182 and file patterns or None, and used to filter 'revs'. If --stat or
183 --patch are not passed filematcher is None. Otherwise it is a
184 callable taking a revision number and returning a match objects
185 filtering the files to be detailed when displaying the revision.
186 """
187 def increasingrevs(repo, revs, matcher):
188 # The sorted input rev sequence is chopped in sub-sequences
189 # which are sorted in ascending order and passed to the
190 # matcher. The filtered revs are sorted again as they were in
191 # the original sub-sequence. This achieve several things:
192 #
193 # - getlogrevs() now returns a generator which behaviour is
194 # adapted to log need. First results come fast, last ones
195 # are batched for performances.
196 #
197 # - revset matchers often operate faster on revision in
198 # changelog order, because most filters deal with the
199 # changelog.
200 #
201 # - revset matchers can reorder revisions. "A or B" typically
202 # returns returns the revision matching A then the revision
203 # matching B. We want to hide this internal implementation
204 # detail from the caller, and sorting the filtered revision
205 # again achieves this.
206 for i, window in cmdutil.increasingwindows(0, len(revs), windowsize=1):
207 orevs = revs[i:i + window]
208 nrevs = set(matcher(repo, sorted(orevs)))
209 for rev in orevs:
210 if rev in nrevs:
211 yield rev
212
213 if not len(repo):
214 return iter([]), None, None
215 # Default --rev value depends on --follow but --follow behaviour
216 # depends on revisions resolved from --rev...
217 follow = opts.get('follow') or opts.get('follow_first')
218 if opts.get('rev'):
219 revs = scmutil.revrange(repo, opts['rev'])
220 else:
221 if follow and len(repo) > 0:
222 revs = scmutil.revrange(repo, ['.:0'])
223 else:
224 revs = range(len(repo) - 1, -1, -1)
225 if not revs:
226 return iter([]), None, None
227 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
228 if expr:
229 matcher = revset.match(repo.ui, expr)
230 revs = increasingrevs(repo, revs, matcher)
231 if not opts.get('hidden'):
232 # --hidden is still experimental and not worth a dedicated revset
233 # yet. Fortunately, filtering revision number is fast.
234 revs = (r for r in revs if r not in repo.changelog.hiddenrevs)
235 else:
236 revs = iter(revs)
237 return revs, expr, filematcher
238
239 def generate(ui, dag, displayer, showparents, edgefn, getrenamed=None,
240 filematcher=None):
241 seen, state = [], graphmod.asciistate()
242 for rev, type, ctx, parents in dag:
243 char = 'o'
244 if ctx.node() in showparents:
245 char = '@'
246 elif ctx.obsolete():
247 char = 'x'
248 copies = None
249 if getrenamed and ctx.rev():
250 copies = []
251 for fn in ctx.files():
252 rename = getrenamed(fn, ctx.rev())
253 if rename:
254 copies.append((fn, rename[0]))
255 revmatchfn = None
256 if filematcher is not None:
257 revmatchfn = filematcher(ctx.rev())
258 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
259 lines = displayer.hunk.pop(rev).split('\n')
260 if not lines[-1]:
261 del lines[-1]
262 displayer.flush(rev)
263 edges = edgefn(type, char, lines, seen, rev, parents)
264 for type, char, lines, coldata in edges:
265 graphmod.ascii(ui, state, type, char, lines, coldata)
266 displayer.close()
267
268 @command('glog',
30 @command('glog',
269 [('f', 'follow', None,
31 [('f', 'follow', None,
270 _('follow changeset history, or file history across copies and renames')),
32 _('follow changeset history, or file history across copies and renames')),
@@ -298,7 +60,7 b' def graphlog(ui, repo, *pats, **opts):'
298 directory.
60 directory.
299 """
61 """
300
62
301 revs, expr, filematcher = getlogrevs(repo, pats, opts)
63 revs, expr, filematcher = cmdutil.getgraphlogrevs(repo, pats, opts)
302 revs = sorted(revs, reverse=1)
64 revs = sorted(revs, reverse=1)
303 limit = cmdutil.loglimit(opts)
65 limit = cmdutil.loglimit(opts)
304 if limit is not None:
66 if limit is not None:
@@ -313,8 +75,8 b' def graphlog(ui, repo, *pats, **opts):'
313 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
75 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
314 displayer = show_changeset(ui, repo, opts, buffered=True)
76 displayer = show_changeset(ui, repo, opts, buffered=True)
315 showparents = [ctx.node() for ctx in repo[None].parents()]
77 showparents = [ctx.node() for ctx in repo[None].parents()]
316 generate(ui, revdag, displayer, showparents, graphmod.asciiedges,
78 cmdutil.displaygraph(ui, revdag, displayer, showparents,
317 getrenamed, filematcher)
79 graphmod.asciiedges, getrenamed, filematcher)
318
80
319 def graphrevs(repo, nodes, opts):
81 def graphrevs(repo, nodes, opts):
320 limit = cmdutil.loglimit(opts)
82 limit = cmdutil.loglimit(opts)
@@ -341,7 +103,8 b' def goutgoing(ui, repo, dest=None, **opt'
341 revdag = graphrevs(repo, o, opts)
103 revdag = graphrevs(repo, o, opts)
342 displayer = show_changeset(ui, repo, opts, buffered=True)
104 displayer = show_changeset(ui, repo, opts, buffered=True)
343 showparents = [ctx.node() for ctx in repo[None].parents()]
105 showparents = [ctx.node() for ctx in repo[None].parents()]
344 generate(ui, revdag, displayer, showparents, graphmod.asciiedges)
106 cmdutil.displaygraph(ui, revdag, displayer, showparents,
107 graphmod.asciiedges)
345
108
346 def gincoming(ui, repo, source="default", **opts):
109 def gincoming(ui, repo, source="default", **opts):
347 """show the incoming changesets alongside an ASCII revision graph
110 """show the incoming changesets alongside an ASCII revision graph
@@ -359,7 +122,8 b' def gincoming(ui, repo, source="default"'
359 def display(other, chlist, displayer):
122 def display(other, chlist, displayer):
360 revdag = graphrevs(other, chlist, opts)
123 revdag = graphrevs(other, chlist, opts)
361 showparents = [ctx.node() for ctx in repo[None].parents()]
124 showparents = [ctx.node() for ctx in repo[None].parents()]
362 generate(ui, revdag, displayer, showparents, graphmod.asciiedges)
125 cmdutil.displaygraph(ui, revdag, displayer, showparents,
126 graphmod.asciiedges)
363
127
364 hg._incoming(display, subreporecurse, ui, repo, source, opts, buffered=True)
128 hg._incoming(display, subreporecurse, ui, repo, source, opts, buffered=True)
365
129
@@ -10,7 +10,7 b' from i18n import _'
10 import os, sys, errno, re, tempfile
10 import os, sys, errno, re, tempfile
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
12 import match as matchmod
12 import match as matchmod
13 import subrepo, context, repair, bookmarks
13 import subrepo, context, repair, bookmarks, graphmod, revset
14
14
15 def parsealiases(cmd):
15 def parsealiases(cmd):
16 return cmd.lstrip("^").split("|")
16 return cmd.lstrip("^").split("|")
@@ -1192,6 +1192,244 b' def walkchangerevs(repo, match, opts, pr'
1192 yield change(rev)
1192 yield change(rev)
1193 return iterate()
1193 return iterate()
1194
1194
1195 def _makegraphfilematcher(repo, pats, followfirst):
1196 # When displaying a revision with --patch --follow FILE, we have
1197 # to know which file of the revision must be diffed. With
1198 # --follow, we want the names of the ancestors of FILE in the
1199 # revision, stored in "fcache". "fcache" is populated by
1200 # reproducing the graph traversal already done by --follow revset
1201 # and relating linkrevs to file names (which is not "correct" but
1202 # good enough).
1203 fcache = {}
1204 fcacheready = [False]
1205 pctx = repo['.']
1206 wctx = repo[None]
1207
1208 def populate():
1209 for fn in pats:
1210 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1211 for c in i:
1212 fcache.setdefault(c.linkrev(), set()).add(c.path())
1213
1214 def filematcher(rev):
1215 if not fcacheready[0]:
1216 # Lazy initialization
1217 fcacheready[0] = True
1218 populate()
1219 return scmutil.match(wctx, fcache.get(rev, []), default='path')
1220
1221 return filematcher
1222
1223 def _makegraphlogrevset(repo, pats, opts, revs):
1224 """Return (expr, filematcher) where expr is a revset string built
1225 from log options and file patterns or None. If --stat or --patch
1226 are not passed filematcher is None. Otherwise it is a callable
1227 taking a revision number and returning a match objects filtering
1228 the files to be detailed when displaying the revision.
1229 """
1230 opt2revset = {
1231 'no_merges': ('not merge()', None),
1232 'only_merges': ('merge()', None),
1233 '_ancestors': ('ancestors(%(val)s)', None),
1234 '_fancestors': ('_firstancestors(%(val)s)', None),
1235 '_descendants': ('descendants(%(val)s)', None),
1236 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1237 '_matchfiles': ('_matchfiles(%(val)s)', None),
1238 'date': ('date(%(val)r)', None),
1239 'branch': ('branch(%(val)r)', ' or '),
1240 '_patslog': ('filelog(%(val)r)', ' or '),
1241 '_patsfollow': ('follow(%(val)r)', ' or '),
1242 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1243 'keyword': ('keyword(%(val)r)', ' or '),
1244 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1245 'user': ('user(%(val)r)', ' or '),
1246 }
1247
1248 opts = dict(opts)
1249 # follow or not follow?
1250 follow = opts.get('follow') or opts.get('follow_first')
1251 followfirst = opts.get('follow_first') and 1 or 0
1252 # --follow with FILE behaviour depends on revs...
1253 startrev = revs[0]
1254 followdescendants = (len(revs) > 1 and revs[0] < revs[1]) and 1 or 0
1255
1256 # branch and only_branch are really aliases and must be handled at
1257 # the same time
1258 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1259 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1260 # pats/include/exclude are passed to match.match() directly in
1261 # _matchfile() revset but walkchangerevs() builds its matcher with
1262 # scmutil.match(). The difference is input pats are globbed on
1263 # platforms without shell expansion (windows).
1264 pctx = repo[None]
1265 match, pats = scmutil.matchandpats(pctx, pats, opts)
1266 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1267 if not slowpath:
1268 for f in match.files():
1269 if follow and f not in pctx:
1270 raise util.Abort(_('cannot follow file not in parent '
1271 'revision: "%s"') % f)
1272 filelog = repo.file(f)
1273 if not len(filelog):
1274 # A zero count may be a directory or deleted file, so
1275 # try to find matching entries on the slow path.
1276 if follow:
1277 raise util.Abort(
1278 _('cannot follow nonexistent file: "%s"') % f)
1279 slowpath = True
1280 if slowpath:
1281 # See walkchangerevs() slow path.
1282 #
1283 if follow:
1284 raise util.Abort(_('can only follow copies/renames for explicit '
1285 'filenames'))
1286 # pats/include/exclude cannot be represented as separate
1287 # revset expressions as their filtering logic applies at file
1288 # level. For instance "-I a -X a" matches a revision touching
1289 # "a" and "b" while "file(a) and not file(b)" does
1290 # not. Besides, filesets are evaluated against the working
1291 # directory.
1292 matchargs = ['r:', 'd:relpath']
1293 for p in pats:
1294 matchargs.append('p:' + p)
1295 for p in opts.get('include', []):
1296 matchargs.append('i:' + p)
1297 for p in opts.get('exclude', []):
1298 matchargs.append('x:' + p)
1299 matchargs = ','.join(('%r' % p) for p in matchargs)
1300 opts['_matchfiles'] = matchargs
1301 else:
1302 if follow:
1303 fpats = ('_patsfollow', '_patsfollowfirst')
1304 fnopats = (('_ancestors', '_fancestors'),
1305 ('_descendants', '_fdescendants'))
1306 if pats:
1307 # follow() revset inteprets its file argument as a
1308 # manifest entry, so use match.files(), not pats.
1309 opts[fpats[followfirst]] = list(match.files())
1310 else:
1311 opts[fnopats[followdescendants][followfirst]] = str(startrev)
1312 else:
1313 opts['_patslog'] = list(pats)
1314
1315 filematcher = None
1316 if opts.get('patch') or opts.get('stat'):
1317 if follow:
1318 filematcher = _makegraphfilematcher(repo, pats, followfirst)
1319 else:
1320 filematcher = lambda rev: match
1321
1322 expr = []
1323 for op, val in opts.iteritems():
1324 if not val:
1325 continue
1326 if op not in opt2revset:
1327 continue
1328 revop, andor = opt2revset[op]
1329 if '%(val)' not in revop:
1330 expr.append(revop)
1331 else:
1332 if not isinstance(val, list):
1333 e = revop % {'val': val}
1334 else:
1335 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
1336 expr.append(e)
1337
1338 if expr:
1339 expr = '(' + ' and '.join(expr) + ')'
1340 else:
1341 expr = None
1342 return expr, filematcher
1343
1344 def getgraphlogrevs(repo, pats, opts):
1345 """Return (revs, expr, filematcher) where revs is an iterable of
1346 revision numbers, expr is a revset string built from log options
1347 and file patterns or None, and used to filter 'revs'. If --stat or
1348 --patch are not passed filematcher is None. Otherwise it is a
1349 callable taking a revision number and returning a match objects
1350 filtering the files to be detailed when displaying the revision.
1351 """
1352 def increasingrevs(repo, revs, matcher):
1353 # The sorted input rev sequence is chopped in sub-sequences
1354 # which are sorted in ascending order and passed to the
1355 # matcher. The filtered revs are sorted again as they were in
1356 # the original sub-sequence. This achieve several things:
1357 #
1358 # - getlogrevs() now returns a generator which behaviour is
1359 # adapted to log need. First results come fast, last ones
1360 # are batched for performances.
1361 #
1362 # - revset matchers often operate faster on revision in
1363 # changelog order, because most filters deal with the
1364 # changelog.
1365 #
1366 # - revset matchers can reorder revisions. "A or B" typically
1367 # returns returns the revision matching A then the revision
1368 # matching B. We want to hide this internal implementation
1369 # detail from the caller, and sorting the filtered revision
1370 # again achieves this.
1371 for i, window in increasingwindows(0, len(revs), windowsize=1):
1372 orevs = revs[i:i + window]
1373 nrevs = set(matcher(repo, sorted(orevs)))
1374 for rev in orevs:
1375 if rev in nrevs:
1376 yield rev
1377
1378 if not len(repo):
1379 return iter([]), None, None
1380 # Default --rev value depends on --follow but --follow behaviour
1381 # depends on revisions resolved from --rev...
1382 follow = opts.get('follow') or opts.get('follow_first')
1383 if opts.get('rev'):
1384 revs = scmutil.revrange(repo, opts['rev'])
1385 else:
1386 if follow and len(repo) > 0:
1387 revs = scmutil.revrange(repo, ['.:0'])
1388 else:
1389 revs = range(len(repo) - 1, -1, -1)
1390 if not revs:
1391 return iter([]), None, None
1392 expr, filematcher = _makegraphlogrevset(repo, pats, opts, revs)
1393 if expr:
1394 matcher = revset.match(repo.ui, expr)
1395 revs = increasingrevs(repo, revs, matcher)
1396 if not opts.get('hidden'):
1397 # --hidden is still experimental and not worth a dedicated revset
1398 # yet. Fortunately, filtering revision number is fast.
1399 revs = (r for r in revs if r not in repo.changelog.hiddenrevs)
1400 else:
1401 revs = iter(revs)
1402 return revs, expr, filematcher
1403
1404 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
1405 filematcher=None):
1406 seen, state = [], graphmod.asciistate()
1407 for rev, type, ctx, parents in dag:
1408 char = 'o'
1409 if ctx.node() in showparents:
1410 char = '@'
1411 elif ctx.obsolete():
1412 char = 'x'
1413 copies = None
1414 if getrenamed and ctx.rev():
1415 copies = []
1416 for fn in ctx.files():
1417 rename = getrenamed(fn, ctx.rev())
1418 if rename:
1419 copies.append((fn, rename[0]))
1420 revmatchfn = None
1421 if filematcher is not None:
1422 revmatchfn = filematcher(ctx.rev())
1423 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
1424 lines = displayer.hunk.pop(rev).split('\n')
1425 if not lines[-1]:
1426 del lines[-1]
1427 displayer.flush(rev)
1428 edges = edgefn(type, char, lines, seen, rev, parents)
1429 for type, char, lines, coldata in edges:
1430 graphmod.ascii(ui, state, type, char, lines, coldata)
1431 displayer.close()
1432
1195 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1433 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1196 join = lambda f: os.path.join(prefix, f)
1434 join = lambda f: os.path.join(prefix, f)
1197 bad = []
1435 bad = []
@@ -82,13 +82,12 b' o (0) root'
82 > }
82 > }
83
83
84 $ cat > printrevset.py <<EOF
84 $ cat > printrevset.py <<EOF
85 > from mercurial import extensions, revset, commands
85 > from mercurial import extensions, revset, commands, cmdutil
86 > from hgext import graphlog
87 >
86 >
88 > def uisetup(ui):
87 > def uisetup(ui):
89 > def printrevset(orig, ui, repo, *pats, **opts):
88 > def printrevset(orig, ui, repo, *pats, **opts):
90 > if opts.get('print_revset'):
89 > if opts.get('print_revset'):
91 > expr = graphlog.getlogrevs(repo, pats, opts)[1]
90 > expr = cmdutil.getgraphlogrevs(repo, pats, opts)[1]
92 > if expr:
91 > if expr:
93 > tree = revset.parse(expr)[0]
92 > tree = revset.parse(expr)[0]
94 > else:
93 > else:
General Comments 0
You need to be logged in to leave comments. Login now