예제 #1
0
        public void GitWithEnvironmentVariables()
        {
            // The trace info is an error, so we can't use CheckGitCommand().
            // We just want to make sure this doesn't throw an exception.
            ProcessResult result = GitHelpers.InvokeGitAgainstScalarRepo(
                this.Enlistment.RepoRoot,
                "branch",
                new Dictionary <string, string>
            {
                { "GIT_TRACE_PERFORMANCE", "1" },
                { "git_trace", "1" },
            },
                removeWaitingMessages: false);

            result.Output.ShouldContain("* FunctionalTests");
            result.Errors.ShouldNotContain(ignoreCase: true, unexpectedSubstrings: "exception");
            result.Errors.ShouldContain("trace.c:", "git command:");
        }
예제 #2
0
        /* We are using the following method for these scenarios
         * 1. Some commands compute a new commit sha, which is dependent on time and therefore
         *    won't match what is in the control repo.  For those commands, we just ensure that
         *    the errors match what we expect, but we skip comparing the output
         * 2. Using the sparse-checkout feature git will error out before checking the untracked files
         *    so the control repo will show the untracked files as being overwritten while the GVFS
         *    repo which is using the sparse-checkout will not.
         * 3. GVFS is returning not found for files that are outside the sparse-checkout and there
         *    are cases when git will delete these files during a merge outputting that it removed them
         *    which the GVFS repo did not have to remove so the message is missing that output.
         */
        protected void RunGitCommand(string command, bool ignoreErrors = false, bool checkStatus = true)
        {
            string controlRepoRoot = this.ControlGitRepo.RootPath;
            string gvfsRepoRoot    = this.Enlistment.RepoRoot;

            ProcessResult expectedResult = GitProcess.InvokeProcess(controlRepoRoot, command);
            ProcessResult actualResult   = GitHelpers.InvokeGitAgainstGVFSRepo(gvfsRepoRoot, command);

            if (!ignoreErrors)
            {
                GitHelpers.ErrorsShouldMatch(command, expectedResult, actualResult);
            }

            if (command != "status" && checkStatus)
            {
                this.ValidateGitCommand("status");
            }
        }
예제 #3
0
        public void GitStatusAfterUnstage()
        {
            string existingFilename = "test.cs";

            this.Enlistment.GetSourcePath(existingFilename).ShouldBeAFile(this.fileSystem);

            GitHelpers.CheckGitCommandAgainstScalarRepo(
                this.Enlistment.RepoRoot,
                "reset HEAD " + existingFilename,
                new string[] { });

            GitHelpers.CheckGitCommandAgainstScalarRepo(
                this.Enlistment.RepoRoot,
                "status",
                "On branch " + Properties.Settings.Default.Commitish,
                "Untracked files:",
                existingFilename);
        }
예제 #4
0
        public void ModifiedFileWillGetAddedToModifiedPathsFile()
        {
            string gitFileToTest = "GVFS/GVFS.Common/RetryWrapper.cs";
            string fileToCreate  = this.Enlistment.GetVirtualPathTo(gitFileToTest);

            this.VerifyWorktreeBit(gitFileToTest, LsFilesStatus.SkipWorktree);

            ManualResetEventSlim resetEvent = GitHelpers.AcquireGVFSLock(this.Enlistment, out _);

            this.fileSystem.WriteAllText(fileToCreate, "Anything can go here");
            this.fileSystem.FileExists(fileToCreate).ShouldEqual(true);
            resetEvent.Set();

            this.Enlistment.WaitForBackgroundOperations();

            GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, gitFileToTest);
            this.VerifyWorktreeBit(gitFileToTest, LsFilesStatus.Cached);
        }
예제 #5
0
        public void CanReadFileAfterHashObject()
        {
            this.ValidateGitCommand("status");

            // Validate that Readme.md is not on disk at all
            string fileName = "Readme.md";

            this.Enlistment.UnmountGVFS();
            this.Enlistment.GetVirtualPathTo(fileName).ShouldNotExistOnDisk(this.FileSystem);
            this.Enlistment.MountGVFS();

            // TODO 1087312: Fix 'git hash-oject' so that it works for files that aren't on disk yet
            GitHelpers.InvokeGitAgainstGVFSRepo(
                this.Enlistment.RepoRoot,
                "hash-object " + fileName);

            this.FileContentsShouldMatch(fileName);
        }
예제 #6
0
        /* We are using the following method for these scenarios
         * 1. Some commands compute a new commit sha, which is dependent on time and therefore
         *    won't match what is in the control repo.  For those commands, we just ensure that
         *    the errors match what we expect, but we skip comparing the output
         * 2. Using the sparse-checkout feature git will error out before checking the untracked files
         *    so the control repo will show the untracked files as being overwritten while the Scalar
         *    repo which is using the sparse-checkout will not.
         * 3. Scalar is returning not found for files that are outside the sparse-checkout and there
         *    are cases when git will delete these files during a merge outputting that it removed them
         *    which the Scalar repo did not have to remove so the message is missing that output.
         */
        protected void RunGitCommand(string command, bool ignoreErrors = false, bool checkStatus = true, string standardInput = null)
        {
            string controlRepoRoot = this.ControlGitRepo.RootPath;
            string scalarRepoRoot  = this.Enlistment.RepoRoot;

            ProcessResult expectedResult = GitProcess.InvokeProcess(controlRepoRoot, command, standardInput);
            ProcessResult actualResult   = GitHelpers.InvokeGitAgainstScalarRepo(scalarRepoRoot, command, input: standardInput);

            if (!ignoreErrors)
            {
                GitHelpers.LinesShouldMatch(command, expectedResult.Errors, actualResult.Errors);
            }

            if (command != "status" && checkStatus)
            {
                this.ValidateGitCommand("status");
            }
        }
