예제 #1
0
        protected void CompareManifestsRecursiveSource(
            ManifestDirectoryInfo sourceDir,
            ManifestDirectoryInfo destDir,
            HashSet <ManifestFileInfo> destFileMatch)
        {
            foreach (ManifestFileInfo sourceFile in sourceDir.Files.Values)
            {
                if (destDir != null &&
                    destDir.Files.ContainsKey(sourceFile.Name))
                {
                    ManifestFileInfo destFile = destDir.Files[sourceFile.Name];
                    destFileMatch.Add(destFile);

                    if (sourceFile.FileHash.Equals(destFile.FileHash) == false)
                    {
                        ChangedFiles.Add(sourceFile, destFile);
                    }
                    else
                    {
                        if (Manifest.CompareManifestDates(
                                sourceFile.LastModifiedUtc,
                                destFile.LastModifiedUtc) == false)
                        {
                            LastModifiedDateFiles.Add(sourceFile, destFile);
                        }

                        if (Manifest.CompareManifestDates(
                                sourceFile.RegisteredUtc,
                                destFile.RegisteredUtc) == false)
                        {
                            RegisteredDateFiles.Add(sourceFile, destFile);
                        }
                    }
                }
                else
                {
                    SourceOnlyFiles.Add(sourceFile);
                }
            }

            foreach (ManifestDirectoryInfo nextSourceDir in
                     sourceDir.Subdirectories.Values)
            {
                ManifestDirectoryInfo nextDestDir = null;
                if (destDir != null &&
                    destDir.Subdirectories.ContainsKey(nextSourceDir.Name))
                {
                    nextDestDir = destDir.Subdirectories[nextSourceDir.Name];
                }

                CompareManifestsRecursiveSource(
                    nextSourceDir,
                    nextDestDir,
                    destFileMatch);
            }
        }
예제 #2
0
 public void Clear()
 {
     SourceOnlyFiles.Clear();
     DestOnlyFiles.Clear();
     ChangedFiles.Clear();
     LastModifiedDateFiles.Clear();
     RegisteredDateFiles.Clear();
     MovedFiles.Clear();
     MovedFileOrder.Clear();
     ErrorFiles.Clear();
 }
예제 #3
0
        public GitFileStatus GetFileStatus(string fileName)
        {
            var file = ChangedFiles.Where(f => string.Compare(f.FileName, fileName, true) == 0).FirstOrDefault();

            if (file != null)
            {
                return(file.Status);
            }
            if (FileExistsInRepo(fileName))
            {
                return(GitFileStatus.Tracked);
            }
            // did not check if the file is ignored for performance reason
            return(GitFileStatus.NotControlled);
        }
예제 #4
0
        public void Clear()
        {
            FileCheckedCount = 0;

            NewFiles.Clear();
            NewFilesForGroom.Clear();
            ChangedFiles.Clear();
            MissingFiles.Clear();
            LastModifiedDateFiles.Clear();
            ErrorFiles.Clear();
            IgnoredFiles.Clear();
            NewlyIgnoredFiles.Clear();
            IgnoredFilesForGroom.Clear();
            MovedFiles.Clear();
            MovedFileOrder.Clear();
            DuplicateFiles.Clear();
        }
        private static PredictionRequest BuildRequest(ChangedFiles changedFiles)
        {
            var request = new PredictionRequest();

            foreach (var loopFile in changedFiles.Files)
            {
                request.Items.Add(new PredictionRequestFile
                {
                    Author  = loopFile.Author,
                    Path    = loopFile.Path,
                    OldPath = loopFile.OldPath,
                    NumberOfModifiedLines = loopFile.NumberOfModifiedLines,
                    CCMMd  = loopFile.CCMMd,
                    CCMAvg = loopFile.CCMAvg,
                    CCMMax = loopFile.CCMMax
                });
            }

            return(request);
        }
예제 #6
0
        /// <summary>
        /// Add a new fileinfo to the list of changed files.
        /// </summary>
        /// <param name="change"></param>
        internal void AddNewChange(ChangedFile change)
        {
            if (Application.Current == null)
            {
                return;
            }

            if (Application.Current.Dispatcher.CheckAccess())
            {
                if (!ChangedFiles.Contains(change))
                {
                    ChangedFiles.Add(change);
                }
            }
            else
            {
                AddChangeDelegate del = new AddChangeDelegate(AddNewChange);
                Application.Current.Dispatcher.Invoke(del, new object[] { change });
            }
        }
