public void GetHeight_Merge() { var firstCommit = this.LibGit2Repository.Commit("First", this.Signer, this.Signer, new CommitOptions { AllowEmptyCommit = true }); var anotherBranch = this.LibGit2Repository.CreateBranch("another"); var secondCommit = this.LibGit2Repository.Commit("Second", this.Signer, this.Signer, new CommitOptions { AllowEmptyCommit = true }); Commands.Checkout(this.LibGit2Repository, anotherBranch); Commit[] branchCommits = new Commit[5]; for (int i = 1; i <= branchCommits.Length; i++) { branchCommits[i - 1] = this.LibGit2Repository.Commit($"branch commit #{i}", this.Signer, this.Signer, new CommitOptions { AllowEmptyCommit = true }); } this.LibGit2Repository.Merge(secondCommit, new Signature("t", "*****@*****.**", DateTimeOffset.Now), new MergeOptions { FastForwardStrategy = FastForwardStrategy.NoFastForward }); this.SetContextToHead(); // While we've created 8 commits, the tallest height is only 7. Assert.Equal(7, LibGit2GitExtensions.GetHeight(this.Context)); // Now stop enumerating early on just one branch of the ancestry -- the number should remain high. Assert.Equal(7, LibGit2GitExtensions.GetHeight(this.Context, c => c != secondCommit)); // This time stop in both branches of history, and verify that we count the taller one. Assert.Equal(3, LibGit2GitExtensions.GetHeight(this.Context, c => c != secondCommit && c != branchCommits[2])); }
public void GetIdAsVersion_Roundtrip_WithSubdirectoryVersionFiles() { var rootVersionExpected = VersionOptions.FromVersion(new Version(1, 0)); this.Context.VersionFile.SetVersion(this.RepoPath, rootVersionExpected); var subPathVersionExpected = VersionOptions.FromVersion(new Version(1, 1)); const string subPathRelative = "a"; string subPath = Path.Combine(this.RepoPath, subPathRelative); Directory.CreateDirectory(subPath); this.Context.VersionFile.SetVersion(subPath, subPathVersionExpected); this.InitializeSourceControl(); Commit head = this.LibGit2Repository.Head.Commits.First(); Version rootVersionActual = this.GetVersion(committish: head.Sha); Version subPathVersionActual = this.GetVersion(subPathRelative, head.Sha); // Verify that the versions calculated took the path into account. Assert.Equal(rootVersionExpected.Version.Version.Minor, rootVersionActual?.Minor); Assert.Equal(subPathVersionExpected.Version.Version.Minor, subPathVersionActual?.Minor); // Verify that we can find the commit given the version and path. Assert.Equal(head, LibGit2GitExtensions.GetCommitFromVersion(this.Context, rootVersionActual)); this.Context.RepoRelativeProjectDirectory = subPathRelative; Assert.Equal(head, LibGit2GitExtensions.GetCommitFromVersion(this.Context, subPathVersionActual)); // Verify that mismatching path and version results in a null value. Assert.Null(LibGit2GitExtensions.GetCommitFromVersion(this.Context, rootVersionActual)); this.Context.RepoRelativeProjectDirectory = string.Empty; Assert.Null(LibGit2GitExtensions.GetCommitFromVersion(this.Context, subPathVersionActual)); }
public void GetCommitsFromVersion_WithPathFilters() { string relativeDirectory = "some-sub-dir"; var commitsAt121 = new List <Commit>(); var commitsAt122 = new List <Commit>(); var commitsAt123 = new List <Commit>(); var versionData = VersionOptions.FromVersion(new Version("1.2")); versionData.PathFilters = new[] { new FilterPath("./", relativeDirectory), new FilterPath(":^/some-sub-dir/ignore.txt", relativeDirectory), new FilterPath(":^excluded-dir", relativeDirectory) }; commitsAt121.Add(this.WriteVersionFile(versionData, relativeDirectory)); // Commit touching excluded path does not affect version height var ignoredFilePath = Path.Combine(this.RepoPath, relativeDirectory, "ignore.txt"); File.WriteAllText(ignoredFilePath, "hello"); Commands.Stage(this.LibGit2Repository, ignoredFilePath); commitsAt121.Add(this.LibGit2Repository.Commit("Add excluded file", this.Signer, this.Signer)); // Commit touching both excluded and included path does affect height var includedFilePath = Path.Combine(this.RepoPath, relativeDirectory, "another-file.txt"); File.WriteAllText(includedFilePath, "hello"); File.WriteAllText(ignoredFilePath, "changed"); Commands.Stage(this.LibGit2Repository, includedFilePath); Commands.Stage(this.LibGit2Repository, ignoredFilePath); commitsAt122.Add(this.LibGit2Repository.Commit("Change both excluded and included file", this.Signer, this.Signer)); // Commit touching excluded directory does not affect version height var fileInExcludedDirPath = Path.Combine(this.RepoPath, relativeDirectory, "excluded-dir", "ignore.txt"); Directory.CreateDirectory(Path.GetDirectoryName(fileInExcludedDirPath)); File.WriteAllText(fileInExcludedDirPath, "hello"); Commands.Stage(this.LibGit2Repository, fileInExcludedDirPath); commitsAt122.Add(this.LibGit2Repository.Commit("Add file to excluded dir", this.Signer, this.Signer)); // Commit touching project directory affects version height File.WriteAllText(includedFilePath, "more changes"); Commands.Stage(this.LibGit2Repository, includedFilePath); commitsAt123.Add(this.LibGit2Repository.Commit("Changed included file", this.Signer, this.Signer)); this.Context.RepoRelativeProjectDirectory = relativeDirectory; Assert.Equal( commitsAt121.OrderBy(c => c.Sha), LibGit2GitExtensions.GetCommitsFromVersion(this.Context, new Version(1, 2, 1)).OrderBy(c => c.Sha)); Assert.Equal( commitsAt122.OrderBy(c => c.Sha), LibGit2GitExtensions.GetCommitsFromVersion(this.Context, new Version(1, 2, 2)).OrderBy(c => c.Sha)); Assert.Equal( commitsAt123.OrderBy(c => c.Sha), LibGit2GitExtensions.GetCommitsFromVersion(this.Context, new Version(1, 2, 3)).OrderBy(c => c.Sha)); }
public void GetHeight_EmptyRepo() { this.InitializeSourceControl(); Branch head = this.LibGit2Repository.Head; Assert.Throws <InvalidOperationException>(() => LibGit2GitExtensions.GetHeight(this.Context)); Assert.Throws <InvalidOperationException>(() => LibGit2GitExtensions.GetHeight(this.Context, c => true)); }
private void VerifyCommitsWithVersion(Commit[] commits) { Requires.NotNull(commits, nameof(commits)); for (int i = 0; i < commits.Length; i++) { Version encodedVersion = this.GetVersion(committish: commits[i].Sha); Assert.Equal(i + 1, encodedVersion.Build); Assert.Equal(commits[i], LibGit2GitExtensions.GetCommitFromVersion(this.Context, encodedVersion)); } }
public void GetCommitsFromVersion_MatchesOnEitherEndian() { this.InitializeSourceControl(); Commit commit = this.WriteVersionFile(new VersionOptions { Version = SemanticVersion.Parse("1.2"), GitCommitIdShortAutoMinimum = 4 }); Version originalVersion = new VersionOracle(this.Context).Version; Version swappedEndian = new Version(originalVersion.Major, originalVersion.Minor, originalVersion.Build, BinaryPrimitives.ReverseEndianness((ushort)originalVersion.Revision)); ushort twoBytesFromCommitId = checked ((ushort)originalVersion.Revision); Assert.Contains(commit, LibGit2GitExtensions.GetCommitsFromVersion(this.Context, originalVersion)); Assert.Contains(commit, LibGit2GitExtensions.GetCommitsFromVersion(this.Context, swappedEndian)); }
public void GetCommitsFromVersion_WithMajorMinorChecks() { Commit v1_0_50 = this.WriteVersionFile(new VersionOptions { Version = SemanticVersion.Parse("1.0.50-preview.{height}") }); Commit v1_1_50 = this.WriteVersionFile(new VersionOptions { Version = SemanticVersion.Parse("1.1.50-preview.{height}") }); Assert.Empty(LibGit2GitExtensions.GetCommitsFromVersion(this.Context, new Version(1, 0))); Assert.Empty(LibGit2GitExtensions.GetCommitsFromVersion(this.Context, new Version(1, 0, 49))); Assert.Equal(v1_0_50, Assert.Single(LibGit2GitExtensions.GetCommitsFromVersion(this.Context, new Version(1, 0, 50)))); Assert.Equal(v1_1_50, Assert.Single(LibGit2GitExtensions.GetCommitsFromVersion(this.Context, new Version(1, 1, 50)))); }
[SkippableFact(Skip = "It fails already.")] // Skippable, only run test on specific machine public void TestBiggerRepo() { var testBiggerRepoPath = @"D:\git\NerdBank.GitVersioning"; Skip.If(!Directory.Exists(testBiggerRepoPath)); using var largeRepo = new Repository(testBiggerRepoPath); foreach (var commit in largeRepo.Head.Commits) { var version = this.GetVersion("src", commit.Sha); this.Logger.WriteLine($"commit {commit.Id} got version {version}"); using var context = LibGit2Context.Create("src", commit.Sha); var backAgain = LibGit2GitExtensions.GetCommitFromVersion(context, version); Assert.Equal(commit, backAgain); } }
[InlineData(50, -2, true)] // force many build number collisions. generally revision will still make them unique, but it *might* collide on occasion. public void GetIdAsVersion_Roundtrip_UnstableOffset(int startingOffset, int offsetStepChange, bool allowCollisions) { var versionOptions = new VersionOptions { Version = SemanticVersion.Parse("1.2"), AssemblyVersion = null, VersionHeightOffset = startingOffset, }; this.WriteVersionFile(versionOptions); Commit[] commits = new Commit[16]; // create enough that statistically we'll likely hit interesting bits as MSB and LSB Version[] versions = new Version[commits.Length]; for (int i = 0; i < commits.Length; i += 2) { versionOptions.VersionHeightOffset += offsetStepChange; commits[i] = this.WriteVersionFile(versionOptions); versions[i] = this.GetVersion(committish: commits[i].Sha); commits[i + 1] = this.LibGit2Repository.Commit($"Commit {i + 1}", this.Signer, this.Signer, new CommitOptions { AllowEmptyCommit = true }); versions[i + 1] = this.GetVersion(committish: commits[i + 1].Sha); this.Logger.WriteLine($"Commit {commits[i].Id.Sha.Substring(0, 8)} as version: {versions[i]}"); this.Logger.WriteLine($"Commit {commits[i + 1].Id.Sha.Substring(0, 8)} as version: {versions[i + 1]}"); // Find the commits we just wrote while they are still at the tip of the branch. var matchingCommits = LibGit2GitExtensions.GetCommitsFromVersion(this.Context, versions[i]); Assert.Contains(commits[i], matchingCommits); matchingCommits = LibGit2GitExtensions.GetCommitsFromVersion(this.Context, versions[i + 1]); Assert.Contains(commits[i + 1], matchingCommits); } // Find all commits (again) now that history has been written. for (int i = 0; i < commits.Length; i++) { var matchingCommits = LibGit2GitExtensions.GetCommitsFromVersion(this.Context, versions[i]).ToList(); Assert.Contains(commits[i], matchingCommits); if (!allowCollisions) { Assert.Single(matchingCommits); } } }
public void GetHeight_SinglePath() { var first = this.LibGit2Repository.Commit("First", this.Signer, this.Signer, new CommitOptions { AllowEmptyCommit = true }); var second = this.LibGit2Repository.Commit("Second", this.Signer, this.Signer, new CommitOptions { AllowEmptyCommit = true }); var third = this.LibGit2Repository.Commit("Third", this.Signer, this.Signer, new CommitOptions { AllowEmptyCommit = true }); this.SetContextToHead(); Assert.Equal(3, LibGit2GitExtensions.GetHeight(this.Context)); Assert.Equal(3, LibGit2GitExtensions.GetHeight(this.Context, c => true)); Assert.Equal(2, LibGit2GitExtensions.GetHeight(this.Context, c => c != first)); Assert.Equal(1, LibGit2GitExtensions.GetHeight(this.Context, c => c != second)); }
public void GetIdAsVersion_Roundtrip(string version, string assemblyVersion, int versionHeightOffset) { var semanticVersion = SemanticVersion.Parse(version); const string repoRelativeSubDirectory = "subdir"; this.WriteVersionFile( new VersionOptions { Version = semanticVersion, AssemblyVersion = new VersionOptions.AssemblyVersionOptions(new Version(assemblyVersion)), VersionHeightOffset = versionHeightOffset, }, repoRelativeSubDirectory); Commit[] commits = new Commit[16]; // create enough that statistically we'll likely hit interesting bits as MSB and LSB Version[] versions = new Version[commits.Length]; for (int i = 0; i < commits.Length; i++) { commits[i] = this.LibGit2Repository.Commit($"Commit {i + 1}", this.Signer, this.Signer, new CommitOptions { AllowEmptyCommit = true }); versions[i] = this.GetVersion(repoRelativeSubDirectory, commits[i].Sha); this.Logger.WriteLine($"Commit {commits[i].Id.Sha.Substring(0, 8)} as version: {versions[i]}"); } this.Context.RepoRelativeProjectDirectory = repoRelativeSubDirectory; for (int i = 0; i < commits.Length; i++) { Assert.Equal(commits[i], LibGit2GitExtensions.GetCommitFromVersion(this.Context, versions[i])); // Also verify that we can find it without the revision number. // This is important because stable, publicly released NuGet packages // that contain no assemblies may only have major.minor.build as their version evidence. // But folks who specify a.b.c version numbers don't have any unique version component for the commit at all without the 4th integer. if (semanticVersion.Version.Build == -1) { Assert.Equal(commits[i], LibGit2GitExtensions.GetCommitFromVersion(this.Context, new Version(versions[i].Major, versions[i].Minor, versions[i].Build))); } } }
private static int OnGetCommitsCommand(string project, bool quiet, string version) { if (!Version.TryParse(version, out Version parsedVersion)) { Console.Error.WriteLine($"\"{version}\" is not a simple a.b.c[.d] version spec."); return((int)ExitCodes.InvalidVersionSpec); } string searchPath = GetSpecifiedOrCurrentDirectoryPath(project); using var context = (LibGit2Context)GitContext.Create(searchPath, writable: true); if (!context.IsRepository) { Console.Error.WriteLine("No git repo found at or above: \"{0}\"", searchPath); return((int)ExitCodes.NoGitRepo); } var candidateCommits = LibGit2GitExtensions.GetCommitsFromVersion(context, parsedVersion); PrintCommits(quiet, context, candidateCommits); return((int)ExitCodes.OK); }
private static int OnTagCommand(string project, string versionOrRef) { if (string.IsNullOrEmpty(versionOrRef)) { versionOrRef = DefaultRef; } string searchPath = GetSpecifiedOrCurrentDirectoryPath(project); using var context = (LibGit2Context)GitContext.Create(searchPath, writable: true); if (context is null) { Console.Error.WriteLine("No git repo found at or above: \"{0}\"", searchPath); return((int)ExitCodes.NoGitRepo); } var repository = context.Repository; if (!context.TrySelectCommit(versionOrRef)) { if (!Version.TryParse(versionOrRef, out Version parsedVersion)) { Console.Error.WriteLine($"\"{versionOrRef}\" is not a simple a.b.c[.d] version spec or git reference."); return((int)ExitCodes.InvalidVersionSpec); } string repoRelativeProjectDir = GetRepoRelativePath(searchPath, repository); var candidateCommits = LibGit2GitExtensions.GetCommitsFromVersion(context, parsedVersion).ToList(); if (candidateCommits.Count == 0) { Console.Error.WriteLine("No commit with that version found."); return((int)ExitCodes.NoMatchingVersion); } else if (candidateCommits.Count > 1) { PrintCommits(false, context, candidateCommits, includeOptions: true); int selection; do { Console.Write("Enter selection: "); }while (!int.TryParse(Console.ReadLine(), out selection) || selection > candidateCommits.Count || selection < 1); context.TrySelectCommit(candidateCommits[selection - 1].Sha); } else { context.TrySelectCommit(candidateCommits.Single().Sha); } } var oracle = new VersionOracle(context, CloudBuild.Active); if (!oracle.VersionFileFound) { Console.Error.WriteLine("No version.json file found in or above \"{0}\" in commit {1}.", searchPath, context.GitCommitId); return((int)ExitCodes.NoVersionJsonFound); } oracle.PublicRelease = true; // assume a public release so we don't get a redundant -gCOMMITID in the tag name string tagName = $"v{oracle.SemVer2}"; try { context.ApplyTag(tagName); } catch (LibGit2Sharp.NameConflictException) { var taggedCommit = repository.Tags[tagName].Target as LibGit2Sharp.Commit; bool correctTag = taggedCommit?.Sha == context.GitCommitId; Console.Error.WriteLine("The tag {0} is already defined ({1}).", tagName, correctTag ? "to the right commit" : $"expected {context.GitCommitId} but was on {taggedCommit.Sha}"); return((int)(correctTag ? ExitCodes.OK : ExitCodes.TagConflict)); } Console.WriteLine("{0} tag created at {1}.", tagName, context.GitCommitId); Console.WriteLine("Remember to push to a remote: git push origin {0}", tagName); return((int)ExitCodes.OK); }