예제 #7
0
        public void CanReadFileAfterHashObject()
        {
            this.ValidateGitCommand("status");

            // Validate that Scripts\RunUnitTests.bad is not on disk at all
            string filePath = Path.Combine("Scripts", "RunUnitTests.bat");

            this.Enlistment.UnmountGVFS();
            this.Enlistment.GetVirtualPathTo(filePath).ShouldNotExistOnDisk(this.FileSystem);
            this.Enlistment.MountGVFS();

            // TODO 1087312: Fix 'git hash-oject' so that it works for files that aren't on disk yet
            GitHelpers.InvokeGitAgainstGVFSRepo(
                this.Enlistment.RepoRoot,
                "hash-object " + GitHelpers.ConvertPathToGitFormat(filePath));

            this.FileContentsShouldMatch(filePath);
        }
예제 #8
0
        public void GitStatusAfterFileRename()
        {
            string oldFilename = "New.cs";

            this.EnsureTestFileExists(oldFilename);

            string newFilename = "test.cs";
            string newFilePath = this.Enlistment.GetSourcePath(newFilename);

            this.fileSystem.MoveFile(this.Enlistment.GetSourcePath(oldFilename), newFilePath);

            GitHelpers.CheckGitCommandAgainstScalarRepo(
                this.Enlistment.RepoRoot,
                "status",
                "On branch " + Properties.Settings.Default.Commitish,
                "Untracked files:",
                newFilename);
        }
예제 #9
0
        public void ModifiedFileWillGetSkipworktreeBitCleared()
        {
            string fileToTest    = "GVFS\\GVFS.Common\\RetryWrapper.cs";
            string fileToCreate  = Path.Combine(this.Enlistment.RepoRoot, fileToTest);
            string gitFileToTest = fileToTest.Replace('\\', '/');

            this.VerifyWorktreeBit(gitFileToTest, LsFilesStatus.SkipWorktree);

            ManualResetEventSlim resetEvent = GitHelpers.AcquireGVFSLock(this.Enlistment, out _);

            this.fileSystem.WriteAllText(fileToCreate, "Anything can go here");
            this.fileSystem.FileExists(fileToCreate).ShouldEqual(true);
            resetEvent.Set();

            this.Enlistment.WaitForBackgroundOperations().ShouldEqual(true, "Background operations did not complete.");

            GVFSHelpers.ModifiedPathsShouldContain(this.fileSystem, this.Enlistment.DotGVFSRoot, gitFileToTest + Environment.NewLine);
            this.VerifyWorktreeBit(gitFileToTest, LsFilesStatus.Cached);
        }
예제 #10
0
        public void GitStatusAfterNewFile()
        {
            string filename = "new.cs";
            string filePath = this.Enlistment.GetSourcePath(filename);

            filePath.ShouldNotExistOnDisk(this.fileSystem);
            this.fileSystem.WriteAllText(filePath, this.testFileContents);

            filePath.ShouldBeAFile(this.fileSystem).WithContents(this.testFileContents);

            GitHelpers.CheckGitCommandAgainstScalarRepo(
                this.Enlistment.RepoRoot,
                "status",
                "On branch " + Properties.Settings.Default.Commitish,
                "Untracked files:",
                filename);

            this.fileSystem.DeleteFile(filePath);
        }
        public void MergeConflictEnsureStatusFailsDueToConfig()
        {
            // This is compared against the message emitted by GVFS.Hooks\Program.cs
            string expectedErrorMessagePart = "--no-renames";

            this.ValidateGitCommand("checkout " + GitRepoTests.ConflictTargetBranch);
            this.RunGitCommand("merge " + GitRepoTests.ConflictSourceBranch, checkStatus: false);

            ProcessResult result1 = GitHelpers.InvokeGitAgainstGVFSRepo(this.Enlistment.RepoRoot, "status");

            result1.Errors.Contains(expectedErrorMessagePart);

            ProcessResult result2 = GitHelpers.InvokeGitAgainstGVFSRepo(this.Enlistment.RepoRoot, "status --no-renames");

            result2.Errors.Contains(expectedErrorMessagePart);

            // Complete setup to ensure teardown succeeds
            GitHelpers.InvokeGitAgainstGVFSRepo(this.Enlistment.RepoRoot, "config --local test.renames false");
        }
