private IChangeInfo Analyze(Change change)
        {
            if (this.IsLibFile(change.Item))
                return null;

            IChangeInfo info = null;

            string url = change.Item.ServerItem;
            Lazy<byte[]> fileContent = new Lazy<byte[]>(() => this.Download(change.Item));

            if (BinaryHelper.IsProbablyBinary(url, fileContent))
                return null;

            if (change.ChangeType.HasFlag(ChangeType.Add)
                || change.ChangeType.HasFlag(ChangeType.Undelete))
            {
                info = new ChangeInfo
                {
                    RemovedLines = 0,
                    AddedLines = this.CountLines(fileContent.Value),
                };
            }
            else if (change.ChangeType.HasFlag(ChangeType.Delete) 
                && !change.ChangeType.HasFlag(ChangeType.SourceRename)
                && !change.ChangeType.HasFlag(ChangeType.Rename))
            {
                info = new ChangeInfo
                {
                    AddedLines = 0,
                    RemovedLines = this.CountLines(fileContent.Value),
                };
            }
            else if (change.ChangeType.HasFlag(ChangeType.Edit) 
                || change.ChangeType.HasFlag(ChangeType.Rollback))
            {
                var others = this.tfs.GetCheckins(url);

                Changeset previous = null;

                if (others.Any())
                {
                    previous = others
                        .SkipWhile(x => x.ChangesetId != change.Item.ChangesetId)
                        .Skip(1)
                        .FirstOrDefault();
                }

                if (previous == null)
                {
                    info = new ChangeInfo
                    {
                        RemovedLines = 0,
                        AddedLines = this.CountLines(fileContent.Value),
                    };
                }
                else
                {
                    Change previousFile = change.ChangeType.HasFlag(ChangeType.Rename) && previous.Changes.Count() == 1
                        ? previous.Changes.Single()
                        : previous.Changes.FirstOrDefault(s => s.Item.ServerItem == url);

                    if (previousFile == null)
                        return null;

                    info = this.DiffFiles(previousFile.Item, change.Item);
                }
            }

            var local = info as ChangeInfo;
            if (local != null)
            {
                local.FileCount = 1;
                local.Name = change.Item.ServerItem;
            }

            return info;
        }
        public IChangeInfo GetDiff(Changeset changeset, bool force)
        {
            string key = changeset.ChangesetId.ToString();

            if (!force)
            {
                IChangeInfo stored = this.changesCache.GetById(key);

                if (stored != null)
                    return stored;
            }

            var infos = new List<IChangeInfo>();

            IEnumerable<Change> changes;

            string[] excludedUrls = this.HasBranchDeletes(changeset);

            if (excludedUrls != null)
            {
                // Exclude deletions of complete branches.
                changes = changeset.Changes.Where(c => excludedUrls.All(e => !c.Item.ServerItem.StartsWith(e))).ToList();

                foreach (var change in changeset.Changes.Except(changes))
                {
                    string changeKey = changeset.ChangesetId.ToString() + change.Item.ItemId;
                    this.changesCache.Remove(changeKey);
                }
            }
            else
            {
                changes = changeset.Changes;
            }

            foreach (Change change in changes)
            {
                IChangeInfo info = this.GetDiffCore(changeset, change, force);

                if (info != null)
                {
                    infos.Add(info);
                }
            }

            var result = new ChangeInfo
            {
                Id = changeset.ChangesetId.ToString(),
                Author = changeset.Committer,
                Name = changeset.ChangesetId.ToString(),
                FileCount = infos.Count,
                AddedLines = infos.Sum(i => i.AddedLines),
                RemovedLines = infos.Sum(i => i.RemovedLines),
            };

            this.changesCache.Save(result);

            return result;
        }