예제 #7
0
        internal void AddNewChangeSet(IEnumerable <ChangedFile> changes)
        {
            if (Application.Current == null)
            {
                return;
            }

            if (Application.Current.Dispatcher.CheckAccess())
            {
                foreach (var change in changes)
                {
                    if (!ChangedFiles.Contains(change))
                    {
                        ChangedFiles.Add(change);
                    }
                }
            }
            else
            {
                AddChangesDelegate del = new AddChangesDelegate(AddNewChangeSet);
                Application.Current.Dispatcher.Invoke(del, new object[] { changes });
            }
        }
        public GitFileStatus GetFileStatus(string fileName)
        {
            try
            {
                fileName = Path.GetFullPath(fileName);
                var file = ChangedFiles.FirstOrDefault(f => string.Equals(f.FilePath, fileName, StringComparison.OrdinalIgnoreCase));
                if (file != null)
                {
                    return(file.Status);
                }

                if (FileExistsInRepo(fileName))
                {
                    return(GitFileStatus.Tracked);
                }
                // did not check if the file is ignored for performance reason
                return(GitFileStatus.NotControlled);
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Error In File System Changed Event: " + ex.Message);
                return(GitFileStatus.NotControlled);
            }
        }
        private static ChangedFiles GetChangedFiles(string repositoryPath, IEnumerable <string> filePaths)
        {
            try
            {
                using (var repository = new Repository(repositoryPath))
                {
                    string userName = repository.Config.GetValueOrDefault("user.name", string.Empty);

                    var status = repository.RetrieveStatus(
                        new StatusOptions
                    {
                        DetectRenamesInIndex   = true,
                        DetectRenamesInWorkDir = true,
                        DisablePathSpecMatch   = true,
                        ExcludeSubmodules      = true,
                        IncludeUnaltered       = true,
                        Show = StatusShowOption.IndexAndWorkDir,
                    });

                    var statusEntries = status.Where(g => g.State.HasFlag(FileStatus.ModifiedInWorkdir) || g.State.HasFlag(FileStatus.RenamedInWorkdir) || g.State.HasFlag(FileStatus.DeletedFromWorkdir)).ToList();

                    var result = new ChangedFiles();

                    if (filePaths != null)
                    {
                        statusEntries = statusEntries
                                        .Where(g => filePaths.Any(path => path.IndexOf(g.FilePath, StringComparison.OrdinalIgnoreCase) > -1))
                                        .ToList();
                    }

                    if (!statusEntries.Any())
                    {
                        return(result);
                    }

                    var changesLookup = repository.Diff.Compare <Patch>(statusEntries.Select(g => g.FilePath), true)
                                        .ToDictionary(g => g.Path, g => g);

                    foreach (var loopEntry in statusEntries)
                    {
                        string filePath = loopEntry.FilePath;

                        PatchEntryChanges patchEntryChanges;
                        if (!changesLookup.TryGetValue(filePath, out patchEntryChanges))
                        {
                            continue;
                        }

                        string oldFilePath = filePath;
                        if (loopEntry.IndexToWorkDirRenameDetails != null)
                        {
                            oldFilePath = loopEntry.IndexToWorkDirRenameDetails.OldFilePath;
                        }

                        CyclomaticComplexityMetric metric = null;
                        if (CyclomaticComplexityHelper.IsSupportedFile(filePath))
                        {
                            var fullFilePath = Path.Combine(repositoryPath, filePath);
                            if (File.Exists(fullFilePath))
                            {
                                metric = CyclomaticComplexityHelper.CalculateMetric(
                                    File.OpenRead(fullFilePath),
                                    filePath,
                                    true);
                            }
                        }

                        result.Files.Add(new ChangedFile
                        {
                            Author  = userName,
                            Path    = filePath,
                            OldPath = oldFilePath,
                            NumberOfModifiedLines = patchEntryChanges.LinesAdded + patchEntryChanges.LinesDeleted,
                            CCMMd  = metric?.CCMMd,
                            CCMAvg = metric?.CCMAvg,
                            CCMMax = metric?.CCMMax
                        });
                    }

                    return(result);
                }
            }
            catch (RepositoryNotFoundException)
            {
                return(null);
            }
        }