예제 #12
0
        /// <summary>
        ///     Verify that a branch exists in the specified repo and contains a version details file.
        ///     If it does not, optionally prompt the user to confirm that they wish to continue.
        /// </summary>
        /// <param name="remote">Remote</param>
        /// <param name="repo">Repository that the branch should be in</param>
        /// <param name="branch">Branch to check the existence of</param>
        /// <param name="prompt">Prompt the user to verify that they want to continue</param>
        /// <returns>True if the branch exists, prompting is not desired, or if the user confirms that they want to continue. False otherwise.</returns>
        public static async Task <bool> VerifyAndConfirmBranchExistsAsync(IRemote remote, string repo, string branch, bool prompt)
        {
            try
            {
                branch = GitHelpers.NormalizeBranchName(branch);

                await remote.GetDependenciesAsync(repo, branch);
            }
            catch (DependencyFileNotFoundException)
            {
                Console.WriteLine($"Warning: Could not find an eng/Version.Details.xml at '{repo}@{branch}'. Dependency updates may not happen as expected.");
                if (prompt)
                {
                    return(PromptForYesNo("Continue?"));
                }
            }

            return(true);
        }
예제 #13
0
        public void MountSetsCoreHooksPath()
        {
            this.Enlistment.UnmountGVFS();

            GitProcess.Invoke(this.Enlistment.RepoRoot, "config --unset core.hookspath");
            string.IsNullOrWhiteSpace(
                GitProcess.Invoke(this.Enlistment.RepoRoot, "config core.hookspath"))
            .ShouldBeTrue();

            this.Enlistment.MountGVFS();
            string expectedHooksPath = Path.Combine(this.Enlistment.RepoRoot, ".git", "hooks");

            expectedHooksPath = GitHelpers.ConvertPathToGitFormat(expectedHooksPath);

            GitProcess.Invoke(
                this.Enlistment.RepoRoot, "config core.hookspath")
            .Trim('\n')
            .ShouldEqual(expectedHooksPath);
        }
예제 #14
0
        private bool ReminderMessagingEnabled()
        {
            for (int count = 0; count < 50; count++)
            {
                ProcessResult result = GitHelpers.InvokeGitAgainstGSDRepo(
                    this.Enlistment.RepoRoot,
                    "status",
                    removeWaitingMessages: true,
                    removeUpgradeMessages: false);

                if (!string.IsNullOrEmpty(result.Errors) &&
                    result.Errors.Contains("A new version of GSD is available."))
                {
                    return(true);
                }
            }

            return(false);
        }
예제 #15
0
        public void CheckoutBranchWhereSymLinksChangeContentsAndTransitionToFile()
        {
            GitHelpers.InvokeGitAgainstGVFSRepo(this.Enlistment.RepoRoot, "checkout FunctionalTests/20201014_SymLinksPart2");
            GitHelpers.CheckGitCommandAgainstGVFSRepo(
                this.Enlistment.RepoRoot,
                "status",
                "On branch FunctionalTests/20201014_SymLinksPart2",
                "nothing to commit, working tree clean");

            // testFilePath and testFile2Path are unchanged from FunctionalTests/20180925_SymLinksPart2
            string testFilePath = this.Enlistment.GetVirtualPathTo(Path.Combine(TestFolderName, TestFileName));

            testFilePath.ShouldBeAFile(this.bashRunner).WithContents(TestFileContents);
            this.bashRunner.IsSymbolicLink(testFilePath).ShouldBeFalse($"{testFilePath} should not be a symlink");
            GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.bashRunner, TestFolderName + "/" + TestFileName);

            string testFile2Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestFolderName, TestFile2Name));

            testFile2Path.ShouldBeAFile(this.bashRunner).WithContents(TestFile2Contents);
            this.bashRunner.IsSymbolicLink(testFile2Path).ShouldBeFalse($"{testFile2Path} should not be a symlink");
            GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.bashRunner, TestFolderName + "/" + TestFile2Name);

            // In this branch childLinkPath has been changed to point to testFile2Path
            string childLinkPath = this.Enlistment.GetVirtualPathTo(Path.Combine(TestFolderName, ChildLinkName));

            this.bashRunner.IsSymbolicLink(childLinkPath).ShouldBeTrue($"{childLinkPath} should be a symlink");
            childLinkPath.ShouldBeAFile(this.bashRunner).WithContents(TestFile2Contents);
            GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.bashRunner, TestFolderName + "/" + ChildLinkName);

            // grandChildLinkPath should now be a file
            string grandChildLinkPath = this.Enlistment.GetVirtualPathTo(Path.Combine(TestFolderName, ChildFolderName, GrandChildLinkName));

            this.bashRunner.IsSymbolicLink(grandChildLinkPath).ShouldBeFalse($"{grandChildLinkPath} should not be a symlink");
            grandChildLinkPath.ShouldBeAFile(this.bashRunner).WithContents(GrandChildLinkNowAFileContents);

            // There should also be a new file in the child folder
            string newGrandChildFilePath = this.Enlistment.GetVirtualPathTo(Path.Combine(TestFolderName, ChildFolderName, GrandChildFileName));

            newGrandChildFilePath.ShouldBeAFile(this.bashRunner).WithContents(GrandChildFileContents);
            this.bashRunner.IsSymbolicLink(newGrandChildFilePath).ShouldBeFalse($"{newGrandChildFilePath} should not be a symlink");
            GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.bashRunner, TestFolderName + "/" + ChildFolderName + "/" + GrandChildFileName);
        }
