# HG changeset patch # User Yuya Nishihara # Date 1599863027 -32400 # Node ID 5f0eeda2005d986d02c702961813170d97f1e399 # Parent 07324227f6b792d16c2146c3c62e6a2ab6cb25a2 log: make -frREV PATH detect missing files before falling back to slow path If -rREV isn't specified, "log --follow" would abort on nonexistent paths. Let's implement this behavior for "-frREV" case as we have ctx.hasdir() now. Otherwise "log -frREV PATH" would silently fall back to slow path and files wouldn't be followed across renames. The loop is quadratic (as before), but the size of the startctxs and match.files() should be small in general. Some tests are marked as BROKEN since file renames aren't tracked in the slow path. This is a known limitation of the current history traversal function. diff -r 07324227f6b7 -r 5f0eeda2005d mercurial/logcmdutil.py --- a/mercurial/logcmdutil.py Fri Sep 11 15:13:35 2020 +0900 +++ b/mercurial/logcmdutil.py Sat Sep 12 07:23:47 2020 +0900 @@ -692,13 +692,27 @@ if not slowpath: follow = opts.get(b'follow') or opts.get(b'follow_first') if follow and opts.get(b'rev'): + # There may be the case that a path doesn't exist in some (but + # not all) of the specified start revisions, but let's consider + # the path is valid. Missing files will be warned by the matcher. startctxs = [repo[r] for r in revs] for f in match.files(): - # No idea if the path was a directory at that revision, so - # take the slow path. - if any(f not in c for c in startctxs): - slowpath = True - break + found = False + for c in startctxs: + if f in c: + found = True + elif c.hasdir(f): + # If a directory exists in any of the start revisions, + # take the slow path. + found = slowpath = True + if not found: + raise error.Abort( + _( + b'cannot follow file not in any of the specified ' + b'revisions: "%s"' + ) + % f + ) elif follow: for f in match.files(): if f not in wctx: diff -r 07324227f6b7 -r 5f0eeda2005d tests/test-log.t --- a/tests/test-log.t Fri Sep 11 15:13:35 2020 +0900 +++ b/tests/test-log.t Sat Sep 12 07:23:47 2020 +0900 @@ -504,14 +504,50 @@ 0 (false !) follow files from the specified revisions with missing patterns -(BROKEN: should follow copies from e@4) $ hg log -T '{rev}\n' -fr4 e x - 4 - 2 (false !) + abort: cannot follow file not in any of the specified revisions: "x" + [255] + +follow files from the specified revisions with directory patterns +(BROKEN: should follow copies from dir/b@2) + + $ hg log -T '{rev}\n' -fr2 dir/b dir + 2 1 (false !) 0 (false !) +follow files from multiple revisions, but the pattern is missing in +one of the specified revisions + + $ hg log -T '{rev}\n' -fr'2+4' dir/b e + e: no such file in rev f8954cd4dc1f + dir/b: no such file in rev 7e4639b4691b + 4 + 2 + 1 + 0 + +follow files from multiple revisions, and the pattern matches a file in +one revision but matches a directory in another: +(BROKEN: should follow copies from dir/b@2 and dir/b/g@5) +(BROKEN: the revision 4 should not be included since dir/b/g@5 is unchanged) + + $ mkdir -p dir/b + $ hg mv g dir/b + $ hg ci -m 'make dir/b a directory' + + $ hg log -T '{rev}\n' -fr'2+5' dir/b + 5 + 4 + 3 (false !) + 2 + 1 (false !) + 0 (false !) + + $ hg --config extensions.strip= strip -r. --no-backup + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + follow files from the specified revisions across copies with -p/--patch $ hg log -T '== rev: {rev},{file_copies % " {source}->{name}"} ==\n' -fpr 4 e g @@ -2312,7 +2348,15 @@ $ hg log -T '== {rev} ==\n' -fr'wdir()' --git --stat notfound - notfound: $ENOENT$ + abort: cannot follow file not in any of the specified revisions: "notfound" + [255] + +follow files from wdir and non-wdir revision: + + $ hg log -T '{rev}\n' -fr'wdir()+.' f1-copy + f1-copy: no such file in rev 65624cd9070a + 2147483647 + 0 follow added/removed files from wdir parent