/// <summary>
        /// finds out the path and revision of all diffs needed for this search
        /// </summary>
        /// <param name="filesOfInterest"></param>
        /// <param name="searchOptions"></param>
        /// <param name="diffInfoFileRelations"></param>
        /// <returns></returns>
        public IList <DiffInfo> GetDiffInfos(ICollection <string> filesOfInterest, SearchOptions searchOptions, out IDictionary <DiffInfo, IList <string> > diffInfoFileRelations)
        {
            diffInfoFileRelations = new Dictionary <DiffInfo, IList <string> >();

            if (this.Disposed)
            {
                Progress.DebugLog("GetDiffInfos called on closed SubversionSearcher");
                return(null);
            }

            long?startRevisionNumber = null;
            long?endRevisionNumber   = null;

            if (searchOptions.SearchFromRevision.RevisionType == SvnRevisionType.Number)
            {
                startRevisionNumber = searchOptions.SearchFromRevision.Revision;
            }
            else if (searchOptions.SearchFromRevision.RevisionType == SvnRevisionType.Time)
            {
                for (int i = 0; i < Revisions.Count; i++)
                {
                    if (Revisions[i].Time >= searchOptions.SearchFromRevision.Time)
                    {
                        startRevisionNumber = Revisions[i].Revision;
                        break;
                    }
                }
            }

            if (searchOptions.SearchToRevision.RevisionType == SvnRevisionType.Number)
            {
                endRevisionNumber = searchOptions.SearchToRevision.Revision;
            }
            else if (searchOptions.SearchToRevision.RevisionType == SvnRevisionType.Time)
            {
                for (int i = Revisions.Count - 1; i >= 0; i--)
                {
                    if (Revisions[i].Time <= searchOptions.SearchToRevision.Time)
                    {
                        endRevisionNumber = Revisions[i].Revision;
                        break;
                    }
                }
            }
            else if (searchOptions.SearchToRevision.RevisionType == SvnRevisionType.Head)
            {
                endRevisionNumber = HeadRevision;
            }


            if (!startRevisionNumber.HasValue || !endRevisionNumber.HasValue)
            {
                Progress.DebugLog("startRevision or endRevision number is null");
                Progress.Log("something failed");
                return(null);
            }


            HashSet <long>   ignoreRevisions = new HashSet <long>();
            HashSet <string> authors         = new HashSet <string>(from a in searchOptions.Authors where !String.IsNullOrWhiteSpace(a) select a.ToUpper());

            if (authors.Count != 0)
            {
                foreach (CommitInfo r in Revisions.Values)
                {
                    if (!((r.Author != null && searchOptions.Authors.Contains(r.Author)) ^ searchOptions.ExcludeAuthors))
                    {
                        ignoreRevisions.Add(r.Revision);
                    }
                }
            }


            // adding the files that were selected in the checkbox tree
            foreach (string fname in filesOfInterest)
            {
                foreach (DiffInfo di in GetFileHistory(fname, startRevisionNumber.Value, endRevisionNumber.Value, ignoreRevisions, searchOptions.StopOnCopy))
                {
                    if (diffInfoFileRelations.TryGetValue(di, out IList <string> li) == false)
                    {
                        diffInfoFileRelations[di] = li = new List <string>();
                    }
                    li.Add(fname);
                }
            }


            // add files that contain substring in name
            if (!String.IsNullOrEmpty(searchOptions.FilenameSubstring))
            {
                foreach (KeyValuePair <string, IList <NodeAtTime> > kv in nodes)
                {
                    if (searchOptions.HeadNodePath != "" && !kv.Key.StartsWith(searchOptions.HeadNodePath + '/'))
                    {
                        continue;
                    }

                    var stringInFname = searchOptions.FilenameSubstring.ToUpperInvariant();

                    if ((searchOptions.FilenameSubstring == "*" && !FiletypeBlacklist.Contains(Path.GetExtension(kv.Key).ToLower())) || kv.Key.ToUpperInvariant().Contains(stringInFname))
                    {
                        foreach (NodeAtTime n in kv.Value)
                        {
                            foreach (DiffInfo di in GetFileHistory(n, startRevisionNumber.Value, endRevisionNumber.Value, ignoreRevisions, true))
                            {
                                IList <string> li;
                                if (diffInfoFileRelations.TryGetValue(di, out li) == false)
                                {
                                    diffInfoFileRelations[di] = li = new List <string>();
                                }
                                if (!li.Contains(kv.Key))
                                {
                                    li.Add(kv.Key);
                                }
                            }
                        }
                    }
                }
            }

            Utils.Vardump <DiffInfo>("relevantDiffs.txt", diffInfoFileRelations.Keys, (di) =>
            {
                string result = String.Format("r{0,5} {1}", di.RevB, di.Path);
                return(result);
            });

            return(diffInfoFileRelations.Keys.ToList());
        }
        // --

        private void BuildRepositoryStructure(IList <CommitInfo> revs, IList <SimplifiedSvnChangeItem> changeItems)
        {
            Revisions = new Dictionary <long, CommitInfo>();
            nodes     = new Dictionary <string, IList <NodeAtTime> >();
            equalAdds = new Dictionary <NodeAtTime, ISet <NodeAtTime> >();
            table     = new Table(this);

            nodes.Add("", new List <NodeAtTime> {
                new NodeAtTime("", 0)
            });

            foreach (var e in revs)
            {
                Revisions.Add(e.Revision, e);
            }

            if (revs.Count > 0)
            {
                HeadRevision = revs[revs.Count - 1].Revision;
            }

            Stopwatch sw = Stopwatch.StartNew();

            long          currentRevsion          = 0;
            ISet <string> filesCopiedThisRevision = new HashSet <string>();

            foreach (var changeItem in changeItems)
            {
                if (changeItem.Revision != currentRevsion)
                {
                    currentRevsion = changeItem.Revision;
                    if (changeItem.Revision == revs.Count - 1 || changeItem.Revision % 17 == 0)
                    {
                        Progress.Log("Building repository structure ({0} of {1})", changeItem.Revision, revs.Count - 1);
                    }
                    filesCopiedThisRevision = new HashSet <string>();
                }

                string nodePath = changeItem.Path.Substring(1);

                if (changeItem.Action == SvnChangeAction.Modify)
                {
                    AddModification(nodePath, currentRevsion, filesCopiedThisRevision.Contains(nodePath));
                }
                else if (changeItem.Action == SvnChangeAction.Add)
                {
                    AddAddition(nodePath, changeItem, currentRevsion, ref filesCopiedThisRevision);
                    // add to same files
                }
                else if (changeItem.Action == SvnChangeAction.Delete)
                {
                    AddDeletion(nodePath, changeItem, currentRevsion);
                }
                else if (changeItem.Action == SvnChangeAction.Replace)
                {
                    // investigate replace without copying
                    AddDeletion(nodePath, changeItem, currentRevsion);
                    AddAddition(nodePath, changeItem, currentRevsion, ref filesCopiedThisRevision);
                    // add to same files
                }
                else
                {
                    Progress.DebugLog("{0} occured at {1}@{2}", changeItem.Action, nodePath, currentRevsion);
                }
            }


            {
                IList <string> listsToRemove = new List <string>();
                // remove nodes with deleteRevision == addRevision, remove nodesPaths with no nodes, add to table
                foreach (var kv in nodes)
                {
                    IList <NodeAtTime> natsToRemove = null;

                    foreach (var nat in kv.Value)
                    {
                        if (nat.DeleteRevision.HasValue && nat.DeleteRevision == nat.AddRevision)
                        {
                            // happens when replaces happen
                            if (natsToRemove == null)
                            {
                                natsToRemove = new List <NodeAtTime>();
                            }
                            natsToRemove.Add(nat);
                            continue;
                        }

                        if (nat.IsFolder)
                        {
                            continue;
                        }
                        else if (NodesTaggedAsFolder.Count > 0 && NodesTaggedAsFolder.Contains(new Tuple <string, long>(nat.Path, nat.AddRevision)))
                        {
                            nat.SetIsFolder();
                        }
                        else
                        {
                            bool isBinary = FiletypeBlacklist.Contains(Path.GetExtension(nat.Path));

                            for (int i = nat.Actions.Count - 1; i > 0; i--)
                            {
                                table.AddToDict(nat.Actions[i].Revision, kv.Key, isBinary);
                            }

                            if (nat.Actions[0].Modified)
                            {
                                table.AddToDict(nat.Actions[0].Revision, kv.Key, isBinary);
                            }
                            else
                            {
                                table.AddToDict(nat.Actions[0].Revision, kv.Key, isBinary);
                            }
                            if (nat.DeleteRevision.HasValue)
                            {
                                table.AddToDict(nat.DeleteRevision.Value, kv.Key, isBinary);
                            }
                        }
                    }

                    if (natsToRemove != null)
                    {
                        foreach (var n in natsToRemove)
                        {
                            kv.Value.Remove(n);
                        }
                    }

                    if (kv.Value.Count == 0)
                    {
                        listsToRemove.Add(kv.Key);
                    }
                }

                foreach (var v in listsToRemove)
                {
                    nodes.Remove(v);
                }
            }

            {
                IList <NodeAtTime> toRemove = new List <NodeAtTime>();
                // clear all the equalsAdds entries who's Set contain only one entry
                foreach (var kv in equalAdds)
                {
                    if (kv.Value.Count <= 1)
                    {
                        toRemove.Add(kv.Key);
                    }
                    else
                    {
                        foreach (var s in kv.Value)
                        {
                            if (s.IsFolder)
                            {
                                // if one is a folder, all of them are
                                toRemove.Add(kv.Key);
                                break;
                            }
                        }
                    }
                }
                foreach (var v in toRemove)
                {
                    equalAdds.Remove(v);
                }
            }

            sw.Stop();
            Progress.DebugLog("repository structure building time: {0}ms", sw.Elapsed.TotalMilliseconds);
        }