예제 #16
0
        public void SecondCloneSucceedsWithMissingTrees()
        {
            string newCachePath = Path.Combine(this.localCacheParentPath, ".customGvfsCache2");
            GVFSFunctionalTestEnlistment enlistment1 = this.CreateNewEnlistment(localCacheRoot: newCachePath, skipPrefetch: true);

            File.ReadAllText(Path.Combine(enlistment1.RepoRoot, WellKnownFile));
            this.AlternatesFileShouldHaveGitObjectsRoot(enlistment1);

            // This Git command loads the commit and root tree for WellKnownCommitSha,
            // but does not download any more reachable objects.
            string        command = "cat-file -p origin/" + WellKnownBranch + "^{tree}";
            ProcessResult result  = GitHelpers.InvokeGitAgainstGVFSRepo(enlistment1.RepoRoot, command);

            result.ExitCode.ShouldEqual(0, $"git {command} failed with error: " + result.Errors);

            // If we did not properly check the failed checkout at this step, then clone will fail during checkout.
            GVFSFunctionalTestEnlistment enlistment2 = this.CreateNewEnlistment(localCacheRoot: newCachePath, branch: WellKnownBranch, skipPrefetch: true);

            File.ReadAllText(Path.Combine(enlistment2.RepoRoot, WellKnownFile));
        }
예제 #17
0
        public void GitWithEnvironmentVariables()
        {
            string previousPerf = Environment.GetEnvironmentVariable("GIT_TRACE_PERFORMANCE");

            Environment.SetEnvironmentVariable("GIT_TRACE_PERFORMANCE", "1");

            string previousTrace = Environment.GetEnvironmentVariable("git_trace");

            Environment.SetEnvironmentVariable("git_trace", "1");

            // The trace info is an error, so we can't use CheckGitCommand().
            // We just want to make sure this doesn't throw an exception.
            ProcessResult result = GitHelpers.InvokeGitAgainstGVFSRepo(this.Enlistment.RepoRoot, "branch", cleanErrors: false);

            result.Output.ShouldContain("* FunctionalTests");
            result.Errors.ShouldNotContain(ignoreCase: true, unexpectedSubstrings: "exception");
            result.Errors.ShouldContain("trace.c:", "git command:");
            Environment.SetEnvironmentVariable("GIT_TRACE_PERFORMANCE", previousPerf);
            Environment.SetEnvironmentVariable("git_trace", previousTrace);
        }
예제 #18
0
        public void GitStatusAfterFileNameCaseChange()
        {
            string oldFilename = "new.cs";

            this.EnsureTestFileExists(oldFilename);

            string newFilename = "New.cs";
            string newFilePath = this.Enlistment.GetVirtualPathTo(newFilename);

            this.fileSystem.MoveFile(this.Enlistment.GetVirtualPathTo(oldFilename), newFilePath);

            GitHelpers.CheckGitCommandAgainstGVFSRepo(
                this.Enlistment.RepoRoot,
                "status",
                "On branch " + Properties.Settings.Default.Commitish,
                "Untracked files:",
                newFilename);

            this.fileSystem.DeleteFile(newFilePath);
        }
        private bool ReminderMessagingEnabled()
        {
            Dictionary <string, string> environmentVariables = new Dictionary <string, string>();

            environmentVariables["GVFS_UPGRADE_DETERMINISTIC"] = "true";
            ProcessResult result = GitHelpers.InvokeGitAgainstGVFSRepo(
                this.Enlistment.RepoRoot,
                "status",
                environmentVariables,
                removeWaitingMessages: true,
                removeUpgradeMessages: false);

            if (!string.IsNullOrEmpty(result.Errors) &&
                result.Errors.Contains("A new version of VFS for Git is available."))
            {
                return(true);
            }

            return(false);
        }
예제 #20
0
        public void CheckoutBranchWhereSymLinkTransistionsToFolderAndFolderTransitionsToSymlink()
        {
            GitHelpers.InvokeGitAgainstGVFSRepo(this.Enlistment.RepoRoot, "checkout FunctionalTests/20201014_SymLinksPart4");
            GitHelpers.CheckGitCommandAgainstGVFSRepo(
                this.Enlistment.RepoRoot,
                "status",
                "On branch FunctionalTests/20201014_SymLinksPart4",
                "nothing to commit, working tree clean");

            // In this branch ChildLinkName has been changed to a directory and ChildFolder2Name has been changed to a link to ChildFolderName
            string linkNowADirectoryPath = this.Enlistment.GetVirtualPathTo(Path.Combine(TestFolderName, ChildLinkName));

            this.bashRunner.IsSymbolicLink(linkNowADirectoryPath).ShouldBeFalse($"{linkNowADirectoryPath} should not be a symlink");
            linkNowADirectoryPath.ShouldBeADirectory(this.bashRunner);
            GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.bashRunner, TestFolderName + "/" + ChildLinkName);

            string directoryNowALinkPath = this.Enlistment.GetVirtualPathTo(Path.Combine(TestFolderName, ChildFolder2Name));

            this.bashRunner.IsSymbolicLink(directoryNowALinkPath).ShouldBeTrue($"{directoryNowALinkPath} should be a symlink");
            GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.bashRunner, TestFolderName + "/" + ChildFolder2Name);
        }
