diff options
author | nulltoken <emeric.fermas@gmail.com> | 2012-04-19 12:14:13 +0400 |
---|---|---|
committer | nulltoken <emeric.fermas@gmail.com> | 2012-04-28 05:32:17 +0400 |
commit | 77fd1e48d22b99dbcac789ae014edaf9328776c2 (patch) | |
tree | fbbb10470919d2b31c9c49521570b239edc9e479 | |
parent | bd127243515540350875c8fa1d359db82d7411cf (diff) |
Add TreeDefinition and TreeEntryDefinition types
-rw-r--r-- | LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj | 1 | ||||
-rw-r--r-- | LibGit2Sharp.Tests/TreeDefinitionFixture.cs | 277 | ||||
-rw-r--r-- | LibGit2Sharp/LibGit2Sharp.csproj | 2 | ||||
-rw-r--r-- | LibGit2Sharp/TreeDefinition.cs | 207 | ||||
-rw-r--r-- | LibGit2Sharp/TreeEntryDefinition.cs | 151 |
5 files changed, 638 insertions, 0 deletions
diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj index 1116d395..9071e3b3 100644 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj @@ -71,6 +71,7 @@ <Compile Include="TestHelpers\SkipException.cs" /> <Compile Include="TestHelpers\SkippableFactAttribute.cs" /> <Compile Include="TestHelpers\TemporaryCloneOfTestRepo.cs" /> + <Compile Include="TreeDefinitionFixture.cs" /> <Compile Include="TreeFixture.cs" /> <Compile Include="TupleFixture.cs" /> </ItemGroup> diff --git a/LibGit2Sharp.Tests/TreeDefinitionFixture.cs b/LibGit2Sharp.Tests/TreeDefinitionFixture.cs new file mode 100644 index 00000000..310b3212 --- /dev/null +++ b/LibGit2Sharp.Tests/TreeDefinitionFixture.cs @@ -0,0 +1,277 @@ +using System; +using LibGit2Sharp.Tests.TestHelpers; +using Xunit; +using Xunit.Extensions; + +namespace LibGit2Sharp.Tests +{ + public class TreeDefinitionFixture : BaseFixture + { + /* + * $ git ls-tree -r HEAD + * 100755 blob 45b983be36b73c0788dc9cbcb76cbb80fc7bb057 1/branch_file.txt + * 100644 blob a8233120f6ad708f843d861ce2b7228ec4e3dec6 README + * 100644 blob 45b983be36b73c0788dc9cbcb76cbb80fc7bb057 branch_file.txt + * 100644 blob a71586c1dfe8a71c6cbf6c129f404c5642ff31bd new.txt + */ + + [Fact] + public void CanBuildATreeDefinitionFromATree() + { + using (var repo = new Repository(BareTestRepoPath)) + { + TreeDefinition td = TreeDefinition.From(repo.Head.Tip.Tree); + Assert.NotNull(td); + } + } + + [Fact] + public void BuildingATreeDefinitionWithBadParamsThrows() + { + Assert.Throws<ArgumentNullException>(() => TreeDefinition.From(null)); + } + + [Fact] + public void RequestingANonExistingEntryReturnsNull() + { + using (var repo = new Repository(BareTestRepoPath)) + { + TreeDefinition td = TreeDefinition.From(repo.Head.Tip.Tree); + + Assert.Null(td["nope"]); + Assert.Null(td["not/here"]); + Assert.Null(td["neither/in/here"]); + Assert.Null(td["README/is/a-Blob/not-a-Tree"]); + } + } + + [Fact] + public void RequestingAnEntryWithBadParamsThrows() + { + using (var repo = new Repository(BareTestRepoPath)) + { + TreeDefinition td = TreeDefinition.From(repo.Head.Tip.Tree); + + Assert.Throws<ArgumentNullException>(() => td[null]); + Assert.Throws<ArgumentException>(() => td[string.Empty]); + Assert.Throws<ArgumentException>(() => td["/"]); + Assert.Throws<ArgumentException>(() => td["/a"]); + Assert.Throws<ArgumentException>(() => td["1//branch_file.txt"]); + Assert.Throws<ArgumentException>(() => td["README/"]); + Assert.Throws<ArgumentException>(() => td["1/"]); + } + } + + [Theory] + [InlineData("1/branch_file.txt", "100755", GitObjectType.Blob, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057")] + [InlineData("README", "100644", GitObjectType.Blob, "a8233120f6ad708f843d861ce2b7228ec4e3dec6")] + [InlineData("branch_file.txt", "100644", GitObjectType.Blob, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057")] + [InlineData("new.txt", "100644", GitObjectType.Blob, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd")] + [InlineData("1", "040000", GitObjectType.Tree, "7f76480d939dc401415927ea7ef25c676b8ddb8f")] + public void CanRetrieveEntries(string path, string expectedAttributes, GitObjectType expectedType, string expectedSha) + { + using (var repo = new Repository(BareTestRepoPath)) + { + TreeDefinition td = TreeDefinition.From(repo.Head.Tip.Tree); + + TreeEntryDefinition ted = td[path]; + + Assert.Equal(ToMode(expectedAttributes), ted.Mode); + Assert.Equal(expectedType, ted.Type); + Assert.Equal(new ObjectId(expectedSha), ted.TargetId); + } + } + + //TODO: Convert Mode to a static class and add this helper method as 'FromString()' + private static Mode ToMode(string expectedAttributes) + { + return (Mode)Convert.ToInt32(expectedAttributes, 8); + } + + [Theory] + [InlineData("README", "README_TOO")] + [InlineData("README", "1/README")] + [InlineData("1/branch_file.txt", "1/another_one.txt")] + [InlineData("1/branch_file.txt", "another_one.txt")] + [InlineData("1/branch_file.txt", "1/2/another_one.txt")] + public void CanAddAnExistingTreeEntryDefinition(string sourcePath, string targetPath) + { + using (var repo = new Repository(BareTestRepoPath)) + { + TreeDefinition td = TreeDefinition.From(repo.Head.Tip.Tree); + Assert.Null(td[targetPath]); + + TreeEntryDefinition ted = td[sourcePath]; + td.Add(targetPath, ted); + + TreeEntryDefinition fetched = td[targetPath]; + Assert.NotNull(fetched); + + Assert.Equal(ted, fetched); + } + } + + [Theory] + [InlineData("a8233120f6ad708f843d861ce2b7228ec4e3dec6", "README_TOO")] + [InlineData("a8233120f6ad708f843d861ce2b7228ec4e3dec6", "1/README")] + [InlineData("45b983be36b73c0788dc9cbcb76cbb80fc7bb057", "1/another_one.txt")] + [InlineData("45b983be36b73c0788dc9cbcb76cbb80fc7bb057", "another_one.txt")] + public void CanAddAnExistingBlob(string blobSha, string targetPath) + { + using (var repo = new Repository(BareTestRepoPath)) + { + TreeDefinition td = TreeDefinition.From(repo.Head.Tip.Tree); + Assert.Null(td[targetPath]); + + var objectId = new ObjectId(blobSha); + var blob = repo.Lookup<Blob>(objectId); + + td.Add(targetPath, blob, Mode.NonExecutableFile); + + TreeEntryDefinition fetched = td[targetPath]; + Assert.NotNull(fetched); + + Assert.Equal(objectId, fetched.TargetId); + Assert.Equal(Mode.NonExecutableFile, fetched.Mode); + } + } + + [Fact] + public void CanAddAnExistingTree() + { + const string treeSha = "7f76480d939dc401415927ea7ef25c676b8ddb8f"; + const string targetPath = "1/2"; + + using (var repo = new Repository(BareTestRepoPath)) + { + TreeDefinition td = TreeDefinition.From(repo.Head.Tip.Tree); + + var objectId = new ObjectId(treeSha); + var tree = repo.Lookup<Tree>(objectId); + + td.Add(targetPath, tree); + + TreeEntryDefinition fetched = td[targetPath]; + Assert.NotNull(fetched); + + Assert.Equal(objectId, fetched.TargetId); + Assert.Equal(Mode.Directory, fetched.Mode); + + Assert.NotNull(td["1/2/branch_file.txt"]); + } + } + + [Fact] + public void CanReplaceAnExistingTreeWithABlob() + { + const string blobSha = "a8233120f6ad708f843d861ce2b7228ec4e3dec6"; + const string targetPath = "1"; + + using (var repo = new Repository(BareTestRepoPath)) + { + TreeDefinition td = TreeDefinition.From(repo.Head.Tip.Tree); + Assert.Equal(GitObjectType.Tree, td[targetPath].Type); + + var objectId = new ObjectId(blobSha); + var blob = repo.Lookup<Blob>(objectId); + + Assert.NotNull(td["1/branch_file.txt"]); + + td.Add(targetPath, blob, Mode.NonExecutableFile); + + TreeEntryDefinition fetched = td[targetPath]; + Assert.NotNull(fetched); + + Assert.Equal(GitObjectType.Blob, td[targetPath].Type); + Assert.Equal(objectId, fetched.TargetId); + Assert.Equal(Mode.NonExecutableFile, fetched.Mode); + + Assert.Null(td["1/branch_file.txt"]); + } + } + + [Theory] + [InlineData("README")] + [InlineData("1/branch_file.txt")] + public void CanReplaceAnExistingBlobWithATree(string targetPath) + { + const string treeSha = "7f76480d939dc401415927ea7ef25c676b8ddb8f"; + + using (var repo = new Repository(BareTestRepoPath)) + { + TreeDefinition td = TreeDefinition.From(repo.Head.Tip.Tree); + Assert.NotNull(td[targetPath]); + Assert.Equal(GitObjectType.Blob, td[targetPath].Type); + + var objectId = new ObjectId(treeSha); + var tree = repo.Lookup<Tree>(objectId); + + td.Add(targetPath, tree); + + TreeEntryDefinition fetched = td[targetPath]; + Assert.NotNull(fetched); + + Assert.Equal(GitObjectType.Tree, td[targetPath].Type); + Assert.Equal(objectId, fetched.TargetId); + Assert.Equal(Mode.Directory, fetched.Mode); + } + } + + [Fact] + public void CanNotReplaceAnExistingTreeWithATreeBeingAssembled() + { + using (var repo = new Repository(BareTestRepoPath)) + { + TreeDefinition td = TreeDefinition.From(repo.Head.Tip.Tree); + Assert.Equal(GitObjectType.Tree, td["1"].Type); + + td.Add("new/one", repo.Lookup<Blob>("a823312"), Mode.NonExecutableFile) + .Add("new/two", repo.Lookup<Blob>("a71586c"), Mode.NonExecutableFile) + .Add("new/tree", repo.Lookup<Tree>("7f76480")); + + Assert.Throws<InvalidOperationException>(() => td.Add("1", td["new"])); + } + } + + [Fact] + public void ModifyingTheContentOfATreeSetsItsOidToNull() + { + const string blobSha = "a8233120f6ad708f843d861ce2b7228ec4e3dec6"; + const string targetPath = "1/another_branch_file.txt"; + + using (var repo = new Repository(BareTestRepoPath)) + { + TreeDefinition td = TreeDefinition.From(repo.Head.Tip.Tree); + + var objectId = new ObjectId(blobSha); + var blob = repo.Lookup<Blob>(objectId); + + Assert.NotEqual(ObjectId.Zero, td["1"].TargetId); + + td.Add(targetPath, blob, Mode.NonExecutableFile); + + Assert.Equal(ObjectId.Zero, td["1"].TargetId); + } + } + + [Fact] + public void CanAddAnExistingBlobEntryWithAnExistingTree() + { + using (var repo = new Repository(BareTestRepoPath)) + { + TreeDefinition td = TreeDefinition.From(repo.Head.Tip.Tree); + TreeEntryDefinition original = td["README"]; + + td.Add("1/2/README", original); + + TreeEntryDefinition fetched = td["1/2/README"]; + Assert.NotNull(fetched); + + Assert.Equal(original.TargetId, fetched.TargetId); + Assert.Equal(original.Mode, fetched.Mode); + + Assert.NotNull(td["1/branch_file.txt"]); + } + } + } +} diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index a52b563e..4e6451e2 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -131,7 +131,9 @@ <Compile Include="TagAnnotation.cs" /> <Compile Include="TagCollection.cs" /> <Compile Include="Tree.cs" /> + <Compile Include="TreeDefinition.cs" /> <Compile Include="TreeEntry.cs" /> + <Compile Include="TreeEntryDefinition.cs" /> </ItemGroup> <ItemGroup> <CodeAnalysisDictionary Include="CustomDictionary.xml" /> diff --git a/LibGit2Sharp/TreeDefinition.cs b/LibGit2Sharp/TreeDefinition.cs new file mode 100644 index 00000000..dddccd25 --- /dev/null +++ b/LibGit2Sharp/TreeDefinition.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.IO; +using LibGit2Sharp.Core; +using LibGit2Sharp.Core.Compat; + +namespace LibGit2Sharp +{ + /// <summary> + /// Holds the meta data of a <see cref = "Tree" />. + /// </summary> + public class TreeDefinition + { + private readonly Dictionary<string, TreeEntryDefinition> entries = new Dictionary<string, TreeEntryDefinition>(); + private readonly Dictionary<string, TreeDefinition> unwrappedTrees = new Dictionary<string, TreeDefinition>(); + + /// <summary> + /// Builds a <see cref = "TreeDefinition" /> from an existing <see cref = "Tree" />. + /// </summary> + /// <param name = "tree">The <see cref = "Tree" /> to be processed.</param> + /// <returns>A new <see cref = "TreeDefinition" /> holding the meta data of the <paramref name = "tree" />.</returns> + public static TreeDefinition From(Tree tree) + { + Ensure.ArgumentNotNull(tree, "tree"); + + var td = new TreeDefinition(); + + foreach (TreeEntry treeEntry in tree) + { + td.AddEntry(treeEntry.Name, TreeEntryDefinition.From(treeEntry)); + } + + return td; + } + + private void AddEntry(string targetTreeEntryName, TreeEntryDefinition treeEntryDefinition) + { + if (entries.ContainsKey(targetTreeEntryName)) + { + WrapTree(targetTreeEntryName, treeEntryDefinition); + return; + } + + entries.Add(targetTreeEntryName, treeEntryDefinition); + } + + /// <summary> + /// Adds or replaces a <see cref="TreeEntryDefinition"/> at the specified <paramref name="targetTreeEntryPath"/> location. + /// </summary> + /// <param name="targetTreeEntryPath">The path within this <see cref="TreeDefinition"/>.</param> + /// <param name="treeEntryDefinition">The <see cref="TreeEntryDefinition"/> to be stored at the described location.</param> + /// <returns>The current <see cref="TreeDefinition"/>.</returns> + public TreeDefinition Add(string targetTreeEntryPath, TreeEntryDefinition treeEntryDefinition) + { + Ensure.ArgumentNotNullOrEmptyString(targetTreeEntryPath, "targetTreeEntryPath"); + Ensure.ArgumentNotNull(treeEntryDefinition, "treeEntryDefinition"); + + if (Path.IsPathRooted(targetTreeEntryPath)) + { + throw new ArgumentException("The provided path is an absolute path."); + } + + if (treeEntryDefinition is TransientTreeTreeEntryDefinition) + { + throw new InvalidOperationException(string.Format("The {0} references a target which hasn't been created in the {1} yet. This situation can occur when the target is a whole new {2} being created, or when an existing {2} is being updated because some of its children were added/removed.", typeof(TreeEntryDefinition).Name, typeof(ObjectDatabase).Name, typeof(Tree).Name)); + } + + Tuple<string, string> segments = ExtractPosixLeadingSegment(targetTreeEntryPath); + + if (segments.Item2 != null) + { + TreeDefinition td = RetrieveOrBuildTreeDefinition(segments.Item1, true); + td.Add(segments.Item2, treeEntryDefinition); + } + else + { + AddEntry(segments.Item1, treeEntryDefinition); + } + + return this; + } + + /// <summary> + /// Adds or replaces a <see cref="TreeEntryDefinition"/>, dynamically built from the provided <see cref="Blob"/>, at the specified <paramref name="targetTreeEntryPath"/> location. + /// </summary> + /// <param name="targetTreeEntryPath">The path within this <see cref="TreeDefinition"/>.</param> + /// <param name="blob">The <see cref="Blob"/> to be stored at the described location.</param> + /// <param name="mode">The file related <see cref="Mode"/> attributes.</param> + /// <returns>The current <see cref="TreeDefinition"/>.</returns> + public TreeDefinition Add(string targetTreeEntryPath, Blob blob, Mode mode) + { + Ensure.ArgumentNotNull(blob, "blob"); + Ensure.ArgumentConformsTo(mode, + m => m.HasAny(new[] { Mode.ExecutableFile, Mode.NonExecutableFile, Mode.NonExecutableGroupWriteableFile }), "mode"); + + TreeEntryDefinition ted = TreeEntryDefinition.From(blob, mode); + + return Add(targetTreeEntryPath, ted); + } + + /// <summary> + /// Adds or replaces a <see cref="TreeEntryDefinition"/>, dynamically built from the provided <see cref="Tree"/>, at the specified <paramref name="targetTreeEntryPath"/> location. + /// </summary> + /// <param name="targetTreeEntryPath">The path within this <see cref="TreeDefinition"/>.</param> + /// <param name="tree">The <see cref="Tree"/> to be stored at the described location.</param> + /// <returns>The current <see cref="TreeDefinition"/>.</returns> + public TreeDefinition Add(string targetTreeEntryPath, Tree tree) + { + Ensure.ArgumentNotNull(tree, "tree"); + + TreeEntryDefinition ted = TreeEntryDefinition.From(tree); + + return Add(targetTreeEntryPath, ted); + } + + private TreeDefinition RetrieveOrBuildTreeDefinition(string treeName, bool shouldOverWrite) + { + TreeDefinition td; + + if (unwrappedTrees.TryGetValue(treeName, out td)) + { + return td; + } + + TreeEntryDefinition treeEntryDefinition; + bool hasAnEntryBeenFound = entries.TryGetValue(treeName, out treeEntryDefinition); + + if (hasAnEntryBeenFound) + { + switch (treeEntryDefinition.Type) + { + case GitObjectType.Tree: + td = From(treeEntryDefinition.Target as Tree); + break; + + case GitObjectType.Blob: + if (shouldOverWrite) + { + td = new TreeDefinition(); + break; + } + + return null; + + default: + throw new NotImplementedException(); + } + } + else + { + if (!shouldOverWrite) + { + return null; + } + + td = new TreeDefinition(); + } + + entries[treeName] = new TransientTreeTreeEntryDefinition(); + + unwrappedTrees.Add(treeName, td); + return td; + } + + private void WrapTree(string entryName, TreeEntryDefinition treeEntryDefinition) + { + entries[entryName] = treeEntryDefinition; + unwrappedTrees.Remove(entryName); + } + + /// <summary> + /// Retrieves the <see cref="TreeEntryDefinition"/> located the specified <paramref name="treeEntryPath"/> path. + /// </summary> + /// <param name="treeEntryPath">The path within this <see cref="TreeDefinition"/>.</param> + /// <returns>The found <see cref="TreeEntryDefinition"/> if any; null otherwise.</returns> + public TreeEntryDefinition this[string treeEntryPath] + { + get + { + Ensure.ArgumentNotNullOrEmptyString(treeEntryPath, "treeEntryPath"); + + Tuple<string, string> segments = ExtractPosixLeadingSegment(treeEntryPath); + + if (segments.Item2 != null) + { + TreeDefinition td = RetrieveOrBuildTreeDefinition(segments.Item1, false); + return td == null ? null : td[segments.Item2]; + } + + TreeEntryDefinition treeEntryDefinition; + return !entries.TryGetValue(segments.Item1, out treeEntryDefinition) ? null : treeEntryDefinition; + } + } + + private static Tuple<string, string> ExtractPosixLeadingSegment(FilePath targetPath) + { + string[] segments = targetPath.Posix.Split(new[] { '/' }, 2); + + if (segments[0] == string.Empty || (segments.Length == 2 && (segments[1] == string.Empty || segments[1].StartsWith("/")))) + { + throw new ArgumentException(string.Format("'{0}' is not a valid path.", targetPath)); + } + + return new Tuple<string, string>(segments[0], segments.Length == 2 ? segments[1] : null); + } + } +} diff --git a/LibGit2Sharp/TreeEntryDefinition.cs b/LibGit2Sharp/TreeEntryDefinition.cs new file mode 100644 index 00000000..ecb2a6a5 --- /dev/null +++ b/LibGit2Sharp/TreeEntryDefinition.cs @@ -0,0 +1,151 @@ +using System; +using LibGit2Sharp.Core; +using LibGit2Sharp.Core.Compat; + +namespace LibGit2Sharp +{ + /// <summary> + /// Holds the meta data of a <see cref = "TreeEntry" />. + /// </summary> + public class TreeEntryDefinition + { + private Lazy<GitObject> target; + + private static readonly LambdaEqualityHelper<TreeEntryDefinition> equalityHelper = + new LambdaEqualityHelper<TreeEntryDefinition>(new Func<TreeEntryDefinition, object>[] { x => x.Mode, x => x.Type, x => x.TargetId }); + + internal TreeEntryDefinition() + { + } + + /// <summary> + /// Gets file mode. + /// </summary> + public virtual Mode Mode { get; private set; } + + /// <summary> + /// Gets the <see cref = "GitObjectType" /> of the target being pointed at. + /// </summary> + public virtual GitObjectType Type { get; private set; } + + /// <summary> + /// Gets the <see cref = "ObjectId" /> of the target being pointed at. + /// </summary> + public virtual ObjectId TargetId { get; private set; } + + internal virtual GitObject Target + { + get { return target.Value; } + } + + internal static TreeEntryDefinition From(TreeEntry treeEntry) + { + return new TreeEntryDefinition + { + Mode = treeEntry.Mode, + Type = treeEntry.Type, + TargetId = treeEntry.TargetId, + target = new Lazy<GitObject>(() => treeEntry.Target) + }; + } + + internal static TreeEntryDefinition From(Blob blob, Mode mode) + { + return new TreeEntryDefinition + { + Mode = mode, + Type = GitObjectType.Blob, + TargetId = blob.Id, + target = new Lazy<GitObject>(() => blob) + }; + } + + internal static TreeEntryDefinition From(Tree tree) + { + return new TreeEntryDefinition + { + Mode = Mode.Directory, + Type = GitObjectType.Tree, + TargetId = tree.Id, + target = new Lazy<GitObject>(() => tree) + }; + } + + /// <summary> + /// Determines whether the specified <see cref = "Object" /> is equal to the current <see cref = "TreeEntryDefinition" />. + /// </summary> + /// <param name = "obj">The <see cref = "Object" /> to compare with the current <see cref = "TreeEntryDefinition" />.</param> + /// <returns>True if the specified <see cref = "Object" /> is equal to the current <see cref = "TreeEntryDefinition" />; otherwise, false.</returns> + public override bool Equals(object obj) + { + return Equals(obj as TreeEntryDefinition); + } + + /// <summary> + /// Determines whether the specified <see cref = "TreeEntryDefinition" /> is equal to the current <see cref = "TreeEntryDefinition" />. + /// </summary> + /// <param name = "other">The <see cref = "TreeEntryDefinition" /> to compare with the current <see cref = "TreeEntryDefinition" />.</param> + /// <returns>True if the specified <see cref = "TreeEntryDefinition" /> is equal to the current <see cref = "TreeEntryDefinition" />; otherwise, false.</returns> + public bool Equals(TreeEntryDefinition other) + { + return equalityHelper.Equals(this, other); + } + + /// <summary> + /// Returns the hash code for this instance. + /// </summary> + /// <returns>A 32-bit signed integer hash code.</returns> + public override int GetHashCode() + { + return equalityHelper.GetHashCode(this); + } + + /// <summary> + /// Tests if two <see cref = "TreeEntryDefinition" /> are equal. + /// </summary> + /// <param name = "left">First <see cref = "TreeEntryDefinition" /> to compare.</param> + /// <param name = "right">Second <see cref = "TreeEntryDefinition" /> to compare.</param> + /// <returns>True if the two objects are equal; false otherwise.</returns> + public static bool operator ==(TreeEntryDefinition left, TreeEntryDefinition right) + { + return Equals(left, right); + } + + /// <summary> + /// Tests if two <see cref = "TreeEntryDefinition" /> are different. + /// </summary> + /// <param name = "left">First <see cref = "TreeEntryDefinition" /> to compare.</param> + /// <param name = "right">Second <see cref = "TreeEntryDefinition" /> to compare.</param> + /// <returns>True if the two objects are different; false otherwise.</returns> + public static bool operator !=(TreeEntryDefinition left, TreeEntryDefinition right) + { + return !Equals(left, right); + } + } + + internal abstract class TransientTreeEntryDefinition : TreeEntryDefinition + { + public override ObjectId TargetId + { + get { return ObjectId.Zero; } + } + + internal override GitObject Target + { + get { return null; } + } + } + + internal class TransientTreeTreeEntryDefinition : TransientTreeEntryDefinition + { + public override Mode Mode + { + get { return Mode.Directory; } + } + + public override GitObjectType Type + { + get { return GitObjectType.Tree; } + } + } +} |