diff --git a/LibGit2Sharp.Tests/ReferenceFixture.cs b/LibGit2Sharp.Tests/ReferenceFixture.cs index f93696de7..ab13754d0 100644 --- a/LibGit2Sharp.Tests/ReferenceFixture.cs +++ b/LibGit2Sharp.Tests/ReferenceFixture.cs @@ -24,6 +24,7 @@ public void CanCreateADirectReference() newRef.CanonicalName.ShouldEqual(name); newRef.Target.ShouldNotBeNull(); newRef.Target.Sha.ShouldEqual("be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + newRef.TargetIdentifier.ShouldEqual(newRef.Target.Sha); repo.Refs[name].ShouldNotBeNull(); } } @@ -41,6 +42,7 @@ public void CanCreateASymbolicReference() newRef.ShouldNotBeNull(); newRef.CanonicalName.ShouldEqual(name); newRef.Target.CanonicalName.ShouldEqual(target); + newRef.TargetIdentifier.ShouldEqual(newRef.Target.CanonicalName); newRef.ResolveToDirectReference().Target.Sha.ShouldEqual("4c062a6361ae6959e06292c1fa5e2822d9c96345"); repo.Refs[name].ShouldNotBeNull(); } @@ -372,7 +374,39 @@ public void UpdatingAReferenceTargetWithBadParametersFails() } [Test] - public void CanMoveAReference() + [Ignore("Currently fails because of feature which is not yet implemented in libgit2")] + public void CanMoveAReferenceToADeeperReferenceHierarchy() + { + using (var path = new TemporaryCloneOfTestRepo()) + using (var repo = new Repository(path.RepositoryPath)) + { + const string newName = "refs/tags/test/deep"; + + Reference moved = repo.Refs.Move("refs/tags/test", newName); + moved.ShouldNotBeNull(); + moved.CanonicalName.ShouldEqual(newName); + } + } + + [Test] + [Ignore("Currently fails because of feature which is not yet implemented in libgit2")] + public void CanMoveAReferenceToAUpperReferenceHierarchy() + { + using (var path = new TemporaryCloneOfTestRepo()) + using (var repo = new Repository(path.RepositoryPath)) + { + const string newName = "refs/heads/o/sole"; + const string oldName = newName + "/mio"; + + repo.Refs.Create(oldName, repo.Head.ResolveToDirectReference().TargetIdentifier); + Reference moved = repo.Refs.Move(oldName, newName); + moved.ShouldNotBeNull(); + moved.CanonicalName.ShouldEqual(newName); + } + } + + [Test] + public void CanMoveAReferenceToADifferentReferenceHierarchy() { using (var path = new TemporaryCloneOfTestRepo()) using (var repo = new Repository(path.RepositoryPath)) diff --git a/LibGit2Sharp.Tests/RepositoryFixture.cs b/LibGit2Sharp.Tests/RepositoryFixture.cs index 59efd7c92..d88334736 100644 --- a/LibGit2Sharp.Tests/RepositoryFixture.cs +++ b/LibGit2Sharp.Tests/RepositoryFixture.cs @@ -13,24 +13,6 @@ public class RepositoryFixture private const string commitSha = "8496071c1b46c854b31185ea97743be6a8774479"; private const string notFoundSha = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; - [Test] - public void CallingExistsWithEmptyThrows() - { - using (var repo = new Repository(Constants.TestRepoPath)) - { - Assert.Throws(() => repo.HasObject(string.Empty)); - } - } - - [Test] - public void CallingExistsWithNullThrows() - { - using (var repo = new Repository(Constants.TestRepoPath)) - { - Assert.Throws(() => repo.HasObject(null)); - } - } - [Test] public void CanCreateBareRepo() { @@ -42,10 +24,10 @@ public void CanCreateBareRepo() using (var repo = new Repository(dir)) { - repo.Info.Path.ShouldNotBeNull(); repo.Info.WorkingDirectory.ShouldBeNull(); repo.Info.IsBare.ShouldBeTrue(); - repo.Info.IsEmpty.ShouldBeTrue(); + + AssertInitializedRepository(repo); } } } @@ -61,14 +43,30 @@ public void CanCreateStandardRepo() using (var repo = new Repository(dir)) { - repo.Info.Path.ShouldNotBeNull(); repo.Info.WorkingDirectory.ShouldNotBeNull(); repo.Info.IsBare.ShouldBeFalse(); - repo.Info.IsEmpty.ShouldBeTrue(); + + AssertInitializedRepository(repo); } } } + private static void AssertInitializedRepository(Repository repo) + { + repo.Info.Path.ShouldNotBeNull(); + repo.Info.IsEmpty.ShouldBeTrue(); + repo.Info.IsHeadDetached.ShouldBeFalse(); + repo.Head.TargetIdentifier.ShouldEqual("refs/heads/master"); + repo.Head.ResolveToDirectReference().ShouldBeNull(); + } + + [Test] + public void CreatingRepoWithBadParamsThrows() + { + Assert.Throws(() => Repository.Init(string.Empty)); + Assert.Throws(() => Repository.Init(null)); + } + [Test] public void CanLookupACommitByTheNameOfABranch() { @@ -135,128 +133,123 @@ public void CanOpenRepoWithFullPath() } [Test] - [Platform(Exclude = "Linux,Unix", Reason = "No need to test windows path separators on non-windows platforms")] - // See http://www.nunit.org/index.php?p=platform&r=2.6 for other platforms that can be excluded/included. - public void CanOpenRepoWithWindowsPathSeparators() + public void LookupObjectByWrongShaReturnsNull() { - using (new Repository(@".\Resources\testrepo.git")) + using (var repo = new Repository(Constants.TestRepoPath)) { + repo.Lookup(notFoundSha).ShouldBeNull(); + repo.Lookup(notFoundSha).ShouldBeNull(); } } [Test] - public void CanOpenRepository() + public void LookupObjectByWrongTypeReturnsNull() { using (var repo = new Repository(Constants.TestRepoPath)) { - repo.Info.Path.ShouldNotBeNull(); - repo.Info.WorkingDirectory.ShouldBeNull(); - repo.Info.IsBare.ShouldBeTrue(); - repo.Info.IsEmpty.ShouldBeFalse(); - repo.Info.IsHeadDetached.ShouldBeFalse(); + repo.Lookup(commitSha).ShouldNotBeNull(); + repo.Lookup(commitSha).ShouldNotBeNull(); + repo.Lookup(commitSha).ShouldBeNull(); } } [Test] - public void CanTellIfObjectsExistInRepository() + public void LookupObjectByUnknownReferenceNameReturnsNull() { using (var repo = new Repository(Constants.TestRepoPath)) { - repo.HasObject("8496071c1b46c854b31185ea97743be6a8774479").ShouldBeTrue(); - repo.HasObject("1385f264afb75a56a5bec74243be9b367ba4ca08").ShouldBeTrue(); - repo.HasObject("ce08fe4884650f067bd5703b6a59a8b3b3c99a09").ShouldBeFalse(); - repo.HasObject("8496071c1c46c854b31185ea97743be6a8774479").ShouldBeFalse(); + repo.Lookup("refs/heads/chopped/off").ShouldBeNull(); + repo.Lookup(notFoundSha).ShouldBeNull(); } } [Test] - public void CheckForDetachedHeadOnNewRepo() + public void LookingUpWithBadParamsThrows() { - using (new SelfCleaningDirectory(newRepoPath)) + using (var repo = new Repository(Constants.TestRepoPath)) { - var dir = Repository.Init(newRepoPath, true); - Path.IsPathRooted(dir).ShouldBeTrue(); - Directory.Exists(dir).ShouldBeTrue(); - - using (var repo = new Repository(dir)) - { - repo.Info.IsEmpty.ShouldBeTrue(); - repo.Info.IsHeadDetached.ShouldBeFalse(); - } + Assert.Throws(() => repo.Lookup(string.Empty)); + Assert.Throws(() => repo.Lookup(string.Empty)); + Assert.Throws(() => repo.Lookup((string)null)); + Assert.Throws(() => repo.Lookup((ObjectId)null)); + Assert.Throws(() => repo.Lookup((string)null)); + Assert.Throws(() => repo.Lookup((ObjectId)null)); } } [Test] - public void CreateRepoWithEmptyStringThrows() - { - Assert.Throws(() => Repository.Init(string.Empty)); - } - - [Test] - public void CreateRepoWithNullThrows() + [Platform(Exclude = "Linux,Unix", Reason = "No need to test windows path separators on non-windows platforms")] + // See http://www.nunit.org/index.php?p=platform&r=2.6 for other platforms that can be excluded/included. + public void CanOpenRepoWithWindowsPathSeparators() { - Assert.Throws(() => Repository.Init(null)); + using (new Repository(@".\Resources\testrepo.git")) + { + } } [Test] - public void LookupObjectByWrongShaReturnsNull() + public void CanOpenRepository() { using (var repo = new Repository(Constants.TestRepoPath)) { - repo.Lookup(notFoundSha).ShouldBeNull(); - repo.Lookup(notFoundSha).ShouldBeNull(); + repo.Info.Path.ShouldNotBeNull(); + repo.Info.WorkingDirectory.ShouldBeNull(); + repo.Info.IsBare.ShouldBeTrue(); + repo.Info.IsEmpty.ShouldBeFalse(); + repo.Info.IsHeadDetached.ShouldBeFalse(); } } [Test] - public void LookupObjectByWrongTypeReturnsNull() + public void OpenNonExistentRepoThrows() { - using (var repo = new Repository(Constants.TestRepoPath)) - { - repo.Lookup(commitSha).ShouldNotBeNull(); - repo.Lookup(commitSha).ShouldNotBeNull(); - repo.Lookup(commitSha).ShouldBeNull(); - } + Assert.Throws(() => { new Repository("a_bad_path"); }); } [Test] - public void LookupWithEmptyStringThrows() + public void OpeningRepositoryWithBadParamsThrows() { - using (var repo = new Repository(Constants.TestRepoPath)) - { - Assert.Throws(() => repo.Lookup(string.Empty)); - Assert.Throws(() => repo.Lookup(string.Empty)); - } + Assert.Throws(() => new Repository(string.Empty)); + Assert.Throws(() => new Repository(null)); } [Test] - public void LookupWithNullThrows() + public void CanTellIfObjectsExistInRepository() { using (var repo = new Repository(Constants.TestRepoPath)) { - Assert.Throws(() => repo.Lookup((string) null)); - Assert.Throws(() => repo.Lookup(null)); - Assert.Throws(() => repo.Lookup((string) null)); - Assert.Throws(() => repo.Lookup((ObjectId) null)); + repo.HasObject("8496071c1b46c854b31185ea97743be6a8774479").ShouldBeTrue(); + repo.HasObject("1385f264afb75a56a5bec74243be9b367ba4ca08").ShouldBeTrue(); + repo.HasObject("ce08fe4884650f067bd5703b6a59a8b3b3c99a09").ShouldBeFalse(); + repo.HasObject("8496071c1c46c854b31185ea97743be6a8774479").ShouldBeFalse(); } } [Test] - public void OpenNonExistentRepoThrows() + public void CheckingForObjectExistenceWithBadParamsThrows() { - Assert.Throws(() => { new Repository("a_bad_path"); }); + using (var repo = new Repository(Constants.TestRepoPath)) + { + Assert.Throws(() => repo.HasObject(string.Empty)); + Assert.Throws(() => repo.HasObject(null)); + } } [Test] - public void OpeningRepositoryWithEmptyPathThrows() + public void CheckForDetachedHeadOnNewRepo() { - Assert.Throws(() => new Repository(string.Empty)); - } + using (new SelfCleaningDirectory(newRepoPath)) + { + var dir = Repository.Init(newRepoPath, true); + Path.IsPathRooted(dir).ShouldBeTrue(); + Directory.Exists(dir).ShouldBeTrue(); - [Test] - public void OpeningRepositoryWithNullPathThrows() - { - Assert.Throws(() => new Repository(null)); + using (var repo = new Repository(dir)) + { + repo.Info.IsEmpty.ShouldBeTrue(); + repo.Info.IsHeadDetached.ShouldBeFalse(); + } + } } } } \ No newline at end of file diff --git a/LibGit2Sharp.Tests/TagFixture.cs b/LibGit2Sharp.Tests/TagFixture.cs index 9a23d6b2e..4d121b901 100644 --- a/LibGit2Sharp.Tests/TagFixture.cs +++ b/LibGit2Sharp.Tests/TagFixture.cs @@ -10,6 +10,8 @@ namespace LibGit2Sharp.Tests [TestFixture] public class TagFixture { + private const string newRepoPath = "new_repo"; + private readonly List expectedTags = new List {"test", "e90810b", "lw"}; private static readonly Signature signatureTim = new Signature("Tim Clem", "timothy.clem@gmail.com", DateTimeOffset.UtcNow); @@ -17,6 +19,7 @@ public class TagFixture private const string tagTestSha = "b25fa35b38051e4ae45d4222e795f9df2e43f1d1"; private const string commitE90810BSha = "e90810b8df3e80c413d903f631643c716887138d"; private const string tagE90810BSha = "7b4384978d2493e851f9cca7858815fac9b10980"; + const string invalidTargetId = "deadbeef1b46c854b31185ea97743be6a8774479"; [Test] public void CanCreateALightWeightTagFromSha() @@ -74,6 +77,18 @@ public void CanCreateATagWithNameContainingASlash() } } + [Test] + public void CreatingATagWithNameMatchingAnAlreadyExistingReferenceHierarchyThows() + { + using (var path = new TemporaryCloneOfTestRepo()) + using (var repo = new Repository(path.RepositoryPath)) + { + repo.ApplyTag("i/am/deep"); + Assert.Throws(() => repo.ApplyTag("i/am/deep/rooted")); + Assert.Throws(() => repo.ApplyTag("i/am")); + } + } + [Test] public void CanCreateAnAnnotatedTagFromABranchName() { @@ -98,6 +113,20 @@ public void CanCreateAnAnnotatedTagFromSha() } } + [Test] + [Description("Ported from cgit (https://github.com/git/git/blob/1c08bf50cfcf924094eca56c2486a90e2bf1e6e2/t/t7004-tag.sh#L359)")] + public void CanCreateAnAnnotatedTagWithAnEmptyMessage() + { + using (var path = new TemporaryCloneOfTestRepo()) + using (var repo = new Repository(path.RepositoryPath)) + { + var newTag = repo.ApplyTag("empty-annotated-tag", signatureNtk, string.Empty); + newTag.ShouldNotBeNull(); + newTag.IsAnnotated.ShouldBeTrue(); + newTag.Annotation.Message.ShouldEqual(string.Empty); + } + } + [Test] public void CanCreateAndOverwriteAnAnnotatedTag() { @@ -126,6 +155,198 @@ public void CreatingAnAnnotatedTagIsDeterministic() } } + [Test] + [Description("Ported from cgit (https://github.com/git/git/blob/1c08bf50cfcf924094eca56c2486a90e2bf1e6e2/t/t7004-tag.sh#L32)")] + public void CreatingATagInAEmptyRepositoryThrows() + { + using (new SelfCleaningDirectory(newRepoPath)) + { + var dir = Repository.Init(newRepoPath); + + using (var repo = new Repository(dir)) + { + Assert.Throws(() => repo.ApplyTag("mynotag")); + } + } + } + + [Test] + [Description("Ported from cgit (https://github.com/git/git/blob/1c08bf50cfcf924094eca56c2486a90e2bf1e6e2/t/t7004-tag.sh#L37)")] + public void CreatingATagForHeadInAEmptyRepositoryThrows() + { + using (new SelfCleaningDirectory(newRepoPath)) + { + var dir = Repository.Init(newRepoPath); + + using (var repo = new Repository(dir)) + { + Assert.Throws(() => repo.ApplyTag("mytaghead", "HEAD")); + } + } + } + + [Test] + [Description("Ported from cgit (https://github.com/git/git/blob/1c08bf50cfcf924094eca56c2486a90e2bf1e6e2/t/t7004-tag.sh#L42)")] + public void CreatingATagForAnUnknowReferenceShouldFail() + { + using (var path = new TemporaryCloneOfTestRepo()) + using (var repo = new Repository(path.RepositoryPath)) + { + Assert.Throws(() => repo.ApplyTag("mytagnorev", "aaaaaaaaaaa")); + } + } + + [Test] + [Description("Ported from cgit (https://github.com/git/git/blob/1c08bf50cfcf924094eca56c2486a90e2bf1e6e2/t/t7004-tag.sh#L42)")] + public void CreatingATagForAnUnknowObjectIdShouldFail() + { + using (var path = new TemporaryCloneOfTestRepo()) + using (var repo = new Repository(path.RepositoryPath)) + { + Assert.Throws(() => repo.ApplyTag("mytagnorev", invalidTargetId)); + } + } + + [Test] + [Description("Ported from cgit (https://github.com/git/git/blob/1c08bf50cfcf924094eca56c2486a90e2bf1e6e2/t/t7004-tag.sh#L48)")] + public void CanCreateATagForImplicitHead() + { + using (var path = new TemporaryCloneOfTestRepo()) + using (var repo = new Repository(path.RepositoryPath)) + { + var tag = repo.ApplyTag("mytag"); + tag.ShouldNotBeNull(); + + tag.Target.Id.ShouldEqual(repo.Head.ResolveToDirectReference().Target.Id); + + var retrievedTag = repo.Tags[tag.CanonicalName]; + tag.ShouldEqual(retrievedTag); + } + } + + [Test] + [Description("Ported from cgit (https://github.com/git/git/blob/1c08bf50cfcf924094eca56c2486a90e2bf1e6e2/t/t7004-tag.sh#L87)")] + public void CreatingADuplicateTagShouldFail() + { + using (var path = new TemporaryCloneOfTestRepo()) + using (var repo = new Repository(path.RepositoryPath)) + { + repo.ApplyTag("mytag"); + + Assert.Throws(() => repo.ApplyTag("mytag")); + } + } + + [Test] + [Description("Ported from cgit (https://github.com/git/git/blob/1c08bf50cfcf924094eca56c2486a90e2bf1e6e2/t/t7004-tag.sh#L90)")] + public void CreatingATagWithANonValidNameShouldFail() + { + using (var path = new TemporaryCloneOfTestRepo()) + using (var repo = new Repository(path.RepositoryPath)) + { + Assert.Throws(() => repo.ApplyTag("")); + Assert.Throws(() => repo.ApplyTag(".othertag")); + Assert.Throws(() => repo.ApplyTag("other tag")); + Assert.Throws(() => repo.ApplyTag("othertag^")); + Assert.Throws(() => repo.ApplyTag("other~tag")); + } + } + + [Test] + [Description("Ported from cgit (https://github.com/git/git/blob/1c08bf50cfcf924094eca56c2486a90e2bf1e6e2/t/t7004-tag.sh#L101)")] + public void CanCreateATagUsingHead() + { + using (var path = new TemporaryCloneOfTestRepo()) + using (var repo = new Repository(path.RepositoryPath)) + { + var tag = repo.ApplyTag("mytag", "HEAD"); + tag.ShouldNotBeNull(); + + tag.Target.Id.ShouldEqual(repo.Head.ResolveToDirectReference().Target.Id); + + var retrievedTag = repo.Tags[tag.CanonicalName]; + tag.ShouldEqual(retrievedTag); + } + } + + [Test] + public void CanCreateATagPointingToATree() + { + using (var path = new TemporaryCloneOfTestRepo()) + using (var repo = new Repository(path.RepositoryPath)) + { + var headCommit = (Commit)repo.Head.ResolveToDirectReference().Target; + var tree = headCommit.Tree; + + var tag = repo.ApplyTag("tree-tag", tree.Sha); + tag.ShouldNotBeNull(); + tag.IsAnnotated.ShouldBeFalse(); + tag.Target.Id.ShouldEqual(tree.Id); + + repo.Lookup(tag.Target.Id).ShouldEqual(tree); + repo.Tags[tag.Name].ShouldEqual(tag); + } + } + + [Test] + public void CanCreateATagPointingToABlob() + { + using (var path = new TemporaryCloneOfTestRepo()) + using (var repo = new Repository(path.RepositoryPath)) + { + var headCommit = (Commit)repo.Head.ResolveToDirectReference().Target; + var blob = headCommit.Tree.Files.First(); + + var tag = repo.ApplyTag("blob-tag", blob.Sha); + tag.ShouldNotBeNull(); + tag.IsAnnotated.ShouldBeFalse(); + tag.Target.Id.ShouldEqual(blob.Id); + + repo.Lookup(tag.Target.Id).ShouldEqual(blob); + repo.Tags[tag.Name].ShouldEqual(tag); + } + } + + [Test] + public void CreatingALightweightTagPointingToATagAnnotationGeneratesAnAnnotatedTagReusingThePointedAtTagAnnotation() + { + using (var path = new TemporaryCloneOfTestRepo()) + using (var repo = new Repository(path.RepositoryPath)) + { + var annotatedTag = repo.Tags["e90810b"]; + var annotation = annotatedTag.Annotation; + + var tag = repo.ApplyTag("lightweight-tag", annotation.Sha); + tag.ShouldNotBeNull(); + tag.IsAnnotated.ShouldBeTrue(); + tag.Target.Id.ShouldEqual(annotation.Id); + tag.Annotation.ShouldEqual(annotation); + + repo.Lookup(tag.Target.Id).ShouldEqual(annotation); + repo.Tags[tag.Name].ShouldEqual(tag); + } + } + + [Test] + public void CanCreateAnAnnotatedTagPointingToATagAnnotation() + { + using (var path = new TemporaryCloneOfTestRepo()) + using (var repo = new Repository(path.RepositoryPath)) + { + var annotatedTag = repo.Tags["e90810b"]; + var annotation = annotatedTag.Annotation; + + var tag = repo.ApplyTag("annotatedtag-tag", annotation.Sha, signatureNtk, "A new annotation"); + tag.ShouldNotBeNull(); + tag.IsAnnotated.ShouldBeTrue(); + tag.Annotation.TargetId.ShouldEqual(annotation.Id); + tag.Annotation.ShouldNotEqual(annotation); + + repo.Lookup(tag.Target.Id).ShouldEqual(tag.Annotation); + repo.Tags[tag.Name].ShouldEqual(tag); + } + } + [Test] public void BlindlyCreatingALightweightTagOverAnExistingOneThrows() { @@ -155,15 +376,6 @@ public void CreateTagWithADuplicateNameThrows() } } - [Test] - public void CreateTagWithEmptyMessageThrows() - { - using (var repo = new Repository(Constants.TestRepoPath)) - { - Assert.Throws(() => repo.Tags.Create("test_tag", "refs/heads/master", signatureTim, string.Empty)); - } - } - [Test] public void CreateTagWithEmptyNameThrows() { @@ -185,8 +397,6 @@ public void CreateTagWithEmptyTargetThrows() [Test] public void CreateTagWithNotExistingTargetThrows() { - const string invalidTargetId = "deadbeef1b46c854b31185ea97743be6a8774479"; - using (var repo = new Repository(Constants.TestRepoPath)) { Assert.Throws(() => repo.Tags.Create("test_tag", invalidTargetId, signatureTim, "message")); @@ -283,6 +493,16 @@ public void DeletingATagDecreasesTheTagsCount() } } + [Test] + [Description("Ported from cgit (https://github.com/git/git/blob/1c08bf50cfcf924094eca56c2486a90e2bf1e6e2/t/t7004-tag.sh#L108)")] + public void DeletingAnUnknownTagShouldFail() + { + using (var repo = new Repository(Constants.TestRepoPath)) + { + Assert.Throws(() => repo.Tags.Delete("unknown-tag")); + } + } + [Test] public void CanListTags() { @@ -296,6 +516,37 @@ public void CanListTags() } } + [Test] + [Description("Ported from cgit (https://github.com/git/git/blob/1c08bf50cfcf924094eca56c2486a90e2bf1e6e2/t/t7004-tag.sh#L24)")] + public void CanListAllTagsInAEmptyRepository() + { + using (new SelfCleaningDirectory(newRepoPath)) + { + var dir = Repository.Init(newRepoPath); + + using (var repo = new Repository(dir)) + { + repo.Info.IsEmpty.ShouldBeTrue(); + repo.Tags.Count().ShouldEqual(0); + } + } + } + + [Test] + [Description("Ported from cgit (https://github.com/git/git/blob/1c08bf50cfcf924094eca56c2486a90e2bf1e6e2/t/t7004-tag.sh#L165)")] + public void ListAllTagsShouldOutputThemInAnOrderedWay() + { + using (var repo = new Repository(Constants.TestRepoPath)) + { + List tagNames = repo.Tags.Select(t => t.Name).ToList(); + + var sortedTags = expectedTags; + sortedTags.Sort(); + + CollectionAssert.AreEqual(sortedTags, tagNames); + } + } + [Test] public void CanLookupALightweightTag() { diff --git a/LibGit2Sharp/Branch.cs b/LibGit2Sharp/Branch.cs index b0961e442..3918ee6b7 100644 --- a/LibGit2Sharp/Branch.cs +++ b/LibGit2Sharp/Branch.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using LibGit2Sharp.Core; namespace LibGit2Sharp @@ -109,22 +110,22 @@ public override int GetHashCode() private static bool IsRemoteBranch(string canonicalName) { - return canonicalName.StartsWith("refs/remotes/"); + return canonicalName.StartsWith("refs/remotes/", StringComparison.Ordinal); } private static string ShortenName(string branchName) { - if (branchName.StartsWith("refs/heads/")) + if (branchName.StartsWith("refs/heads/", StringComparison.Ordinal)) { return branchName.Substring("refs/heads/".Length); } - if (branchName.StartsWith("refs/remotes/")) + if (branchName.StartsWith("refs/remotes/", StringComparison.Ordinal)) { return branchName.Substring("refs/remotes/".Length); } - throw new ArgumentException(string.Format("'{0}' does not look like a valid branch name.", branchName)); + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,"'{0}' does not look like a valid branch name.", branchName)); } /// diff --git a/LibGit2Sharp/BranchCollection.cs b/LibGit2Sharp/BranchCollection.cs index 6d2677d68..351b24e96 100644 --- a/LibGit2Sharp/BranchCollection.cs +++ b/LibGit2Sharp/BranchCollection.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Globalization; using System.Linq; using LibGit2Sharp.Core; @@ -114,7 +115,7 @@ private static string NormalizeToCanonicalName(string name) return name; } - return string.Format("refs/heads/{0}", name); + return string.Format(CultureInfo.InvariantCulture, "refs/heads/{0}", name); } } } \ No newline at end of file diff --git a/LibGit2Sharp/CommitCollection.cs b/LibGit2Sharp/CommitCollection.cs index 46f9fa694..15c0fa0f7 100644 --- a/LibGit2Sharp/CommitCollection.cs +++ b/LibGit2Sharp/CommitCollection.cs @@ -131,7 +131,7 @@ private class CommitEnumerator : IEnumerator { private readonly bool forCountOnly; private readonly Repository repo; - private readonly IntPtr walker = IntPtr.Zero; + private readonly IntPtr walker = IntPtr.Zero; //TODO: Convert to SafeHandle? private bool disposed; public CommitEnumerator(Repository repo, bool forCountOnly = false) diff --git a/LibGit2Sharp/Core/Ensure.cs b/LibGit2Sharp/Core/Ensure.cs index a0ceafb21..88eb96908 100644 --- a/LibGit2Sharp/Core/Ensure.cs +++ b/LibGit2Sharp/Core/Ensure.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; namespace LibGit2Sharp.Core { @@ -47,7 +48,7 @@ public static void Success(int result) } throw new ApplicationException( - String.Format("There was an error in libgit2, but error handling sucks right now, so I can't tell you what it was. Error code = {0} ({1})", Enum.GetName(typeof(GitErrorCode), result) + String.Format(CultureInfo.InvariantCulture, "There was an error in libgit2, but error handling sucks right now, so I can't tell you what it was. Error code = {0} ({1})", Enum.GetName(typeof(GitErrorCode), result) , result)); } diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index 812417cb9..7ff8e63c8 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -25,6 +25,7 @@ false true x86 + AllRules.ruleset pdbonly diff --git a/LibGit2Sharp/ObjectId.cs b/LibGit2Sharp/ObjectId.cs index 80f2a9246..d576a769a 100644 --- a/LibGit2Sharp/ObjectId.cs +++ b/LibGit2Sharp/ObjectId.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Text; using LibGit2Sharp.Core; @@ -90,7 +91,7 @@ internal static ObjectId CreateFromMaybeSha(string sha) return null; } - throw new ArgumentException(string.Format("'{0}' is not a valid sha. Expected length should equal {1}.", sha, hexSize)); + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "'{0}' is not a valid sha. Expected length should equal {1}.", sha, hexSize)); } GitOid oid; diff --git a/LibGit2Sharp/Reference.cs b/LibGit2Sharp/Reference.cs index 7ba3f0443..d37cb9444 100644 --- a/LibGit2Sharp/Reference.cs +++ b/LibGit2Sharp/Reference.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Runtime.InteropServices; using LibGit2Sharp.Core; @@ -19,6 +20,11 @@ public abstract class Reference : IEquatable internal static T BuildFromPtr(IntPtr ptr, Repository repo) where T : class { + if (ptr == IntPtr.Zero) + { + return default(T); + } + var name = NativeMethods.git_reference_name(ptr); var type = NativeMethods.git_reference_type(ptr); @@ -28,16 +34,19 @@ internal static T BuildFromPtr(IntPtr ptr, Repository repo) where T : class { case GitReferenceType.Symbolic: IntPtr resolveRef; + var targetName = NativeMethods.git_reference_target(ptr); NativeMethods.git_reference_resolve(out resolveRef, ptr); var targetRef = BuildFromPtr(resolveRef, repo); - reference = new SymbolicReference { CanonicalName = name, Target = targetRef }; + reference = new SymbolicReference { CanonicalName = name, Target = targetRef, TargetIdentifier = targetName}; break; case GitReferenceType.Oid: var oidPtr = NativeMethods.git_reference_oid(ptr); var oid = (GitOid)Marshal.PtrToStructure(oidPtr, typeof(GitOid)); - var target = repo.Lookup(new ObjectId(oid)); - reference = new DirectReference { CanonicalName = name, Target = target }; + var targetId = new ObjectId(oid); + + var target = repo.Lookup(targetId); + reference = new DirectReference { CanonicalName = name, Target = target, TargetIdentifier = targetId.Sha}; break; default: @@ -62,18 +71,27 @@ internal static T BuildFromPtr(IntPtr ptr, Repository repo) where T : class } throw new InvalidOperationException( - string.Format("Unable to build a new instance of '{0}' from a reference of type '{1}'.", + string.Format(CultureInfo.InvariantCulture, "Unable to build a new instance of '{0}' from a reference of type '{1}'.", typeof (T), Enum.GetName(typeof (GitReferenceType), type))); } + protected abstract object ProvideAdditionalEqualityComponent(); + /// /// Recursively peels the target of the reference until a direct reference is encountered. /// /// The this points to. - public abstract DirectReference ResolveToDirectReference(); - - protected abstract object ProvideAdditionalEqualityComponent(); + public abstract DirectReference ResolveToDirectReference(); + + /// + /// Gets the target declared by the reference. + /// + /// If this reference is a , returns the canonical name of the target. + /// Otherwise, if this reference is a , returns the sha of the target. + /// + /// + public string TargetIdentifier { get; private set; } //TODO: Maybe find a better name for this property. /// /// Determines whether the specified is equal to the current . diff --git a/LibGit2Sharp/ReferenceCollection.cs b/LibGit2Sharp/ReferenceCollection.cs index 82f51b73c..8c77ff56e 100644 --- a/LibGit2Sharp/ReferenceCollection.cs +++ b/LibGit2Sharp/ReferenceCollection.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Globalization; using System.Linq; using LibGit2Sharp.Core; @@ -158,11 +159,6 @@ internal T Resolve(string name) where T : class IntPtr reference = RetrieveReferencePtr(name, false); - if (reference == IntPtr.Zero) - { - return default(T); - } - return Reference.BuildFromPtr(reference, repo); } @@ -184,16 +180,16 @@ public void UpdateTarget(string name, string target) switch (type) { case GitReferenceType.Oid: - if (id == null) throw new ArgumentException(String.Format("The reference specified by {0} is an Oid reference, you must provide a sha as the target.", name), "target"); + if (id == null) throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "The reference specified by {0} is an Oid reference, you must provide a sha as the target.", name), "target"); var oid = id.Oid; res = NativeMethods.git_reference_set_oid(reference, ref oid); break; case GitReferenceType.Symbolic: - if (id != null) throw new ArgumentException(String.Format("The reference specified by {0} is an Symbolic reference, you must provide a symbol as the target.", name), "target"); + if (id != null) throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "The reference specified by {0} is an Symbolic reference, you must provide a symbol as the target.", name), "target"); res = NativeMethods.git_reference_set_target(reference, target); break; default: - throw new InvalidOperationException(string.Format("Reference '{0}' has an un unexpected type ('{1}').", name, Enum.GetName(typeof(GitReferenceType), type))); + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Reference '{0}' has an un unexpected type ('{1}').", name, Enum.GetName(typeof(GitReferenceType), type))); } Ensure.Success(res); diff --git a/LibGit2Sharp/Repository.cs b/LibGit2Sharp/Repository.cs index a265b41bb..c5bcf3c96 100644 --- a/LibGit2Sharp/Repository.cs +++ b/LibGit2Sharp/Repository.cs @@ -126,9 +126,9 @@ protected virtual void Dispose(bool disposing) handle.Dispose(); } - if (Index != null) + if (index != null) { - Index.Dispose(); + index.Dispose(); } } @@ -178,8 +178,8 @@ public static string Init(string path, bool bare = false) /// Try to lookup an object by its and . If no matching object is found, null will be returned. /// /// The id to lookup. - /// - /// the or null if it was not found. + /// The kind of GitObject being looked up + /// The or null if it was not found. public GitObject Lookup(ObjectId id, GitObjectType type = GitObjectType.Any) { Ensure.ArgumentNotNull(id, "id"); @@ -196,5 +196,34 @@ public GitObject Lookup(ObjectId id, GitObjectType type = GitObjectType.Any) return GitObject.CreateFromPtr(obj, id, this); } + + /// + /// Try to lookup an object by its sha or a reference canonical name and . If no matching object is found, null will be returned. + /// + /// The sha or reference canonical name to lookup. + /// The kind of being looked up + /// The or null if it was not found. + public GitObject Lookup(string shaOrReferenceName, GitObjectType type = GitObjectType.Any) + { + ObjectId id = ObjectId.CreateFromMaybeSha(shaOrReferenceName); + if (id != null) + { + return Lookup(id, type); + } + + var reference = Refs[shaOrReferenceName]; + + if (!IsReferencePeelable(reference)) + { + return null; + } + + return Lookup(reference.ResolveToDirectReference().Target.Id, type); + } + + private static bool IsReferencePeelable(Reference reference) + { + return reference != null && ((reference is DirectReference) ||(reference is SymbolicReference && ((SymbolicReference)reference).Target != null)); + } } } \ No newline at end of file diff --git a/LibGit2Sharp/RepositoryExtensions.cs b/LibGit2Sharp/RepositoryExtensions.cs index b4a416552..f5f91938d 100644 --- a/LibGit2Sharp/RepositoryExtensions.cs +++ b/LibGit2Sharp/RepositoryExtensions.cs @@ -1,7 +1,8 @@ -using LibGit2Sharp.Core; - -namespace LibGit2Sharp +namespace LibGit2Sharp { + /// + /// Provides helper overloads to a . + /// public static class RepositoryExtensions { /// @@ -29,26 +30,49 @@ public static T Lookup(this Repository repository, ObjectId id) where T : Git } /// - /// Try to lookup an object by its sha or a reference name and . If no matching object is found, null will be returned. - /// - /// Exceptions: - /// ArgumentNullException + /// Creates a lightweight tag with the specified name. This tag will point at the commit pointed at by the . /// /// The being looked up. - /// The shaOrRef to lookup. - /// - /// the or null if it was not found. - public static GitObject Lookup(this Repository repository, string shaOrRef, GitObjectType type = GitObjectType.Any) + /// The name of the tag to create. + public static Tag ApplyTag(this Repository repository, string tagName) { - ObjectId id = ObjectId.CreateFromMaybeSha(shaOrRef); - if (id != null) - { - return repository.Lookup(id, type); - } + return ApplyTag(repository, tagName, repository.Head.CanonicalName); + } + + /// + /// Creates a lightweight tag with the specified name. This tag will point at the . + /// + /// The being looked up. + /// The name of the tag to create. + /// The canonical reference name or sha which should be pointed at by the Tag. + public static Tag ApplyTag(this Repository repository, string tagName, string target) + { + return repository.Tags.Create(tagName, target); + } - var reference = repository.Refs[shaOrRef]; - return repository.Lookup(reference.ResolveToDirectReference().Target.Id, type); + /// + /// Creates an annotated tag with the specified name. This tag will point at the commit pointed at by the . + /// + /// The being looked up. + /// The name of the tag to create. + /// The identity of the creator of this tag. + /// The annotation message. + public static Tag ApplyTag(this Repository repository, string tagName, Signature tagger, string message) + { + return ApplyTag(repository, tagName, repository.Head.CanonicalName, tagger, message); } + /// + /// Creates an annotated tag with the specified name. This tag will point at the . + /// + /// The being looked up. + /// The name of the tag to create. + /// The canonical reference name or sha which should be pointed at by the Tag. + /// The identity of the creator of this tag. + /// The annotation message. + public static Tag ApplyTag(this Repository repository, string tagName, string target, Signature tagger, string message) + { + return repository.Tags.Create(tagName, target, tagger, message); + } } } \ No newline at end of file diff --git a/LibGit2Sharp/SymbolicReference.cs b/LibGit2Sharp/SymbolicReference.cs index 29307ec35..0ddc5be67 100644 --- a/LibGit2Sharp/SymbolicReference.cs +++ b/LibGit2Sharp/SymbolicReference.cs @@ -21,7 +21,7 @@ protected override object ProvideAdditionalEqualityComponent() /// The this points to. public override DirectReference ResolveToDirectReference() { - return Target.ResolveToDirectReference(); + return (Target == null) ? null : Target.ResolveToDirectReference(); } } } \ No newline at end of file diff --git a/LibGit2Sharp/TagCollection.cs b/LibGit2Sharp/TagCollection.cs index 17031e56e..929cc5fba 100644 --- a/LibGit2Sharp/TagCollection.cs +++ b/LibGit2Sharp/TagCollection.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Globalization; using System.Linq; using LibGit2Sharp.Core; @@ -72,7 +73,7 @@ public Tag Create(string name, string target, Signature tagger, string message, Ensure.ArgumentNotNullOrEmptyString(name, "name"); Ensure.ArgumentNotNullOrEmptyString(target, "target"); Ensure.ArgumentNotNull(tagger, "tagger"); - Ensure.ArgumentNotNullOrEmptyString(message, "message"); + Ensure.ArgumentNotNull(message, "message"); GitObject objectToTag = RetrieveObjectToTag(target); @@ -120,7 +121,14 @@ public void Delete(string name) { Ensure.ArgumentNotNullOrEmptyString(name, "name"); - repo.Refs.Delete(this[name].CanonicalName); //TODO: To be replaced by native libgit2 git_tag_delete() when available. + Tag tag = this[name]; + + if (tag == null) + { + throw new ApplicationException(String.Format(CultureInfo.InvariantCulture, "No tag identified by '{0}' can be found in the repository.", name)); + } + + repo.Refs.Delete(tag.CanonicalName); //TODO: To be replaced by native libgit2 git_tag_delete() when available. } private GitObject RetrieveObjectToTag(string target) @@ -129,7 +137,7 @@ private GitObject RetrieveObjectToTag(string target) if (objectToTag == null) { - throw new ApplicationException(String.Format("No object identified by '{0}' can be found in the repository.", target)); + throw new ApplicationException(String.Format(CultureInfo.InvariantCulture, "No object identified by '{0}' can be found in the repository.", target)); } return objectToTag; @@ -144,7 +152,7 @@ private static string NormalizeToCanonicalName(string name) return name; } - return string.Format("refs/tags/{0}", name); + return string.Format(CultureInfo.InvariantCulture, "refs/tags/{0}", name); } } } \ No newline at end of file diff --git a/backlog.md b/backlog.md index 6b17db2e7..40f12cf3c 100644 --- a/backlog.md +++ b/backlog.md @@ -2,6 +2,7 @@ ### LibGit2Sharp + - Add branch renaming (public Branch Move(string oldName, string newName, bool allowOverwrite = false)) - Turn duplicated strings "refs/xxx" into properties of a generic Constants helper type - Refactor the error handling (OutputResult -> Exceptions) - Launch Code Analysis (Issues related to interop and marshaling will be worked on once we're able to succesffully exchange non ascii encoded data with libgit2) @@ -11,7 +12,7 @@ - https://bugzilla.novell.com/show_bug.cgi?id=324680 generates false-positive warnings regarding xml documentation when LibGit2Sharp is built with xbuild - The freeing of a newly created signature pointer doesn't "feel" to be done at the right place. - Should we throw when trying to delete a reference which can not be found? - - Favor overloads over optional parameters + - Favor overloads over optional parameters (http://msdn.microsoft.com/en-us/library/ms182135.aspx) - Ensure that types that are not supposed to be built by the Consumer do not expose a constructor. - Escape as early as possible from a method. Fight against the arrowhead effect (cf. http://elegantcode.com/2009/08/14/observations-on-the-if-statement/) @@ -21,6 +22,8 @@ - Add GitObject equality test suite - Add Reference equality test suite - Ensure former API tests are ported and passing + - Remove Ignore attribute from ReferenceFixture.CanMoveAReferenceToADeeperReferenceHierarchy() once git_reference_rename() is fixed + - Remove Ignore attribute from ReferenceFixture.CanMoveAReferenceToAUpperReferenceHierarchy() once git_reference_rename() is fixed ### Documentation