private FixedFilterTuple BuildFilter(string fileName)
        {
            if (string.IsNullOrEmpty(fileName))
                return null;

            //Replace windows path separator to Linux path separator.
            //This is needed to keep the file history working when started from file tree in
            //browse dialog.
            fileName = fileName.ToPosixPath();

            // we will need this later to look up proper casing for the file
            var fullFilePath = Path.Combine(Module.WorkingDir, fileName);

            //The section below contains native windows (kernel32) calls
            //and breaks on Linux. Only use it on Windows. Casing is only
            //a Windows problem anyway.
            if (EnvUtils.RunningOnWindows() && File.Exists(fullFilePath))
            {
                // grab the 8.3 file path
                var shortPath = new StringBuilder(4096);
                NativeMethods.GetShortPathName(fullFilePath, shortPath, shortPath.Capacity);

                // use 8.3 file path to get properly cased full file path
                var longPath = new StringBuilder(4096);
                NativeMethods.GetLongPathName(shortPath.ToString(), longPath, longPath.Capacity);

                // remove the working directory and now we have a properly cased file name.
                fileName = longPath.ToString().Substring(Module.WorkingDir.Length);
            }

            if (fileName.StartsWith(Module.WorkingDir, StringComparison.InvariantCultureIgnoreCase))
                fileName = fileName.Substring(Module.WorkingDir.Length);

            FileName = fileName;

            FixedFilterTuple res = new FixedFilterTuple();
            if (AppSettings.FollowRenamesInFileHistory && !Directory.Exists(fullFilePath))
            {
                // git log --follow is not working as expected (see  http://kerneltrap.org/mailarchive/git/2009/1/30/4856404/thread)
                FollowParentRewriter hrw = new FollowParentRewriter(fileName, delegate(string arg){
                    Process p = Module.RunGitCmdDetached(arg);
                    return p.StandardOutput;
                });
                // here we need --name-only to get the previous filenames in the revision graph
                if (hrw.RewriteNecessary)
                {
                    res.Rewriter = hrw;
                    res.RevisionFilter = " " + GitCommandHelpers.FindRenamesAndCopiesOpts() + " --name-only --follow";
                }
                else
                {
                    res.RevisionFilter = " " + GitCommandHelpers.FindRenamesAndCopiesOpts() + " --name-only --parents";
                }
            }
            else if (AppSettings.FollowRenamesInFileHistory)
            {
                // history of a directory
                // --parents doesn't work with --follow enabled, but needed to graph a filtered log
                res.RevisionFilter = " " + GitCommandHelpers.FindRenamesOpt() + " --follow --parents";
            }
            else
            {
                // rename following disabled
                res.RevisionFilter = " --parents";
            }

            if (AppSettings.FullHistoryInFileHistory)
            {
                res.RevisionFilter = string.Concat(" --full-history ", res.RevisionFilter);
            }

            res.PathFilter = " \"" + fileName + "\"";

            return res;
        }
 private void CheckFlush(FollowParentRewriter rw, bool flushAll, GitRevision[] expected)
 {
     GitRevisionExpectation x = new GitRevisionExpectation(expected);
     rw.Flush(flushAll, x.ProcessRevision);
     x.CheckCalledEnoughTimes();
 }
        public void RewriterTestCompleteGraph()
        {
            string dataFollow =
                "dd/aa.txt\n"+
                "\n"+
                "dd/aa.txt\n"+
                "\n"+
                "dd/aa.txt\n"+
                "\n"+
                "aa.txt\n"+
                "\n"+
                "aa.txt\n"+
                "\n"+
                "aa.txt\n"+
                "\n"+
                "aa.txt\n"+
                "\n"+
                "aa.txt\n";
            string dataGraph =
                "bef6694d0b1a58243d817cc3e915952f53269b40 5632365bb31f2da1aae700f47e1a50951754f9ff\n" +
                "5632365bb31f2da1aae700f47e1a50951754f9ff 9eea2112f54967a17ae0f02fb096a4bc41200846 5744afa2ee91a44eb4393220b19aa99a66459a3f\n" +
                "9eea2112f54967a17ae0f02fb096a4bc41200846 2cfff1e08548361ca16c3c05bb4407dc3a190ff7\n" +
                "2cfff1e08548361ca16c3c05bb4407dc3a190ff7 5744afa2ee91a44eb4393220b19aa99a66459a3f\n" +
                "5744afa2ee91a44eb4393220b19aa99a66459a3f e12f41482b55a19a8cf27ba243855f382c277cbb\n" +
                "e12f41482b55a19a8cf27ba243855f382c277cbb 02612100c26a72840edbcd971dd3310b6e3f499e\n" +
                "02612100c26a72840edbcd971dd3310b6e3f499e\n";

            FollowParentRewriter rw = new FollowParentRewriter("dd/aa.txt", StaticReader(dataFollow, dataGraph));
            rw.RewriteNecessary.Should().BeTrue();
            rw.PushRevision(null);
            // 02_e1_57_2c_9e_56_be
            //         \_____/
            CheckFlush(rw, false, new GitRevision[] { null });
            //
            rw.PushRevision(MkGitRevision("bef6694d0b1a58243d817cc3e915952f53269b40", "5632365bb31f2da1aae700f47e1a50951754f9ff"));
            CheckFlush(rw, false, new GitRevision[] { });
            // 
            rw.PushRevision(MkGitRevision("5632365bb31f2da1aae700f47e1a50951754f9ff", "9eea2112f54967a17ae0f02fb096a4bc41200846", "5744afa2ee91a44eb4393220b19aa99a66459a3f"));
            CheckFlush(rw, false, new GitRevision[] { MkGitRevision("bef6694d0b1a58243d817cc3e915952f53269b40", "5632365bb31f2da1aae700f47e1a50951754f9ff") });
            //
            rw.PushRevision(MkGitRevision("9eea2112f54967a17ae0f02fb096a4bc41200846", "2cfff1e08548361ca16c3c05bb4407dc3a190ff7"));
            CheckFlush(rw, false, new GitRevision[] { });
            rw.PushRevision(MkGitRevision("2cfff1e08548361ca16c3c05bb4407dc3a190ff7", "5744afa2ee91a44eb4393220b19aa99a66459a3f"));
            CheckFlush(rw, false, new GitRevision[] { });
            //
            rw.PushRevision(MkGitRevision("5744afa2ee91a44eb4393220b19aa99a66459a3f", "e12f41482b55a19a8cf27ba243855f382c277cbb"));
            CheckFlush(rw, false, new GitRevision[] { 
                MkGitRevision("5632365bb31f2da1aae700f47e1a50951754f9ff", "9eea2112f54967a17ae0f02fb096a4bc41200846", "5744afa2ee91a44eb4393220b19aa99a66459a3f"),
                MkGitRevision("9eea2112f54967a17ae0f02fb096a4bc41200846", "2cfff1e08548361ca16c3c05bb4407dc3a190ff7"),
                MkGitRevision("2cfff1e08548361ca16c3c05bb4407dc3a190ff7", "5744afa2ee91a44eb4393220b19aa99a66459a3f")
            });
            //
            rw.PushRevision(MkGitRevision("e12f41482b55a19a8cf27ba243855f382c277cbb", "02612100c26a72840edbcd971dd3310b6e3f499e"));
            CheckFlush(rw, false, new GitRevision[] { 
                MkGitRevision("5744afa2ee91a44eb4393220b19aa99a66459a3f", "e12f41482b55a19a8cf27ba243855f382c277cbb")
            });
            //
            CheckFlush(rw, true, new GitRevision[] { 
                MkGitRevision("e12f41482b55a19a8cf27ba243855f382c277cbb", "02612100c26a72840edbcd971dd3310b6e3f499e")
            });
        }
        public void RewriterTestSingleFileName()
        {
            string dataFollow =
                "dd/aa.txt\n" +
                "\n";
            string dataGraph = null;

            FollowParentRewriter rw = new FollowParentRewriter("dd/aa.txt", StaticReader(dataFollow, dataGraph));
            rw.RewriteNecessary.Should().BeFalse();
        }