diff --git a/LibGit2Sharp.Tests/ArchiveFixture.cs b/LibGit2Sharp.Tests/ArchiveFixture.cs new file mode 100644 index 000000000..e070db85d --- /dev/null +++ b/LibGit2Sharp.Tests/ArchiveFixture.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections; +using System.IO; +using LibGit2Sharp.Tests.TestHelpers; +using Xunit; + +namespace LibGit2Sharp.Tests +{ + public class ArchiveFixture : BaseFixture + { + [Fact] + public void CanArchiveATree() + { + using (var repo = new Repository(BareTestRepoPath)) + { + var tree = repo.Lookup("581f9824ecaf824221bd36edf5430f2739a7c4f5"); + + var archiver = new MockArchiver(); + + repo.ObjectDatabase.Archive(tree, archiver); + + var expected = new ArrayList + { + new { Path = "1", Sha = "7f76480d939dc401415927ea7ef25c676b8ddb8f" }, + new { Path = Path.Combine("1", "branch_file.txt"), Sha = "45b983be36b73c0788dc9cbcb76cbb80fc7bb057" }, + new { Path = "README", Sha = "a8233120f6ad708f843d861ce2b7228ec4e3dec6" }, + new { Path = "branch_file.txt", Sha = "45b983be36b73c0788dc9cbcb76cbb80fc7bb057" }, + new { Path = "new.txt", Sha = "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd" }, + }; + Assert.Equal(expected, archiver.Files); + Assert.Null(archiver.ReceivedCommitSha); + Assert.InRange(archiver.ModificationTime, DateTimeOffset.UtcNow.Subtract(TimeSpan.FromMilliseconds(100)), DateTimeOffset.UtcNow); + } + } + + [Fact] + public void CanArchiveACommit() + { + using (var repo = new Repository(BareTestRepoPath)) + { + var commit = repo.Lookup("4c062a6361ae6959e06292c1fa5e2822d9c96345"); + + var archiver = new MockArchiver(); + + repo.ObjectDatabase.Archive(commit, archiver); + + var expected = new ArrayList + { + new { Path = "1", Sha = "7f76480d939dc401415927ea7ef25c676b8ddb8f" }, + new { Path = Path.Combine("1", "branch_file.txt"), Sha = "45b983be36b73c0788dc9cbcb76cbb80fc7bb057" }, + new { Path = "README", Sha = "a8233120f6ad708f843d861ce2b7228ec4e3dec6" }, + new { Path = "branch_file.txt", Sha = "45b983be36b73c0788dc9cbcb76cbb80fc7bb057" }, + new { Path = "new.txt", Sha = "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd" }, + }; + Assert.Equal(expected, archiver.Files); + Assert.Equal(commit.Sha, archiver.ReceivedCommitSha); + Assert.Equal(commit.Committer.When, archiver.ModificationTime); + } + } + + [Fact] + public void ArchivingANullTreeOrCommitThrows() + { + using (var repo = new Repository(BareTestRepoPath)) + { + Assert.Throws(() => repo.ObjectDatabase.Archive((Commit)null, null)); + Assert.Throws(() => repo.ObjectDatabase.Archive((Tree)null, null)); + } + } + + #region MockArchiver + + private class MockArchiver : ArchiverBase + { + public readonly ArrayList Files = new ArrayList(); + public string ReceivedCommitSha; + public DateTimeOffset ModificationTime; + + #region Overrides of ArchiverBase + + public override void BeforeArchiving(Tree tree, ObjectId oid, DateTimeOffset modificationTime) + { + if (oid != null) + { + ReceivedCommitSha = oid.Sha; + } + ModificationTime = modificationTime; + } + + protected override void AddTreeEntry(string path, TreeEntry entry, DateTimeOffset modificationTime) + { + Files.Add(new { Path = path, entry.Target.Sha }); + } + + #endregion + } + + #endregion + } +} diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj index ab526ec12..5cf72bc21 100644 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj @@ -76,6 +76,7 @@ + diff --git a/LibGit2Sharp/ArchiverBase.cs b/LibGit2Sharp/ArchiverBase.cs new file mode 100644 index 000000000..8268c1075 --- /dev/null +++ b/LibGit2Sharp/ArchiverBase.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace LibGit2Sharp +{ + /// + /// The archiving method needs to be passed an inheritor of this class, which will then be used + /// to provide low-level archiving facilities (tar, zip, ...). + /// + /// + /// + /// + public abstract class ArchiverBase + { + /// + /// Override this method to perform operations before the archiving of each entry of the tree takes place. + /// + /// The tree that will be archived + /// The ObjectId of the commit being archived, or null if there is no commit. + /// The modification time that will be used for the files in the archive. + public virtual void BeforeArchiving(Tree tree, ObjectId oid, DateTimeOffset modificationTime) + { } + + /// + /// Override this method to perform operations after the archiving of each entry of the tree took place. + /// + /// The tree that was archived + /// The ObjectId of the commit being archived, or null if there is no commit. + /// The modification time that was used for the files in the archive. + public virtual void AfterArchiving(Tree tree, ObjectId oid, DateTimeOffset modificationTime) + { } + + internal void OrchestrateArchiving(Tree tree, ObjectId oid, DateTimeOffset modificationTime) + { + BeforeArchiving(tree, oid, modificationTime); + + ArchiveTree(tree, "", modificationTime); + + AfterArchiving(tree, oid, modificationTime); + } + + private void ArchiveTree(IEnumerable tree, string path, DateTimeOffset modificationTime) + { + foreach (var entry in tree) + { + AddTreeEntry(Path.Combine(path, entry.Name), entry, modificationTime); + + // Recurse if we have subtrees + if (entry.Mode == Mode.Directory) + { + ArchiveTree((Tree)entry.Target, Path.Combine(path, entry.Name), modificationTime); + } + } + } + + /// + /// Implements the archiving of a TreeEntry in a given format. + /// + /// The path of the entry in the archive. + /// The entry to archive. + /// The datetime the entry was last modified. + protected abstract void AddTreeEntry(string path, TreeEntry entry, DateTimeOffset modificationTime); + } +} diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index ed965eba9..b71344b34 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -56,6 +56,7 @@ + diff --git a/LibGit2Sharp/ObjectDatabase.cs b/LibGit2Sharp/ObjectDatabase.cs index a31681eaa..1fe0a890b 100644 --- a/LibGit2Sharp/ObjectDatabase.cs +++ b/LibGit2Sharp/ObjectDatabase.cs @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using LibGit2Sharp.Core; using LibGit2Sharp.Core.Handles; +using LibGit2Sharp.Handlers; namespace LibGit2Sharp { @@ -236,6 +237,32 @@ public virtual TagAnnotation CreateTagAnnotation(string name, GitObject target, return repo.Lookup(tagId); } + /// + /// Archive the given commit. + /// + /// The commit. + /// The archiver to use. + public virtual void Archive(Commit commit, ArchiverBase archiver) + { + Ensure.ArgumentNotNull(commit, "commit"); + Ensure.ArgumentNotNull(archiver, "archiver"); + + archiver.OrchestrateArchiving(commit.Tree, commit.Id, commit.Committer.When); + } + + /// + /// Archive the given tree. + /// + /// The tree. + /// The archiver to use. + public virtual void Archive(Tree tree, ArchiverBase archiver) + { + Ensure.ArgumentNotNull(tree, "tree"); + Ensure.ArgumentNotNull(archiver, "archiver"); + + archiver.OrchestrateArchiving(tree, null, DateTimeOffset.UtcNow); + } + /// /// Returns the merge base (best common ancestor) of the given commits /// and the distance between each of these commits and this base.