コード例 #1
0
        public void Pull(PullConflictStrategy conflictStrategy, string remoteCommitSha = null)
        {
            UI.PrintLine("Checking remote status...");

            if (!ExistsRemotely())
            {
                throw new InvalidOperationException("The remote repository doesn't exist!");
            }

            if (remoteCommitSha == null)
            {
                if (!BranchExistsRemotely())
                {
                    throw new InvalidOperationException($"Branch '{LocalBranchName}' doesn't exist remotely!");
                }

                remoteCommitSha = Api.GetBranchCommitSha(LocalBranchName);
            }

            if (remoteCommitSha == LocalCommitSha)
            {
                throw new InvalidOperationException("Your local repository is up to date with remote, nothing to pull");
            }

            UI.PrintLine("Calculating changes...");

            var newRemoteFileReferences = GetRemoteFileReferences(remoteCommitSha);
            var oldRemoteFileReferences = ParseTreeFile();
            var remoteState             = CalculateRemoteState(oldRemoteFileReferences, newRemoteFileReferences);
            var localState = GetLocalState();

            var filenamesToDownload      = remoteState.AddedFiles.Union(remoteState.ModifiedFiles).ToList();
            var filenamesToDeleteLocally = remoteState.DeletedFiles.ToList();

            //* Conflict type #1: file was modified both remotely and locally

            var filenamesModifiedRemotelyAndLocally = remoteState.ModifiedFiles.Intersect(localState.ModifiedFiles);

            foreach (var filename in filenamesModifiedRemotelyAndLocally)
            {
                UI.Print($"\r\n{filename} was modified both locally and remotely");
                if (conflictStrategy == PullConflictStrategy.Ask)
                {
                    UI.Print($".\r\nDo you want to keep the [L]ocal version or to overwrite it with the [R]emote version? ");
                    ConsoleKey key = UI.ReadKey(ConsoleKey.L, ConsoleKey.R);
                    UI.PrintLine("");
                    if (key == ConsoleKey.L)
                    {
                        filenamesToDownload.Remove(filename);
                    }
                }
                else if (conflictStrategy == PullConflictStrategy.KeepLocal)
                {
                    UI.PrintLine(", the local copy will be kept.");
                    filenamesToDownload.Remove(filename);
                }
                else
                {
                    UI.PrintLine(", the remote copy will be downloaded.");
                }
            }

            //* Conflict type #2: file was modified remotely but deleted locally

            var filenamesModifiedRemotelyButDeletedLocally = remoteState.ModifiedFiles.Intersect(localState.DeletedFiles);

            foreach (var filename in filenamesModifiedRemotelyButDeletedLocally)
            {
                UI.Print($"\r\n{filename} was modified remotely but deleted locally");
                if (conflictStrategy == PullConflictStrategy.Ask)
                {
                    UI.Print($".\r\nDo you want to keep the file [D]eleted or to download the [R]emote version? ");
                    ConsoleKey key = UI.ReadKey(ConsoleKey.D, ConsoleKey.R);
                    UI.PrintLine("");
                    if (key == ConsoleKey.D)
                    {
                        filenamesToDownload.Remove(filename);
                    }
                }
                else if (conflictStrategy == PullConflictStrategy.KeepLocal)
                {
                    UI.PrintLine(", the file will remain deleted.");
                    filenamesToDownload.Remove(filename);
                }
                else
                {
                    UI.PrintLine(", the remote copy will be downloaded.");
                }
            }

            //* Conflict type #3: file was deleted remotely but modified locally

            var filenamesDeletedRemotelyButModifiedLocally = remoteState.DeletedFiles.Intersect(localState.ModifiedFiles);

            foreach (var filename in filenamesDeletedRemotelyButModifiedLocally)
            {
                UI.Print($"\r\n{filename} was deleted remotely but modified locally");
                if (conflictStrategy == PullConflictStrategy.Ask)
                {
                    UI.Print($".\r\nDo you want to [D]elete the local file or [K]eep it? ");
                    ConsoleKey key = UI.ReadKey(ConsoleKey.D, ConsoleKey.K);
                    UI.PrintLine("");
                    if (key == ConsoleKey.K)
                    {
                        filenamesToDeleteLocally.Remove(filename);
                    }
                }
                else if (conflictStrategy == PullConflictStrategy.KeepLocal)
                {
                    filenamesToDeleteLocally.Remove(filename);
                    UI.PrintLine(", the local copy will be kept.");
                }
                else
                {
                    UI.PrintLine(", the local copy will be deleted.");
                }
            }

            //* Conflict type #4: file was added remotely and locally

            var filenamesAddedRemotelyAndLocally = remoteState.AddedFiles.Intersect(localState.AddedFiles);

            foreach (var filename in filenamesAddedRemotelyAndLocally)
            {
                UI.Print($"\r\n{filename} was added both locally and remotely");
                if (conflictStrategy == PullConflictStrategy.Ask)
                {
                    UI.Print($".\r\nDo you want to keep the [L]ocal file or to download the [R]emote version? ");
                    ConsoleKey key = UI.ReadKey(ConsoleKey.L, ConsoleKey.R);
                    UI.PrintLine("");
                    if (key == ConsoleKey.L)
                    {
                        filenamesToDownload.Remove(filename);
                    }
                }
                else if (conflictStrategy == PullConflictStrategy.KeepLocal)
                {
                    filenamesToDownload.Remove(filename);
                    UI.PrintLine(", the local copy will be kept.");
                }
                else
                {
                    UI.PrintLine(", the remote copy will be downloaded.");
                }
            }

            UI.PrintLine("");

            var newRemoteFileReferencesByName = newRemoteFileReferences.ToDictionary(r => r.Path);

            foreach (var filename in filenamesToDownload)
            {
                UI.PrintLine($"Downloading {filename} ...");
                var fileContents = Api.GetBlob(newRemoteFileReferencesByName[filename].BlobSha);
                Directory.CreateFile(fileContents, filename);
                Directory.SetUnmodified(filename);
            }

            foreach (var filename in filenamesToDeleteLocally)
            {
                UI.PrintLine($"Deleting {filename} ...");
                Directory.DeleteFile(filename);
            }

            LocalCommitSha = remoteCommitSha;
            UpdateStateFile();
            UpdateTreeFile(newRemoteFileReferences);

            UI.PrintLine("");
        }