From patchwork Tue Feb 5 23:38:39 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [2, of, 3] dirstate: walk returns None for files under symlink directory From: Durham Goode X-Patchwork-Id: 815 Message-Id: To: mercurial-devel@selenic.com Date: Tue, 05 Feb 2013 15:38:39 -0800 # HG changeset patch # User Durham Goode # Date 1360016835 28800 # Node ID edbb3ea6c81389151eca1f352cbd7265cc47fd42 # Parent f2a1cf2cbb4ac88f922c5bcb5792c2c7c4c236d5 dirstate: walk returns None for files under symlink directory Previously dirstate.walk would return a stat object for files in the dmap that were under a symlink directory. Now it will return None (when the unknown parameter is True) to indicate that they are no longer considered part of the repository. In a situation like this: mkdir foo && touch foo/a && hg commit -Am "a" mv foo bar ln -s bar foo 'hg status' will now show '! foo/a', whereas before it incorrectly considered 'foo/a' to be unchanged. In addition to making 'hg status' report the correct information, this will allow callers to dirstate.walk to not have to detect symlinks themselves, which can be very expensive. diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -710,9 +710,26 @@ # step 3: report unseen items in the dmap hash if not skipstep3 and not exact: visit = sorted([f for f in dmap if f not in results and matchfn(f)]) - nf = iter(visit).next - for st in util.statfiles([join(i) for i in visit]): - results[nf()] = st + if unknown: + # unknown == True means we walked the full directory tree above. + # So if a file is not seen it was either a) not matching matchfn + # b) ignored, c) missing, or d) under a symlink directory. + audit_path = scmutil.pathauditor(self._root) + + for nf in iter(visit): + # Report ignored items in the dmap as long as they are not + # under a symlink directory. + if ignore(nf) and audit_path.isvalidpath(nf): + results[nf] = util.statfiles([join(nf)])[0] + else: + # It's either missing or under a symlink directory + results[nf] = None + else: + # We may not have walked the full directory tree above, + # so stat everything we missed. + nf = iter(visit).next + for st in util.statfiles([join(i) for i in visit]): + results[nf()] = st for s in subrepos: del results[s] del results['.hg'] diff --git a/tests/test-symlinks.t b/tests/test-symlinks.t --- a/tests/test-symlinks.t +++ b/tests/test-symlinks.t @@ -149,6 +149,10 @@ adding foo/a $ mv foo bar $ ln -s bar foo + $ hg status + ! foo/a + ? bar/a + ? foo now addremove should remove old files