예제 #10
0
        protected void UpdateRecursive(
            DirectoryInfo currentDirectoryInfo,
            ManifestDirectoryInfo currentManfestDirInfo)
        {
            // Setup data for current directory
            Dictionary <String, FileInfo> fileDict =
                new Dictionary <string, FileInfo>();

            Dictionary <String, DirectoryInfo> dirDict =
                new Dictionary <string, DirectoryInfo>();

            if (currentDirectoryInfo != null)
            {
                FileInfo[] fileList = null;
                try
                {
                    fileList = currentDirectoryInfo.GetFiles();
                }
                catch (Exception)
                {
                    WriteLine(Manifest.MakeStandardPathString(
                                  currentManfestDirInfo));

                    if (IgnoreFile(Manifest.MakeStandardPathString(
                                       currentManfestDirInfo)) == true)
                    {
                        // This was implemented primarily to allow the user to
                        // silence the process of skipping over inaccessible
                        // system directories by ignoring them.  For example,
                        // in some cases the "$RECYCLE BIN" under Windows
                        // is not accessible and will generate an error.  The
                        // user can now add such directories to the ignore list
                        // and they will be silently ignored.  The special
                        // message for showProgress alerts the user that the
                        // directory is actually being skipped altogether
                        // since it can't be accessed.  The only significant
                        // implication of this is that the ignored files won't
                        // be enumerated and counted as being ignored.
                        if (ShowProgress)
                        {
                            WriteLine(
                                Manifest.MakeStandardPathString(currentManfestDirInfo) +
                                " [IGNORED DIRECTORY AND CANNOT ACCESS]");
                        }
                    }
                    else
                    {
                        ForceWriteLine("Could not access contents of: " +
                                       currentDirectoryInfo.FullName);
                    }

                    return;
                }

                foreach (FileInfo nextFileInfo in fileList)
                {
                    fileDict.Add(nextFileInfo.Name.Normalize(), nextFileInfo);
                }

                DirectoryInfo[] dirList =
                    currentDirectoryInfo.GetDirectories();

                foreach (DirectoryInfo nextDirInfo in dirList)
                {
                    dirDict.Add(nextDirInfo.Name.Normalize(), nextDirInfo);
                }
            }

            // Clone in case we modify during iteration
            List <ManifestFileInfo> fileListClone =
                new List <ManifestFileInfo>(currentManfestDirInfo.Files.Values);

            // Iterate through existing manifest entries
            foreach (ManifestFileInfo nextManFileInfo in fileListClone)
            {
                if (ShowProgress)
                {
                    Write(Manifest.MakeStandardPathString(nextManFileInfo));
                }

                if (fileDict.ContainsKey(nextManFileInfo.Name))
                {
                    FileCheckedCount++;

                    FileInfo nextFileInfo = fileDict[nextManFileInfo.Name];

                    if (IgnoreFile(Manifest.MakeStandardPathString(nextManFileInfo)))
                    {
                        Write(" [NEWLY IGNORED]");

                        currentManfestDirInfo.Files.Remove(
                            nextManFileInfo.Name);

                        NewlyIgnoredFiles.Add(nextManFileInfo);
                    }
                    else if (nextFileInfo.Length != nextManFileInfo.FileLength &&
                             Update == false &&
                             AlwaysCheckHash == false)
                    {
                        // Don't compute hash if we aren't doing an update
                        Write(" [DIFFERENT]");
                        ChangedFiles.Add(nextManFileInfo);
                    }
                    else if (AlwaysCheckHash == true ||
                             MakeNewHash == true ||
                             nextManFileInfo.FileHash == null ||
                             Manifest.CompareManifestDateToFilesystemDate(nextFileInfo.LastWriteTimeUtc, nextManFileInfo.LastModifiedUtc) == false ||
                             nextFileInfo.Length != nextManFileInfo.FileLength)
                    {
                        FileHash checkHash = null;

                        Exception exception = null;
                        try
                        {
                            string hashType = Manifest.DefaultHashMethod;
                            if (nextManFileInfo.FileHash != null)
                            {
                                hashType = nextManFileInfo.FileHash.HashType;
                            }

                            checkHash = FileHash.ComputeHash(
                                nextFileInfo,
                                hashType);
                        }
                        catch (Exception ex)
                        {
                            exception = ex;
                        }

                        if (exception != null)
                        {
                            WriteLine(" [ERROR]");
                            WriteLine(exception.ToString());

                            ErrorFiles.Add(nextManFileInfo);
                        }
                        else
                        {
                            if (nextManFileInfo.FileHash == null)
                            {
                                Write(" [NULL HASH IN MANIFEST]");
                                ChangedFiles.Add(nextManFileInfo);
                            }
                            else if (checkHash.Equals(nextManFileInfo.FileHash) == false)
                            {
                                Write(" [DIFFERENT]");
                                ChangedFiles.Add(nextManFileInfo);
                            }
                            else
                            {
                                if (Manifest.CompareManifestDateToFilesystemDate(
                                        nextFileInfo.LastWriteTimeUtc,
                                        nextManFileInfo.LastModifiedUtc) == false)
                                {
                                    Write(" [LAST MODIFIED DATE]");
                                    LastModifiedDateFiles.Add(nextManFileInfo);

                                    if (BackDate == true)
                                    {
                                        nextFileInfo.LastWriteTimeUtc =
                                            nextManFileInfo.LastModifiedUtc;
                                    }
                                }
                            }
                        }

                        FileHash newHash = checkHash;
                        if (MakeNewHash)
                        {
                            newHash = FileHash.ComputeHash(
                                nextFileInfo,
                                GetNewHashType(Manifest));
                        }

                        // Update hash and last modified date accordingly
                        nextManFileInfo.FileHash = newHash;

                        nextManFileInfo.LastModifiedUtc = nextFileInfo.LastWriteTimeUtc;
                        nextManFileInfo.FileLength      = nextFileInfo.Length;
                    }
                    else
                    {
                        Write(" [SKIPPED]");
                    }
                }
                else
                {
                    Write(" [MISSING]");
                    currentManfestDirInfo.Files.Remove(nextManFileInfo.Name);
                    MissingFiles.Add(nextManFileInfo);
                }

                WriteLine("");
            }

            // Clone in case we modify during iteration
            List <ManifestDirectoryInfo> directoryListClone =
                new List <ManifestDirectoryInfo>(
                    currentManfestDirInfo.Subdirectories.Values);

            foreach (ManifestDirectoryInfo nextManDirInfo in
                     directoryListClone)
            {
                DirectoryInfo nextDirInfo = null;
                if (dirDict.ContainsKey(nextManDirInfo.Name))
                {
                    nextDirInfo = dirDict[nextManDirInfo.Name];
                }

                UpdateRecursive(
                    nextDirInfo,
                    nextManDirInfo);

                if (nextManDirInfo.Empty)
                {
                    currentManfestDirInfo.Subdirectories.Remove(
                        nextManDirInfo.Name);
                }
            }

            // Look for new files
            foreach (String nextFileName in fileDict.Keys)
            {
                FileInfo nextFileInfo = fileDict[nextFileName];

                if (currentManfestDirInfo.Files.ContainsKey(
                        nextFileName) == false)
                {
                    ManifestFileInfo newManFileInfo =
                        new ManifestFileInfo(
                            nextFileName,
                            currentManfestDirInfo);

                    Write(Manifest.MakeStandardPathString(newManFileInfo));

                    if (IgnoreFile(Manifest.MakeStandardPathString(newManFileInfo)))
                    {
                        IgnoredFiles.Add(newManFileInfo);

                        // Don't groom the manifest file!
                        if (Manifest.MakeNativePathString(newManFileInfo) !=
                            ManifestNativeFilePath)
                        {
                            IgnoredFilesForGroom.Add(nextFileInfo);
                        }

                        Write(" [IGNORED]");
                    }
                    else
                    {
                        FileCheckedCount++;

                        bool checkHash = false;
                        if (Update == true ||
                            AlwaysCheckHash == true ||
                            TrackMoves == true)
                        {
                            checkHash = true;
                        }


                        Exception exception = null;
                        if (checkHash)
                        {
                            try
                            {
                                newManFileInfo.FileHash =
                                    FileHash.ComputeHash(
                                        nextFileInfo,
                                        GetNewHashType(Manifest));
                            }
                            catch (Exception ex)
                            {
                                exception = ex;
                            }
                        }

                        if (checkHash && newManFileInfo.FileHash == null)
                        {
                            ErrorFiles.Add(newManFileInfo);

                            WriteLine(" [ERROR]");
                            WriteLine(exception.ToString());
                        }
                        else
                        {
                            NewFiles.Add(newManFileInfo);
                            NewFilesForGroom.Add(nextFileInfo);
                            Write(" [NEW]");
                        }

                        newManFileInfo.FileLength =
                            nextFileInfo.Length;

                        newManFileInfo.LastModifiedUtc =
                            nextFileInfo.LastWriteTimeUtc;

                        newManFileInfo.RegisteredUtc =
                            DateTime.Now.ToUniversalTime();

                        currentManfestDirInfo.Files.Add(
                            nextFileName,
                            newManFileInfo);
                    }

                    WriteLine("");
                }
            }

            // Recurse looking for new directories
            foreach (String nextDirName in dirDict.Keys)
            {
                DirectoryInfo nextDirInfo = dirDict[nextDirName];

                if (currentManfestDirInfo.Subdirectories.ContainsKey(
                        nextDirName) == false)
                {
                    ManifestDirectoryInfo nextManDirInfo =
                        new ManifestDirectoryInfo(
                            nextDirName,
                            currentManfestDirInfo);

                    currentManfestDirInfo.Subdirectories.Add(
                        nextDirName,
                        nextManDirInfo);

                    UpdateRecursive(
                        nextDirInfo,
                        nextManDirInfo);

                    if (nextManDirInfo.Empty)
                    {
                        currentManfestDirInfo.Subdirectories.Remove(
                            nextDirName);
                    }
                }
            }
        }