예제 #21
0
        public void CreatedFileWillGetSkipworktreeBitCleared()
        {
            string fileToTest    = "GVFS\\GVFS.Common\\RetryWrapper.cs";
            string fileToCreate  = Path.Combine(this.Enlistment.RepoRoot, fileToTest);
            string gitFileToTest = fileToTest.Replace('\\', '/');

            this.VerifyWorktreeBit(gitFileToTest, LsFilesStatus.SkipWorktree);

            ManualResetEventSlim resetEvent = GitHelpers.AcquireGVFSLock(this.Enlistment);

            this.fileSystem.WriteAllText(fileToCreate, "Anything can go here");
            this.fileSystem.FileExists(fileToCreate).ShouldEqual(true);
            resetEvent.Set();

            this.Enlistment.WaitForBackgroundOperations().ShouldEqual(true, "Background operations did not complete.");

            string sparseCheckoutFile = Path.Combine(this.Enlistment.RepoRoot, TestConstants.DotGit.Info.SparseCheckout);

            sparseCheckoutFile.ShouldBeAFile(this.fileSystem).WithContents().ShouldContain(gitFileToTest);
            this.VerifyWorktreeBit(gitFileToTest, LsFilesStatus.Cached);
        }
예제 #22
0
        public void UpdateIndexRemoveAddFileOpenForWrite()
        {
            // TODO 940287: Remove this test and re-enable UpdateIndexRemoveFileOnDisk
            this.ValidateGitCommand("checkout " + GitRepoTests.ConflictTargetBranch);

            // git-status will not match because update-index --remove does not check what is on disk if the skip-worktree bit is set,
            // meaning it will always remove the file from the index
            GitProcess.InvokeProcess(this.ControlGitRepo.RootPath, "update-index --remove Test_ConflictTests/AddedFiles/AddedByBothDifferentContent.txt");
            GitHelpers.InvokeGitAgainstGVFSRepo(this.Enlistment.RepoRoot, "update-index --remove Test_ConflictTests/AddedFiles/AddedByBothDifferentContent.txt");
            this.FilesShouldMatchCheckoutOfTargetBranch();

            // Open Test_ConflictTests/AddedFiles/AddedByBothDifferentContent.txt for write so that it's added to the sparse-checkout
            using (FileStream stream = File.Open(Path.Combine(this.Enlistment.RepoRoot, @"Test_ConflictTests\AddedFiles\AddedByBothDifferentContent.txt"), FileMode.Open, FileAccess.Write))
            {
                // TODO 940287: Remove this File.Open once update-index --add\--remove are working as expected
            }

            // Add the files back to the index so the git-status that is run during teardown matches
            GitProcess.InvokeProcess(this.ControlGitRepo.RootPath, "update-index --add Test_ConflictTests/AddedFiles/AddedByBothDifferentContent.txt");
            GitHelpers.InvokeGitAgainstGVFSRepo(this.Enlistment.RepoRoot, "update-index --add Test_ConflictTests/AddedFiles/AddedByBothDifferentContent.txt");
        }
예제 #23
0
        public void CheckoutCleansUpTombstones()
        {
            const string folderToDelete = "Scripts";

            // Delete directory to create the tombstone
            string directoryToDelete = this.Enlistment.GetVirtualPathTo(folderToDelete);

            this.fileSystem.DeleteDirectory(directoryToDelete);
            this.Enlistment.UnmountGVFS();

            // Remove the directory entry from modified paths so git will not keep the folder up to date
            string modifiedPathsFile    = Path.Combine(this.Enlistment.DotGVFSRoot, TestConstants.Databases.ModifiedPaths);
            string modifiedPathsContent = this.fileSystem.ReadAllText(modifiedPathsFile);

            modifiedPathsContent = string.Join(Delimiter, modifiedPathsContent.Split(new[] { Delimiter }, StringSplitOptions.RemoveEmptyEntries).Where(x => !x.StartsWith($"A {folderToDelete}/")));
            this.fileSystem.WriteAllText(modifiedPathsFile, modifiedPathsContent + Delimiter);

            // Add tombstone folder entry to the placeholder file so the checkout will remove the tombstone
            // and start projecting the folder again
            string placeholderListFile    = Path.Combine(this.Enlistment.DotGVFSRoot, TestConstants.Databases.PlaceholderList);
            string placeholderListContent = this.fileSystem.ReadAllText(placeholderListFile);

            placeholderListContent = string.Join(Delimiter, placeholderListContent.Split(new[] { Delimiter }, StringSplitOptions.RemoveEmptyEntries).Where(x => !x.StartsWith($"A {folderToDelete}")));
            this.fileSystem.WriteAllText(placeholderListFile, placeholderListContent + Delimiter);
            string tombstoneEntry = $"A {folderToDelete}\0               POSSIBLE TOMBSTONE FOLDER{Delimiter}";

            this.fileSystem.AppendAllText(placeholderListFile, tombstoneEntry);

            this.Enlistment.MountGVFS();
            directoryToDelete.ShouldNotExistOnDisk(this.fileSystem);

            // checkout branch to remove tombstones and project the folder again
            GitHelpers.InvokeGitAgainstGVFSRepo(this.Enlistment.RepoRoot, "checkout -f HEAD");
            directoryToDelete.ShouldBeADirectory(this.fileSystem);

            this.Enlistment.UnmountGVFS();
            placeholderListContent = this.fileSystem.ReadAllText(placeholderListFile);
            placeholderListContent.ShouldNotContain(ignoreCase: false, unexpectedSubstrings: tombstoneEntry);
        }
