diff options
author | nulltoken <emeric.fermas@gmail.com> | 2012-04-13 19:07:35 +0400 |
---|---|---|
committer | nulltoken <emeric.fermas@gmail.com> | 2012-05-16 01:57:34 +0400 |
commit | 84c16528d57db4c8fc24e60262a1c89ed5e634dc (patch) | |
tree | a5ee7adf685261e4ee097eed61f6a9ea504b5fcf | |
parent | 5c5b9c7e8937b9bcf80927dedd95f35abae2b903 (diff) |
Add RepositoryOptions to provide the Repository with overridden working directory and/or index
This also allows to work against a bare repository as if it was a standard one.
Partially fixes #127.
-rw-r--r-- | LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj | 1 | ||||
-rw-r--r-- | LibGit2Sharp.Tests/RepositoryOptionsFixture.cs | 154 | ||||
-rw-r--r-- | LibGit2Sharp/Core/NativeMethods.cs | 15 | ||||
-rw-r--r-- | LibGit2Sharp/Index.cs | 14 | ||||
-rw-r--r-- | LibGit2Sharp/LibGit2Sharp.csproj | 1 | ||||
-rw-r--r-- | LibGit2Sharp/Repository.cs | 51 | ||||
-rw-r--r-- | LibGit2Sharp/RepositoryOptions.cs | 29 |
7 files changed, 253 insertions, 12 deletions
diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj index 08d35190..02341209 100644 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj @@ -49,6 +49,7 @@ <Compile Include="AttributesFixture.cs" /> <Compile Include="ObjectDatabaseFixture.cs" /> <Compile Include="DiffFixture.cs" /> + <Compile Include="RepositoryOptionsFixture.cs" /> <Compile Include="ResetFixture.cs" /> <Compile Include="LazyFixture.cs" /> <Compile Include="RemoteFixture.cs" /> diff --git a/LibGit2Sharp.Tests/RepositoryOptionsFixture.cs b/LibGit2Sharp.Tests/RepositoryOptionsFixture.cs new file mode 100644 index 00000000..c7938475 --- /dev/null +++ b/LibGit2Sharp.Tests/RepositoryOptionsFixture.cs @@ -0,0 +1,154 @@ +using System; +using System.IO; +using LibGit2Sharp.Tests.TestHelpers; +using Xunit; + +namespace LibGit2Sharp.Tests +{ + public class RepositoryOptionsFixture : BaseFixture + { + private readonly string newWorkdir; + private readonly string newIndex; + + //TODO: Add facts ensuring the correct opening of a workdir/index through relative and absolute paths + + public RepositoryOptionsFixture() + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + + newWorkdir = Path.Combine(scd.DirectoryPath, "wd"); + Directory.CreateDirectory(newWorkdir); + + newIndex = Path.Combine(scd.DirectoryPath, "my-index"); + } + + [Fact] + public void CanOpenABareRepoAsIfItWasAStandardOne() + { + File.Copy(Path.Combine(StandardTestRepoPath, "index"), newIndex); + + var options = new RepositoryOptions { WorkingDirectoryPath = newWorkdir, IndexPath = newIndex }; + + using (var repo = new Repository(BareTestRepoPath, options)) + { + var st = repo.Index.RetrieveStatus("1/branch_file.txt"); + Assert.Equal(FileStatus.Missing, st); + } + } + + [Fact] + public void CanOpenABareRepoAsIfItWasAStandardOneWithANonExisitingIndexFile() + { + var options = new RepositoryOptions { WorkingDirectoryPath = newWorkdir, IndexPath = newIndex }; + + using (var repo = new Repository(BareTestRepoPath, options)) + { + var st = repo.Index.RetrieveStatus("1/branch_file.txt"); + Assert.Equal(FileStatus.Removed, st); + } + } + + [Fact] + public void CanProvideADifferentWorkDirToAStandardRepo() + { + var scd = BuildTemporaryCloneOfTestRepo(StandardTestRepoWorkingDirPath); + + using (var repo = new Repository(scd.DirectoryPath)) + { + Assert.Equal(FileStatus.Unaltered, repo.Index.RetrieveStatus("1/branch_file.txt")); + } + + var options = new RepositoryOptions { WorkingDirectoryPath = newWorkdir }; + + scd = BuildTemporaryCloneOfTestRepo(StandardTestRepoWorkingDirPath); + using (var repo = new Repository(scd.DirectoryPath, options)) + { + Assert.Equal(FileStatus.Missing, repo.Index.RetrieveStatus("1/branch_file.txt")); + } + } + + [Fact] + public void CanProvideADifferentIndexToAStandardRepo() + { + var scd = BuildTemporaryCloneOfTestRepo(StandardTestRepoWorkingDirPath); + + using (var repo = new Repository(scd.DirectoryPath)) + { + Assert.Equal(FileStatus.Untracked, repo.Index.RetrieveStatus("new_untracked_file.txt")); + + repo.Index.Stage("new_untracked_file.txt"); + + Assert.Equal(FileStatus.Added, repo.Index.RetrieveStatus("new_untracked_file.txt")); + + File.Copy(Path.Combine(repo.Info.Path, "index"), newIndex); + } + + var options = new RepositoryOptions { IndexPath = newIndex }; + + scd = BuildTemporaryCloneOfTestRepo(StandardTestRepoWorkingDirPath); + using (var repo = new Repository(scd.DirectoryPath, options)) + { + Assert.Equal(FileStatus.Added, repo.Index.RetrieveStatus("new_untracked_file.txt")); + } + } + + [Fact] + public void OpeningABareRepoWithoutProvidingBothWorkDirAndIndexThrows() + { + Repository repo; + + Assert.Throws<ArgumentException>(() => repo = new Repository(BareTestRepoPath, new RepositoryOptions { IndexPath = newIndex })); + Assert.Throws<ArgumentException>(() => repo = new Repository(BareTestRepoPath, new RepositoryOptions { WorkingDirectoryPath = newWorkdir })); + } + + [Fact] + public void OpeningARepoWithAnEmptyRepositoryOptionsThrows() + { + var options = new RepositoryOptions(); + Repository repo; + + Assert.Throws<ArgumentException>(() => repo = new Repository(BareTestRepoPath, options)); + Assert.Throws<ArgumentException>(() => repo = new Repository(StandardTestRepoPath, options)); + } + + [Fact] + public void CanSneakAdditionalCommitsIntoAStandardRepoWithoutAlteringTheWorkdirOrTheIndex() + { + var scd = BuildTemporaryCloneOfTestRepo(StandardTestRepoWorkingDirPath); + + using (var repo = new Repository(scd.DirectoryPath)) + { + Branch head = repo.Head; + + Assert.Equal(FileStatus.Nonexistent, repo.Index.RetrieveStatus("zomg.txt")); + + string commitSha = MeanwhileInAnotherDimensionAnEvilMastermindIsAtWork(scd.DirectoryPath); + + Branch newHead = repo.Head; + + Assert.NotEqual(head.Tip.Sha, newHead.Tip.Sha); + Assert.Equal(commitSha, newHead.Tip.Sha); + + Assert.Equal(FileStatus.Removed, repo.Index.RetrieveStatus("zomg.txt")); + } + } + + private string MeanwhileInAnotherDimensionAnEvilMastermindIsAtWork(string workingDirectoryPath) + { + var options = new RepositoryOptions { WorkingDirectoryPath = newWorkdir, IndexPath = newIndex }; + + using (var sneakyRepo = new Repository(workingDirectoryPath, options)) + { + Assert.Equal(Path.GetFullPath(newWorkdir) + Path.DirectorySeparatorChar, Path.GetFullPath(sneakyRepo.Info.WorkingDirectory)); + + sneakyRepo.Reset(ResetOptions.Mixed, sneakyRepo.Head.Tip.Sha); + + var filepath = Path.Combine(sneakyRepo.Info.WorkingDirectory, "zomg.txt"); + File.WriteAllText(filepath, "I'm being sneaked in!\n"); + + sneakyRepo.Index.Stage(filepath); + return sneakyRepo.Commit("Tadaaaa!", DummySignature, DummySignature).Sha; + } + } + } +} diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index 24cebf42..3f9b0660 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -307,6 +307,11 @@ namespace LibGit2Sharp.Core public static extern IndexEntrySafeHandle git_index_get(IndexSafeHandle index, uint n); [DllImport(libgit2)] + public static extern int git_index_open( + out IndexSafeHandle index, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(FilePathMarshaler))] FilePath indexpath); + + [DllImport(libgit2)] public static extern int git_index_read_tree(IndexSafeHandle index, GitObjectSafeHandle tree); [DllImport(libgit2)] @@ -473,6 +478,16 @@ namespace LibGit2Sharp.Core public static extern FilePath git_repository_path(RepositorySafeHandle repository); [DllImport(libgit2)] + public static extern void git_repository_set_index( + RepositorySafeHandle repository, + IndexSafeHandle index); + + [DllImport(libgit2)] + public static extern int git_repository_set_workdir( + RepositorySafeHandle repository, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(FilePathMarshaler))] FilePath workdir); + + [DllImport(libgit2)] [return : MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(FilePathMarshaler))] public static extern FilePath git_repository_workdir(RepositorySafeHandle repository); diff --git a/LibGit2Sharp/Index.cs b/LibGit2Sharp/Index.cs index 470ebca1..69d9fa16 100644 --- a/LibGit2Sharp/Index.cs +++ b/LibGit2Sharp/Index.cs @@ -23,8 +23,18 @@ namespace LibGit2Sharp internal Index(Repository repo) { this.repo = repo; - int res = NativeMethods.git_repository_index(out handle, repo.Handle); - Ensure.Success(res); + + Ensure.Success(NativeMethods.git_repository_index(out handle, repo.Handle)); + + repo.RegisterForCleanup(handle); + } + + internal Index(Repository repo, string indexPath) + { + this.repo = repo; + + Ensure.Success(NativeMethods.git_index_open(out handle, indexPath)); + NativeMethods.git_repository_set_index(repo.Handle, handle); repo.RegisterForCleanup(handle); } diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index 3372f8e6..e20b4162 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -133,6 +133,7 @@ <Compile Include="RepositoryInformation.cs" /> <Compile Include="RepositoryExtensions.cs" /> <Compile Include="Core\Handles\RepositorySafeHandle.cs" /> + <Compile Include="RepositoryOptions.cs" /> <Compile Include="RepositoryStatus.cs" /> <Compile Include="ResetOptions.cs" /> <Compile Include="Signature.cs" /> diff --git a/LibGit2Sharp/Repository.cs b/LibGit2Sharp/Repository.cs index bcee8867..2bd09b94 100644 --- a/LibGit2Sharp/Repository.cs +++ b/LibGit2Sharp/Repository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Reflection; using LibGit2Sharp.Core; @@ -23,33 +24,63 @@ namespace LibGit2Sharp private readonly TagCollection tags; private readonly Lazy<RepositoryInformation> info; private readonly Diff diff; - private readonly bool isBare; private readonly Lazy<ObjectDatabase> odb; private readonly Stack<SafeHandleBase> handlesToCleanup = new Stack<SafeHandleBase>(); private static readonly Lazy<string> versionRetriever = new Lazy<string>(RetrieveVersion); /// <summary> - /// Initializes a new instance of the <see cref = "Repository" /> class. + /// Initializes a new instance of the <see cref = "Repository" /> class, providing ooptional behavioral overrides through <paramref name="options"/> parameter. /// <para>For a standard repository, <paramref name = "path" /> should either point to the ".git" folder or to the working directory. For a bare repository, <paramref name = "path" /> should directly point to the repository folder.</para> /// </summary> /// <param name = "path"> - /// The path to the git repository to open, can be either the path to the git directory (for non-bare repositories this + /// The path to the git repository to open, can be either the path to the git directory (for non-bare repositories this /// would be the ".git" folder inside the working directory) or the path to the working directory. /// </param> - public Repository(string path) + /// <param name="options"> + /// Overrides to the way a repository is opened. + /// </param> + public Repository(string path, RepositoryOptions options = null) { Ensure.ArgumentNotNullOrEmptyString(path, "path"); - int res = NativeMethods.git_repository_open(out handle, path); - Ensure.Success(res); - + Ensure.Success(NativeMethods.git_repository_open(out handle, path)); RegisterForCleanup(handle); - isBare = NativeMethods.RepositoryStateChecker(handle, NativeMethods.git_repository_is_bare); + bool isBare = NativeMethods.RepositoryStateChecker(handle, NativeMethods.git_repository_is_bare); + + Func<Index> indexBuilder = () => new Index(this); + + if (options != null) + { + bool isWorkDirNull = string.IsNullOrEmpty(options.WorkingDirectoryPath); + bool isIndexNull = string.IsNullOrEmpty(options.IndexPath); + + if (isWorkDirNull && isIndexNull) + { + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "At least one member of the {0} instance has to be provided.", typeof(RepositoryOptions).Name)); + } + + if (isBare && (isWorkDirNull ^ isIndexNull)) + { + throw new ArgumentException("When overriding the opening of a bare repository, both RepositoryOptions.WorkingDirectoryPath an RepositoryOptions.IndexPath have to be provided."); + } + + isBare = false; + + if (!isIndexNull) + { + indexBuilder = () => new Index(this, options.IndexPath); + } + + if (!isWorkDirNull) + { + Ensure.Success(NativeMethods.git_repository_set_workdir(handle, options.WorkingDirectoryPath)); + } + } if (!isBare) { - index = new Index(this); + index = indexBuilder(); } commits = new CommitCollection(this); @@ -462,7 +493,7 @@ namespace LibGit2Sharp Assembly assembly = typeof(Repository).Assembly; Version version = assembly.GetName().Version; - + string libgit2Hash = ReadContentFromResource(assembly, "libgit2_hash.txt"); string libgit2sharpHash = ReadContentFromResource(assembly, "libgit2sharp_hash.txt"); diff --git a/LibGit2Sharp/RepositoryOptions.cs b/LibGit2Sharp/RepositoryOptions.cs new file mode 100644 index 00000000..507288d6 --- /dev/null +++ b/LibGit2Sharp/RepositoryOptions.cs @@ -0,0 +1,29 @@ +namespace LibGit2Sharp +{ + /// <summary> + /// Provides optional additional information to the Repository to be opened. + /// </summary> + public class RepositoryOptions + { + /// <summary> + /// Overrides the probed location of the working directory of a standard repository, + /// or, combined with <see cref = "IndexPath" />, would + /// allow to work against a bare repository as it was a standard one. + /// <para> + /// The path has to lead to an existing directory. + /// </para> + /// </summary> + public string WorkingDirectoryPath { get; set; } + + /// <summary> + /// Overrides the probed location of the Index file of a standard repository, + /// or, combined with <see cref = "WorkingDirectoryPath" />, would + /// allow to work against a bare repository as it was a standard one. + /// <para> + /// The path has either to lead to an existing valid Index file, + /// or to a non existent Index file which will be eventually created. + /// </para> + /// </summary> + public string IndexPath { get; set; } + } +} |