예제 #11
0
파일: Program.cs 프로젝트: egil/SPADD
        static void Main(string[] args)
        {
            NameValueCollection settings = ConfigurationManager.AppSettings;

            var listName = "FileChangedLog";
            var siteUrl = new Uri("http://xxxx.xxxxx.xxx");
            var credentials = new NetworkCredential("xxxxx", "xxxxx", "xxxxx");
            var downloadQueue = new ChangedFiles(siteUrl, listName, credentials, 10, 0);
            downloadQueue.Start();
            Console.ReadLine();
            downloadQueue.Stop();
        }
예제 #12
0
        public async Task <UpdateImpactResults> Analyze(
            Stream appxBlockMap1,
            Stream appxBlockMap2,
            CancellationToken cancellationToken   = default,
            IProgress <ProgressData> progressData = default)
        {
            var progressReadFiles1           = new RangeProgress(progressData, 0, 30);
            var progressReadFiles2           = new RangeProgress(progressData, 30, 60);
            var progressCalculatingStatus    = new RangeProgress(progressData, 60, 75);
            var progressDuplicates           = new RangeProgress(progressData, 75, 90);
            var progressRemainingCalculation = new RangeProgress(progressData, 90, 100);

            var filesInOldPackage = new SortedDictionary <string, AppxFile>();
            var filesInNewPackage = new SortedDictionary <string, AppxFile>();
            var blocksInPackage1  = new Dictionary <string, AppxBlock>();
            var blocksInPackage2  = new Dictionary <string, AppxBlock>();

            progressReadFiles1.Report(new ProgressData(0, "Reading the first package..."));
            var files1 = await this.GetFiles(appxBlockMap1, cancellationToken, progressReadFiles1).ConfigureAwait(false);

            foreach (var file1 in files1)
            {
                foreach (var block in file1.Blocks)
                {
                    blocksInPackage1[block.Hash] = block;
                }

                filesInOldPackage.Add(file1.Name, file1);
            }

            progressReadFiles2.Report(new ProgressData(0, "Reading the second package..."));
            var files2 = await this.GetFiles(appxBlockMap2, cancellationToken, progressReadFiles2).ConfigureAwait(false);

            foreach (var file2 in files2)
            {
                foreach (var block in file2.Blocks)
                {
                    blocksInPackage2[block.Hash] = block;
                }

                filesInNewPackage.Add(file2.Name, file2);
            }

            progressCalculatingStatus.Report(new ProgressData(0, "Calculating file status (1/2)..."));
            foreach (var file in filesInOldPackage)
            {
                cancellationToken.ThrowIfCancellationRequested();
                if (!filesInNewPackage.TryGetValue(file.Key, out var fileFromPackage2))
                {
                    file.Value.Status         = ComparisonStatus.Old;
                    file.Value.UpdateImpact   = 0; // file removed = no update on impact
                    file.Value.SizeDifference = -file.Value.UncompressedSize;
                    continue;
                }

                if (file.Value.UncompressedSize == fileFromPackage2.UncompressedSize && file.Value.Blocks.Select(b => b.Hash).SequenceEqual(fileFromPackage2.Blocks.Select(b => b.Hash)))
                {
                    file.Value.Status         = ComparisonStatus.Unchanged;
                    file.Value.UpdateImpact   = 0; // file unchanged = no update on impact
                    file.Value.SizeDifference = 0;

                    fileFromPackage2.Status         = ComparisonStatus.Unchanged;
                    fileFromPackage2.UpdateImpact   = 0; // file unchanged = no update on impact
                    fileFromPackage2.SizeDifference = 0;
                }
                else
                {
                    file.Value.Status         = ComparisonStatus.Changed;
                    file.Value.UpdateImpact   = 0; // file changed = show no update impact. The impact should be shown in the other package.
                    file.Value.SizeDifference = file.Value.UncompressedSize - fileFromPackage2.UncompressedSize;

                    var blocksOnlyInPackage2 = fileFromPackage2.Blocks.Select(b => b.Hash).Except(file.Value.Blocks.Select(b => b.Hash));
                    fileFromPackage2.Status         = ComparisonStatus.Changed;
                    fileFromPackage2.UpdateImpact   = blocksOnlyInPackage2.Select(b => blocksInPackage2[b]).Sum(b => b.CompressedSize);
                    fileFromPackage2.SizeDifference = fileFromPackage2.UncompressedSize - file.Value.UncompressedSize;
                }
            }
            foreach (var block1KeyValuePair in blocksInPackage1)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var block1 = block1KeyValuePair.Value;
                if (!blocksInPackage2.TryGetValue(block1KeyValuePair.Key, out var block2))
                {
                    block1.Status       = ComparisonStatus.Old;
                    block1.UpdateImpact = 0; // block removed, no update impact
                }
                else
                {
                    block1.Status       = ComparisonStatus.Unchanged;
                    block1.UpdateImpact = 0;
                    block2.Status       = ComparisonStatus.Unchanged;
                    block2.UpdateImpact = 0;
                }
            }

            progressCalculatingStatus.Report(new ProgressData(50, "Calculating file status (2/2)..."));
            foreach (var file in filesInNewPackage)
            {
                cancellationToken.ThrowIfCancellationRequested();
                if (!filesInOldPackage.TryGetValue(file.Key, out _))
                {
                    file.Value.Status         = ComparisonStatus.New;
                    file.Value.UpdateImpact   = file.Value.Blocks.Sum(b => b.CompressedSize); // sum of all blocks
                    file.Value.SizeDifference = file.Value.UncompressedSize;
                }
            }
            foreach (var block2KeyValuePair in blocksInPackage2)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var block2 = block2KeyValuePair.Value;
                if (!blocksInPackage1.ContainsKey(block2KeyValuePair.Key))
                {
                    block2.Status       = ComparisonStatus.New;
                    block2.UpdateImpact = block2.CompressedSize;
                }
            }

            var duplicates1 = new Dictionary <string, IList <AppxFile> >();
            var duplicates2 = new Dictionary <string, IList <AppxFile> >();

            progressDuplicates.Report(new ProgressData(0, "Finding duplicates..."));
            using (var md5 = MD5.Create())
            {
                foreach (var file in filesInOldPackage.Values)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    var hash          = string.Join(Environment.NewLine, file.Blocks.Select(b => b.Hash));
                    var allBlocksHash = System.Text.Encoding.ASCII.GetString(md5.ComputeHash(System.Text.Encoding.ASCII.GetBytes(hash)));

                    if (!duplicates1.TryGetValue(allBlocksHash, out var list))
                    {
                        list = new List <AppxFile>();
                        duplicates1[allBlocksHash] = list;
                    }

                    list.Add(file);
                }

                foreach (var file in filesInNewPackage.Values)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    var hash          = string.Join(System.Environment.NewLine, file.Blocks.Select(b => b.Hash));
                    var allBlocksHash = System.Text.Encoding.ASCII.GetString(md5.ComputeHash(System.Text.Encoding.ASCII.GetBytes(hash)));

                    if (!duplicates2.TryGetValue(allBlocksHash, out var list))
                    {
                        list = new List <AppxFile>();
                        duplicates2[allBlocksHash] = list;
                    }

                    list.Add(file);
                }
            }

            var duplicatedFiles1 = new AppxDuplication
            {
                Duplicates = new List <ComparedDuplicate>(),
                FileCount  = duplicates1.Count(d => d.Value.Count > 1) * 2,
                FileSize   = duplicates1.Where(d => d.Value.Count > 1).Sum(d => d.Value[0].UncompressedSize)
            };

            var duplicatedFiles2 = new AppxDuplication
            {
                Duplicates = new List <ComparedDuplicate>(),
                FileCount  = duplicates2.Count(d => d.Value.Count > 1) * 2,
                FileSize   = duplicates2.Where(d => d.Value.Count > 1).Sum(d => d.Value[0].UncompressedSize)
            };

            progressDuplicates.Report(new ProgressData(55, "Analyzing duplication impact (1/3)..."));
            foreach (var file in duplicates1.Where(d => d.Value.Count > 1))
            {
                cancellationToken.ThrowIfCancellationRequested();
                var duplicate = new ComparedDuplicate
                {
                    Files = new List <ComparedDuplicateFile>()
                };

                foreach (var df in file.Value)
                {
                    var mdf = new ComparedDuplicateFile
                    {
                        Name = df.Name,
                        PossibleSizeReduction   = df.UncompressedSize,
                        PossibleImpactReduction = df.Blocks.Sum(d => blocksInPackage1[d.Hash].CompressedSize)
                    };

                    duplicate.Files.Add(mdf);
                }

                duplicate.PossibleSizeReduction   = duplicate.Files[0].PossibleSizeReduction * (duplicate.Files.Count - 1);
                duplicate.PossibleImpactReduction = duplicate.Files[0].PossibleImpactReduction * (duplicate.Files.Count - 1);

                duplicatedFiles1.Duplicates.Add(duplicate);
            }

            progressDuplicates.Report(new ProgressData(70, "Analyzing duplication impact (2/3)..."));
            foreach (var file in duplicates2.Where(d => d.Value.Count > 1))
            {
                cancellationToken.ThrowIfCancellationRequested();
                var duplicate = new ComparedDuplicate
                {
                    Files = new List <ComparedDuplicateFile>()
                };

                foreach (var df in file.Value)
                {
                    var mdf = new ComparedDuplicateFile
                    {
                        Name = df.Name,
                        PossibleSizeReduction   = df.UncompressedSize,
                        PossibleImpactReduction = df.Blocks.Sum(d => blocksInPackage2[d.Hash].CompressedSize)
                    };

                    duplicate.Files.Add(mdf);
                }

                duplicate.PossibleSizeReduction   = duplicate.Files[0].PossibleSizeReduction * (duplicate.Files.Count - 1);
                duplicate.PossibleImpactReduction = duplicate.Files[0].PossibleImpactReduction * (duplicate.Files.Count - 1);

                duplicatedFiles2.Duplicates.Add(duplicate);
            }

            progressDuplicates.Report(new ProgressData(85, "Analyzing duplication impact (3/3)..."));
            duplicatedFiles1.PossibleImpactReduction = duplicatedFiles1.Duplicates.Sum(d => d.PossibleImpactReduction);
            duplicatedFiles1.PossibleSizeReduction   = duplicatedFiles1.Duplicates.Sum(d => d.PossibleSizeReduction);
            duplicatedFiles2.PossibleImpactReduction = duplicatedFiles2.Duplicates.Sum(d => d.PossibleImpactReduction);
            duplicatedFiles2.PossibleSizeReduction   = duplicatedFiles2.Duplicates.Sum(d => d.PossibleSizeReduction);

            cancellationToken.ThrowIfCancellationRequested();
            progressRemainingCalculation.Report(new ProgressData(0, "Analyzing changed files..."));
            var changedFiles = new ChangedFiles
            {
                // Shared parameters
                UpdateImpact       = filesInNewPackage.Values.Where(b => b.Status == ComparisonStatus.Changed).SelectMany(b => b.Blocks).Sum(b => b.UpdateImpact),
                ActualUpdateImpact = blocksInPackage2.Values.Where(b => b.Status == ComparisonStatus.Changed).Sum(b => b.UpdateImpact),
                SizeDifference     = filesInNewPackage.Values.Where(b => b.Status == ComparisonStatus.Changed).Sum(b => b.SizeDifference),
                FileCount          = filesInOldPackage.Values.Count(b => b.Status == ComparisonStatus.Changed),

                // Old package
                OldPackageFiles      = filesInOldPackage.Values.Where(f => f.Status == ComparisonStatus.Changed).ToList(),
                OldPackageFileSize   = filesInOldPackage.Values.Where(b => b.Status == ComparisonStatus.Changed).Sum(b => b.UncompressedSize),
                OldPackageBlockCount = filesInOldPackage.Values.Where(b => b.Status == ComparisonStatus.Changed).Sum(b => b.Blocks.Count),
                OldPackageBlockSize  = filesInOldPackage.Values.Where(b => b.Status == ComparisonStatus.Changed).SelectMany(b => b.Blocks).Sum(b => b.CompressedSize),

                // New package
                NewPackageFiles      = filesInNewPackage.Values.Where(f => f.Status == ComparisonStatus.Changed).ToList(),
                NewPackageFileSize   = filesInNewPackage.Values.Where(b => b.Status == ComparisonStatus.Changed).Sum(b => b.UncompressedSize),
                NewPackageBlockCount = filesInNewPackage.Values.Where(b => b.Status == ComparisonStatus.Changed).Sum(b => b.Blocks.Count),
                NewPackageBlockSize  = filesInNewPackage.Values.Where(b => b.Status == ComparisonStatus.Changed).SelectMany(b => b.Blocks).Sum(b => b.CompressedSize)
            };

            cancellationToken.ThrowIfCancellationRequested();
            progressRemainingCalculation.Report(new ProgressData(25, "Analyzing added files..."));
            var addedFiles = new AddedFiles
            {
                UpdateImpact       = filesInNewPackage.Values.Where(b => b.Status == ComparisonStatus.New).SelectMany(b => b.Blocks).Sum(b => b.UpdateImpact),
                ActualUpdateImpact = blocksInPackage2.Values.Where(b => b.Status == ComparisonStatus.New).Sum(b => b.UpdateImpact),
                BlockCount         = blocksInPackage2.Values.Count(b => b.Status == ComparisonStatus.New),
                BlockSize          = blocksInPackage2.Values.Where(b => b.Status == ComparisonStatus.New).Sum(b => b.CompressedSize),
                FileCount          = filesInNewPackage.Values.Count(s => s.Status == ComparisonStatus.New),
                SizeDifference     = filesInNewPackage.Values.Where(s => s.Status == ComparisonStatus.New).Sum(f => f.SizeDifference),
                FileSize           = filesInNewPackage.Values.Where(s => s.Status == ComparisonStatus.New).Sum(f => f.UncompressedSize),
                Files = filesInNewPackage.Values.Where(f => f.Status == ComparisonStatus.New).ToList()
            };

            cancellationToken.ThrowIfCancellationRequested();
            progressRemainingCalculation.Report(new ProgressData(50, "Analyzing deleted files..."));
            var deletedFiles = new DeletedFiles
            {
                FileSize           = filesInOldPackage.Values.Where(s => s.Status == ComparisonStatus.Old).Sum(f => f.UncompressedSize),
                SizeDifference     = filesInOldPackage.Values.Where(s => s.Status == ComparisonStatus.Old).Sum(f => f.SizeDifference),
                FileCount          = filesInOldPackage.Values.Count(s => s.Status == ComparisonStatus.Old),
                BlockSize          = blocksInPackage1.Values.Where(b => b.Status == ComparisonStatus.Old).Sum(b => b.CompressedSize),
                BlockCount         = blocksInPackage1.Values.Count(b => b.Status == ComparisonStatus.Old),
                ActualUpdateImpact = blocksInPackage1.Values.Where(s => s.Status == ComparisonStatus.Old).Sum(f => f.UpdateImpact),
                Files = filesInOldPackage.Values.Where(f => f.Status == ComparisonStatus.Old).ToList()
            };

            deletedFiles.UpdateImpact = deletedFiles.ActualUpdateImpact;

            cancellationToken.ThrowIfCancellationRequested();
            progressRemainingCalculation.Report(new ProgressData(75, "Analyzing unchanged files..."));
            var unchangedFiles = new UnchangedFiles
            {
                FileCount  = filesInNewPackage.Values.Count(b => b.Status == ComparisonStatus.Unchanged),
                BlockCount = blocksInPackage2.Values.Count(b => b.Status == ComparisonStatus.Unchanged),
                BlockSize  = blocksInPackage2.Values.Where(b => b.Status == ComparisonStatus.Unchanged).Sum(b => b.CompressedSize),
                FileSize   = filesInNewPackage.Values.Where(b => b.Status == ComparisonStatus.Unchanged).Sum(b => b.UncompressedSize),
                Files      = filesInOldPackage.Values.Where(f => f.Status == ComparisonStatus.Unchanged).ToList()
            };

            cancellationToken.ThrowIfCancellationRequested();
            progressRemainingCalculation.Report(new ProgressData(90, "Please wait..."));
            var comparisonResult = new UpdateImpactResults
            {
                OldPackageLayout = new AppxLayout
                {
                    FileSize   = filesInOldPackage.Sum(f => f.Value.UncompressedSize),
                    FileCount  = filesInOldPackage.Count,
                    BlockSize  = blocksInPackage1.Sum(f => f.Value.CompressedSize),
                    BlockCount = blocksInPackage1.Count,
                    Layout     = new PackageLayout
                    {
                        Blocks = this.GetChartForBlocks(filesInOldPackage),
                        Files  = this.GetChartForFiles(filesInOldPackage)
                    }
                },
                NewPackageLayout = new AppxLayout
                {
                    FileSize   = filesInNewPackage.Sum(f => f.Value.UncompressedSize),
                    FileCount  = filesInNewPackage.Count,
                    BlockSize  = blocksInPackage2.Sum(f => f.Value.CompressedSize),
                    BlockCount = blocksInPackage2.Count,
                    Layout     = new PackageLayout
                    {
                        Blocks = this.GetChartForBlocks(filesInNewPackage),
                        Files  = this.GetChartForFiles(filesInNewPackage)
                    }
                },
                OldPackageDuplication = duplicatedFiles1,
                NewPackageDuplication = duplicatedFiles2,
                UpdateImpact          =
                    blocksInPackage2.Where(b => b.Value.Status == ComparisonStatus.New).Sum(b => b.Value.UpdateImpact) +
                    blocksInPackage1.Sum(b => b.Value.UpdateImpact),
                ChangedFiles   = changedFiles,
                DeletedFiles   = deletedFiles,
                AddedFiles     = addedFiles,
                UnchangedFiles = unchangedFiles
            };

            comparisonResult.OldPackageLayout.Size = comparisonResult.OldPackageLayout.BlockSize + filesInOldPackage.Values.Sum(f => f.HeaderSize);
            comparisonResult.NewPackageLayout.Size = comparisonResult.NewPackageLayout.BlockSize + filesInNewPackage.Values.Sum(f => f.HeaderSize);

            comparisonResult.SizeDifference     = comparisonResult.AddedFiles.SizeDifference + comparisonResult.ChangedFiles.SizeDifference + comparisonResult.DeletedFiles.SizeDifference;
            comparisonResult.ActualUpdateImpact = comparisonResult.UpdateImpact;

            return(comparisonResult);
        }