예제 #24
0
        public void GitStatusAfterRenameFileIntoRepo()
        {
            string filename = "GitStatusAfterRenameFileIntoRepo.cs";

            // Create the test file in this.Enlistment.EnlistmentRoot as it's outside of src
            // and is cleaned up when the functional tests run
            string filePath = Path.Combine(this.Enlistment.EnlistmentRoot, filename);

            this.fileSystem.WriteAllText(filePath, this.testFileContents);
            filePath.ShouldBeAFile(this.fileSystem).WithContents(this.testFileContents);

            string renamedFileName = "GVFlt_MoveFileTest\\GitStatusAfterRenameFileIntoRepo.cs";

            this.fileSystem.MoveFile(filePath, this.Enlistment.GetVirtualPathTo(renamedFileName));
            this.Enlistment.GetVirtualPathTo(filePath).ShouldNotExistOnDisk(this.fileSystem);

            GitHelpers.CheckGitCommandAgainstGVFSRepo(
                this.Enlistment.RepoRoot,
                "status",
                "On branch " + Properties.Settings.Default.Commitish,
                "Untracked files:",
                renamedFileName.Replace('\\', '/'));
        }
        public void CheckoutCleansUpTombstones()
        {
            const string folderToDelete = "Scripts";

            // Delete directory to create the tombstone
            string directoryToDelete = this.Enlistment.GetVirtualPathTo(folderToDelete);

            this.fileSystem.DeleteDirectory(directoryToDelete);
            this.Enlistment.UnmountGSD();

            // Remove the directory entry from modified paths so git will not keep the folder up to date
            string modifiedPathsFile    = Path.Combine(this.Enlistment.DotGSDRoot, TestConstants.Databases.ModifiedPaths);
            string modifiedPathsContent = this.fileSystem.ReadAllText(modifiedPathsFile);

            modifiedPathsContent = string.Join(Delimiter, modifiedPathsContent.Split(new[] { Delimiter }, StringSplitOptions.RemoveEmptyEntries).Where(x => !x.StartsWith($"A {folderToDelete}/")));
            this.fileSystem.WriteAllText(modifiedPathsFile, modifiedPathsContent + Delimiter);

            // Add tombstone folder entry to the placeholder database so the checkout will remove the tombstone
            // and start projecting the folder again
            string placeholderDatabasePath = Path.Combine(this.Enlistment.DotGSDRoot, TestConstants.Databases.GSD);

            GSDHelpers.AddPlaceholderFolder(placeholderDatabasePath, folderToDelete, TombstoneFolderPlaceholderType);

            this.Enlistment.MountGSD();
            directoryToDelete.ShouldNotExistOnDisk(this.fileSystem);

            // checkout branch to remove tombstones and project the folder again
            GitHelpers.InvokeGitAgainstGSDRepo(this.Enlistment.RepoRoot, "checkout -f HEAD");
            directoryToDelete.ShouldBeADirectory(this.fileSystem);

            this.Enlistment.UnmountGSD();

            string placholders = GSDHelpers.GetAllSQLitePlaceholdersAsString(placeholderDatabasePath);

            placholders.ShouldNotContain(ignoreCase: false, unexpectedSubstrings: $"{folderToDelete}{GSDHelpers.PlaceholderFieldDelimiter}{TombstoneFolderPlaceholderType}{GSDHelpers.PlaceholderFieldDelimiter}");
        }
