239 def check_unsupported_flags(pats, opts): |
239 def check_unsupported_flags(pats, opts): |
240 for op in ["follow_first", "copies", "newest_first"]: |
240 for op in ["follow_first", "copies", "newest_first"]: |
241 if op in opts and opts[op]: |
241 if op in opts and opts[op]: |
242 raise util.Abort(_("-G/--graph option is incompatible with --%s") |
242 raise util.Abort(_("-G/--graph option is incompatible with --%s") |
243 % op.replace("_", "-")) |
243 % op.replace("_", "-")) |
244 if pats and opts.get('follow'): |
|
245 raise util.Abort(_("-G/--graph option is incompatible with --follow " |
|
246 "with file argument")) |
|
247 |
244 |
248 def revset(repo, pats, opts): |
245 def revset(repo, pats, opts): |
249 """Return revset str built of revisions, log options and file patterns. |
246 """Return revset str built of revisions, log options and file patterns. |
250 """ |
247 """ |
251 opt2revset = { |
248 opt2revset = { |
254 'only_merges': ('merge()', None), |
251 'only_merges': ('merge()', None), |
255 'removed': ('removes("*")', None), |
252 'removed': ('removes("*")', None), |
256 'date': ('date(%(val)r)', None), |
253 'date': ('date(%(val)r)', None), |
257 'branch': ('branch(%(val)r)', ' or '), |
254 'branch': ('branch(%(val)r)', ' or '), |
258 '_patslog': ('filelog(%(val)r)', ' or '), |
255 '_patslog': ('filelog(%(val)r)', ' or '), |
|
256 '_patsfollow': ('follow(%(val)r)', ' or '), |
259 'keyword': ('keyword(%(val)r)', ' or '), |
257 'keyword': ('keyword(%(val)r)', ' or '), |
260 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '), |
258 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '), |
261 'user': ('user(%(val)r)', ' or '), |
259 'user': ('user(%(val)r)', ' or '), |
262 'rev': ('%(val)s', ' or '), |
260 'rev': ('%(val)s', ' or '), |
263 } |
261 } |
266 # branch and only_branch are really aliases and must be handled at |
264 # branch and only_branch are really aliases and must be handled at |
267 # the same time |
265 # the same time |
268 if 'branch' in opts and 'only_branch' in opts: |
266 if 'branch' in opts and 'only_branch' in opts: |
269 opts['branch'] = opts['branch'] + opts.pop('only_branch') |
267 opts['branch'] = opts['branch'] + opts.pop('only_branch') |
270 |
268 |
|
269 follow = opts.get('follow') |
|
270 if 'follow' in opts: |
|
271 del opts['follow'] |
271 # pats/include/exclude are passed to match.match() directly in |
272 # pats/include/exclude are passed to match.match() directly in |
272 # _matchfile() revset but walkchangerevs() builds its matcher with |
273 # _matchfile() revset but walkchangerevs() builds its matcher with |
273 # scmutil.match(). The difference is input pats are globbed on |
274 # scmutil.match(). The difference is input pats are globbed on |
274 # platforms without shell expansion (windows). |
275 # platforms without shell expansion (windows). |
275 match, pats = scmutil.matchandpats(repo[None], pats, opts) |
276 pctx = repo[None] |
|
277 match, pats = scmutil.matchandpats(pctx, pats, opts) |
276 slowpath = match.anypats() or (match.files() and opts.get('removed')) |
278 slowpath = match.anypats() or (match.files() and opts.get('removed')) |
277 if not slowpath: |
279 if not slowpath: |
278 for f in match.files(): |
280 for f in match.files(): |
|
281 if follow and f not in pctx: |
|
282 raise util.Abort(_('cannot follow file not in parent ' |
|
283 'revision: "%s"') % f) |
279 filelog = repo.file(f) |
284 filelog = repo.file(f) |
280 if not len(filelog): |
285 if not len(filelog): |
281 # A zero count may be a directory or deleted file, so |
286 # A zero count may be a directory or deleted file, so |
282 # try to find matching entries on the slow path. |
287 # try to find matching entries on the slow path. |
|
288 if follow: |
|
289 raise util.Abort( |
|
290 _('cannot follow nonexistent file: "%s"') % f) |
283 slowpath = True |
291 slowpath = True |
284 if slowpath: |
292 if slowpath: |
285 # See cmdutil.walkchangerevs() slow path. |
293 # See cmdutil.walkchangerevs() slow path. |
286 # |
294 # |
|
295 if follow: |
|
296 raise util.Abort(_('can only follow copies/renames for explicit ' |
|
297 'filenames')) |
287 # pats/include/exclude cannot be represented as separate |
298 # pats/include/exclude cannot be represented as separate |
288 # revset expressions as their filtering logic applies at file |
299 # revset expressions as their filtering logic applies at file |
289 # level. For instance "-I a -X a" matches a revision touching |
300 # level. For instance "-I a -X a" matches a revision touching |
290 # "a" and "b" while "file(a) and not file(b)" does not. |
301 # "a" and "b" while "file(a) and not file(b)" does not. |
291 matchargs = [] |
302 matchargs = [] |
296 for p in opts.get('exclude', []): |
307 for p in opts.get('exclude', []): |
297 matchargs.append('x:' + p) |
308 matchargs.append('x:' + p) |
298 matchargs = ','.join(('%r' % p) for p in matchargs) |
309 matchargs = ','.join(('%r' % p) for p in matchargs) |
299 opts['rev'] = opts.get('rev', []) + ['_matchfiles(%s)' % matchargs] |
310 opts['rev'] = opts.get('rev', []) + ['_matchfiles(%s)' % matchargs] |
300 else: |
311 else: |
301 opts['_patslog'] = list(pats) |
312 if follow: |
|
313 if pats: |
|
314 opts['_patsfollow'] = list(pats) |
|
315 else: |
|
316 opts['follow'] = True |
|
317 else: |
|
318 opts['_patslog'] = list(pats) |
302 |
319 |
303 revset = [] |
320 revset = [] |
304 for op, val in opts.iteritems(): |
321 for op, val in opts.iteritems(): |
305 if not val: |
322 if not val: |
306 continue |
323 continue |