예제 #26
0
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                IRemote remote = RemoteFactory.GetRemote(_options, _options.Repository, Logger);

                _options.Branch = GitHelpers.NormalizeBranchName(_options.Branch);

                if (!(await UxHelpers.VerifyAndConfirmBranchExistsAsync(remote, _options.Repository, _options.Branch, !_options.NoConfirmation)))
                {
                    Console.WriteLine("Aborting default channel creation.");
                    return(Constants.ErrorCode);
                }

                await remote.AddDefaultChannelAsync(_options.Repository, _options.Branch, _options.Channel);

                return(Constants.SuccessCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed to add a new default channel association.");
                return(Constants.ErrorCode);
            }
        }
        public void LockToPreventDelete_SingleFile()
        {
            string testFile1Contents = "TestContentsLockToPreventDelete \r\n";
            string testFile1Name     = "test.txt";
            string testFile1Path     = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventDelete", testFile1Name));

            testFile1Path.ShouldBeAFile(this.fileSystem).WithContents(testFile1Contents);
            using (SafeFileHandle testFile1Handle = this.CreateFile(testFile1Path, FileShare.Read))
            {
                testFile1Handle.IsInvalid.ShouldEqual(false);

                ProcessResult result = this.InvokeGitAgainstGVFSRepo("checkout " + OldCommitId);
                result.Errors.ShouldContain(
                    "GVFS was unable to delete the following files. To recover, close all handles to the files and run these commands:",
                    "git clean -f " + TestParentFolderName + "/LockToPreventDelete/" + testFile1Name);

                GitHelpers.CheckGitCommandAgainstGVFSRepo(
                    this.Enlistment.RepoRoot,
                    "status -u",
                    "HEAD detached at " + OldCommitId,
                    "Untracked files:",
                    TestParentFolderName + "/LockToPreventDelete/" + testFile1Name);

                testFile1Path.ShouldBeAFile(this.fileSystem).WithContents(testFile1Contents);
            }

            this.GitCleanFile(TestParentFolderName + "/LockToPreventDelete/" + testFile1Name);
            this.GitStatusShouldBeClean(OldCommitId);
            GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, TestParentFolderName + "/LockToPreventDelete/" + testFile1Name);
            testFile1Path.ShouldNotExistOnDisk(this.fileSystem);

            this.GitCheckoutCommitId(NewFilesAndChangesCommitId);

            this.GitStatusShouldBeClean(NewFilesAndChangesCommitId);
            testFile1Path.ShouldBeAFile(this.fileSystem).WithContents(testFile1Contents);
        }
예제 #28
0
 public void GitLog()
 {
     GitHelpers.CheckGitCommandAgainstGVFSRepo(this.Enlistment.RepoRoot, "log -n1", "commit", "Author:", "Date:");
 }
        /// <summary>
        /// Implements the 'add-subscription' operation
        /// </summary>
        /// <param name="options"></param>
        public override async Task <int> ExecuteAsync()
        {
            IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger);

            if (_options.IgnoreChecks.Count() > 0 && !_options.AllChecksSuccessfulMergePolicy)
            {
                Console.WriteLine($"--ignore-checks must be combined with --all-checks-passed");
                return(Constants.ErrorCode);
            }

            // Parse the merge policies
            List <MergePolicy> mergePolicies = new List <MergePolicy>();

            if (_options.NoExtraCommitsMergePolicy)
            {
                mergePolicies.Add(
                    new MergePolicy
                {
                    Name = MergePolicyConstants.NoExtraCommitsMergePolicyName
                });
            }

            if (_options.AllChecksSuccessfulMergePolicy)
            {
                mergePolicies.Add(
                    new MergePolicy
                {
                    Name       = MergePolicyConstants.AllCheckSuccessfulMergePolicyName,
                    Properties = ImmutableDictionary.Create <string, JToken>()
                                 .Add(MergePolicyConstants.IgnoreChecksMergePolicyPropertyName, JToken.FromObject(_options.IgnoreChecks))
                });
            }

            if (_options.NoRequestedChangesMergePolicy)
            {
                mergePolicies.Add(
                    new MergePolicy
                {
                    Name       = MergePolicyConstants.NoRequestedChangesMergePolicyName,
                    Properties = ImmutableDictionary.Create <string, JToken>()
                });
            }

            if (_options.StandardAutoMergePolicies)
            {
                mergePolicies.Add(
                    new MergePolicy
                {
                    Name       = MergePolicyConstants.StandardMergePolicyName,
                    Properties = ImmutableDictionary.Create <string, JToken>()
                });
            }

            if (_options.Batchable && mergePolicies.Count > 0)
            {
                Console.WriteLine("Batchable subscriptions cannot be combined with merge policies. " +
                                  "Merge policies are specified at a repository+branch level.");
                return(Constants.ErrorCode);
            }

            string channel          = _options.Channel;
            string sourceRepository = _options.SourceRepository;
            string targetRepository = _options.TargetRepository;
            string targetBranch     = GitHelpers.NormalizeBranchName(_options.TargetBranch);
            string updateFrequency  = _options.UpdateFrequency;
            bool   batchable        = _options.Batchable;

            // If in quiet (non-interactive mode), ensure that all options were passed, then
            // just call the remote API
            if (_options.Quiet && !_options.ReadStandardIn)
            {
                if (string.IsNullOrEmpty(channel) ||
                    string.IsNullOrEmpty(sourceRepository) ||
                    string.IsNullOrEmpty(targetRepository) ||
                    string.IsNullOrEmpty(targetBranch) ||
                    string.IsNullOrEmpty(updateFrequency) ||
                    !Constants.AvailableFrequencies.Contains(updateFrequency, StringComparer.OrdinalIgnoreCase))
                {
                    Logger.LogError($"Missing input parameters for the subscription. Please see command help or remove --quiet/-q for interactive mode");
                    return(Constants.ErrorCode);
                }
            }
            else
            {
                // Grab existing subscriptions to get suggested values.
                // TODO: When this becomes paged, set a max number of results to avoid
                // pulling too much.
                var suggestedRepos    = remote.GetSubscriptionsAsync();
                var suggestedChannels = remote.GetChannelsAsync();

                // Help the user along with a form.  We'll use the API to gather suggested values
                // from existing subscriptions based on the input parameters.
                AddSubscriptionPopUp addSubscriptionPopup =
                    new AddSubscriptionPopUp("add-subscription/add-subscription-todo",
                                             Logger,
                                             channel,
                                             sourceRepository,
                                             targetRepository,
                                             targetBranch,
                                             updateFrequency,
                                             batchable,
                                             mergePolicies,
                                             (await suggestedChannels).Select(suggestedChannel => suggestedChannel.Name),
                                             (await suggestedRepos).SelectMany(subscription => new List <string> {
                    subscription.SourceRepository, subscription.TargetRepository
                }).ToHashSet(),
                                             Constants.AvailableFrequencies,
                                             Constants.AvailableMergePolicyYamlHelp);

                UxManager uxManager = new UxManager(_options.GitLocation, Logger);
                int       exitCode  = _options.ReadStandardIn ? uxManager.ReadFromStdIn(addSubscriptionPopup) : uxManager.PopUp(addSubscriptionPopup);
                if (exitCode != Constants.SuccessCode)
                {
                    return(exitCode);
                }
                channel          = addSubscriptionPopup.Channel;
                sourceRepository = addSubscriptionPopup.SourceRepository;
                targetRepository = addSubscriptionPopup.TargetRepository;
                targetBranch     = addSubscriptionPopup.TargetBranch;
                updateFrequency  = addSubscriptionPopup.UpdateFrequency;
                mergePolicies    = addSubscriptionPopup.MergePolicies;
                batchable        = addSubscriptionPopup.Batchable;
            }

            try
            {
                // If we are about to add a batchable subscription and the merge policies are empty for the
                // target repo/branch, warn the user.
                if (batchable)
                {
                    var existingMergePolicies = await remote.GetRepositoryMergePoliciesAsync(targetRepository, targetBranch);

                    if (!existingMergePolicies.Any())
                    {
                        Console.WriteLine("Warning: Batchable subscription doesn't have any repository merge policies. " +
                                          "PRs will not be auto-merged.");
                        Console.WriteLine($"Please use 'darc set-repository-policies --repo {targetRepository} --branch {targetBranch}' " +
                                          $"to set policies.{Environment.NewLine}");
                    }
                }

                // Verify the target
                IRemote targetVerifyRemote = RemoteFactory.GetRemote(_options, targetRepository, Logger);
                if (!(await UxHelpers.VerifyAndConfirmBranchExistsAsync(targetVerifyRemote, targetRepository, targetBranch, !_options.Quiet)))
                {
                    Console.WriteLine("Aborting subscription creation.");
                    return(Constants.ErrorCode);
                }

                // Verify the source.
                IRemote sourceVerifyRemote = RemoteFactory.GetRemote(_options, sourceRepository, Logger);
                if (!(await UxHelpers.VerifyAndConfirmRepositoryExistsAsync(sourceVerifyRemote, sourceRepository, !_options.Quiet)))
                {
                    Console.WriteLine("Aborting subscription creation.");
                    return(Constants.ErrorCode);
                }

                var newSubscription = await remote.CreateSubscriptionAsync(channel,
                                                                           sourceRepository,
                                                                           targetRepository,
                                                                           targetBranch,
                                                                           updateFrequency,
                                                                           batchable,
                                                                           mergePolicies);

                Console.WriteLine($"Successfully created new subscription with id '{newSubscription.Id}'.");

                // Prompt the user to trigger the subscription unless they have explicitly disallowed it
                if (!_options.NoTriggerOnCreate)
                {
                    bool triggerAutomatically = _options.TriggerOnCreate || UxHelpers.PromptForYesNo("Trigger this subscription immediately?");
                    if (triggerAutomatically)
                    {
                        await remote.TriggerSubscriptionAsync(newSubscription.Id.ToString());

                        Console.WriteLine($"Subscription '{newSubscription.Id}' triggered.");
                    }
                }

                return(Constants.SuccessCode);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine(e.Message);
                return(Constants.ErrorCode);
            }
            catch (RestApiException e) when(e.Response.Status == (int)System.Net.HttpStatusCode.BadRequest)
            {
                // Could have been some kind of validation error (e.g. channel doesn't exist)
                Logger.LogError($"Failed to create subscription: {e.Response.Content}");
                return(Constants.ErrorCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, $"Failed to create subscription.");
                return(Constants.ErrorCode);
            }
        }
 private void GitCheckoutToDiscardChanges(string gitPath)
 {
     GitHelpers.CheckGitCommandAgainstGVFSRepo(this.Enlistment.RepoRoot, "checkout -- " + gitPath);
 }