From 985b147763764449f30ff5d398d1f7e1e93bc045 Mon Sep 17 00:00:00 2001 From: Jason Goemaat Date: Thu, 26 Oct 2017 05:41:37 -0500 Subject: [PATCH 1/2] Add SubmoduleCollection.Add and test --- LibGit2Sharp.Tests/SubmoduleFixture.cs | 67 ++++++++++++++++++++++++++ LibGit2Sharp/Core/NativeMethods.cs | 29 +++++++++++ LibGit2Sharp/Core/Proxy.cs | 26 ++++++++++ LibGit2Sharp/SubmoduleCollection.cs | 15 ++++++ 4 files changed, 137 insertions(+) diff --git a/LibGit2Sharp.Tests/SubmoduleFixture.cs b/LibGit2Sharp.Tests/SubmoduleFixture.cs index 735bfd938..5b8c8761b 100644 --- a/LibGit2Sharp.Tests/SubmoduleFixture.cs +++ b/LibGit2Sharp.Tests/SubmoduleFixture.cs @@ -314,6 +314,70 @@ public void CanUpdateSubmoduleAfterCheckout() } } + [Fact] + public void CanAddSubmodule() + { + var path = SandboxStandardTestRepo(); + var pathSubRepoOrigin = SandboxStandardTestRepo(); + + string submoduleSubPath = "submodule_target_wd"; + string expectedSubmodulePath = Path.GetFullPath(Path.Combine(path, submoduleSubPath)); + string expectedSubmoduleUrl = pathSubRepoOrigin.Replace('\\', '/'); + ObjectId expectedCommitId = (ObjectId)"32eab9cb1f450b5fe7ab663462b77d7f4b703344"; + + using (var repo = new Repository(path)) + { + // setup config with dummy user so we can commit + CreateConfigurationWithDummyUser(repo, Constants.Identity); + + // check on adding config entry + var configEntryBeforeAdd = repo.Config.Get(string.Format("submodule.{0}.url", submoduleSubPath)); + Assert.Null(configEntryBeforeAdd); + + // first step is cloning the repository to where it goes + Repository.Clone(pathSubRepoOrigin, expectedSubmodulePath); + + // add submodule + repo.Submodules.Add(pathSubRepoOrigin, submoduleSubPath, 1); + Submodule submodule = repo.Submodules[submoduleSubPath]; + Assert.NotNull(submodule); + + // check that the expected commit is checked out, but not set in parent repo until committed + Assert.Equal(expectedCommitId, repo.Submodules[submoduleSubPath].WorkDirCommitId); + Assert.Null(repo.Submodules[submoduleSubPath].HeadCommitId); + + // check status + var submoduleStatus = submodule.RetrieveStatus(); + Assert.True((submoduleStatus & SubmoduleStatus.InIndex) == SubmoduleStatus.InIndex); + Assert.True((submoduleStatus & SubmoduleStatus.InConfig) == SubmoduleStatus.InConfig); + Assert.True((submoduleStatus & SubmoduleStatus.InWorkDir) == SubmoduleStatus.InWorkDir); + Assert.True((submoduleStatus & SubmoduleStatus.IndexAdded) == SubmoduleStatus.IndexAdded); + + // check that config entry was added with the correct url + var configEntryAfterAdd = repo.Config.Get(string.Format("submodule.{0}.url", submoduleSubPath)); + Assert.NotNull(configEntryAfterAdd); + Assert.Equal(expectedSubmoduleUrl, configEntryAfterAdd.Value); + + // check on directory being added and repository directory + Assert.True(Directory.Exists(expectedSubmodulePath)); + Assert.True(Directory.Exists(Path.Combine(expectedSubmodulePath, ".git"))); + + // manually check commit by opening submodule as a repository + using (var repo2 = new Repository(expectedSubmodulePath)) + { + Assert.False(repo2.Info.IsHeadDetached); + Assert.False(repo2.Info.IsHeadUnborn); + Commit headCommit = repo2.Head.Tip; + Assert.Equal(headCommit.Id, expectedCommitId); + } + + // commit parent repository, then verify it reports the correct CommitId for the submodule + Signature signature = repo.Config.BuildSignature(DateTimeOffset.Now); + repo.Commit("Added submodule " + submoduleSubPath, signature, signature); + Assert.Equal(expectedCommitId, repo.Submodules[submoduleSubPath].HeadCommitId); + } + } + [Fact] public void CanReadSubmoduleProperties() { @@ -322,6 +386,9 @@ public void CanReadSubmoduleProperties() using (var repo = new Repository(path)) { + // setup identity for commit we will do + CreateConfigurationWithDummyUser(repo, Constants.Identity); + var submodule = repo.Submodules[submoduleName]; Assert.Equal(SubmoduleUpdate.Checkout, submodule.UpdateRule); diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index b3e7f1297..36616336a 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -1601,6 +1601,35 @@ internal static extern unsafe void git_status_list_free( internal static extern void git_strarray_free( ref GitStrArray array); + /// + /// To fully emulate "git submodule add", call this function, then open + /// the submodule repo and perform the clone step as needed. Lastly, call + /// 'git_submodule_add_finalize()' to wrap up adding the new submodule and + /// .gitmodules to the index to be ready to commit. + /// + //[DllImport(libgit2)] + //internal static extern unsafe int git_submodule_add_setup( + // out git_submodule* reference, + // git_repository* repo, + // [CustomMarshaler(typeof(StrictUtf8Marshaler), typeof(string))] byte* url, + // [CustomMarshaler(typeof(StrictUtf8Marshaler), typeof(string))] byte* path, + // int use_gitlink); + [DllImport(libgit2)] + private static extern unsafe int git_submodule_add_setup( + out git_submodule* reference, + git_repository* repo, + [CustomMarshaler(typeof(StrictUtf8Marshaler), typeof(string))] byte* url, + [CustomMarshaler(typeof(StrictUtf8Marshaler), typeof(string))] byte* path, + int use_gitlink); + + [DllImport(libgit2)] + internal static extern unsafe int git_submodule_add_finalize( + git_submodule* submodule); + + [DllImport(libgit2)] + internal static extern unsafe string git_submodule_name( + git_submodule* submodule); + [DllImport(libgit2)] private static extern unsafe int git_submodule_lookup( out git_submodule* reference, diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs index c46d5fc1e..2a53fa7c9 100644 --- a/LibGit2Sharp/Core/Proxy.cs +++ b/LibGit2Sharp/Core/Proxy.cs @@ -2954,6 +2954,32 @@ public static unsafe int git_status_list_entrycount(StatusListHandle list) #region git_submodule_ + /// + /// This does "git submodule add" up to the fetch and checkout of the + /// submodule contents. It preps a new submodule, creates an entry in + /// .gitmodules and creates an empty initialized repository either at the + /// given path in the working directory or in .git/modules with a gitlink + /// from the working directory to the new repo. + /// s + public static unsafe SubmoduleHandle git_submodule_add_setup(RepositoryHandle repo, string url, string path, int useGitLink) + { + git_submodule* submodule; + var res = NativeMethods.git_submodule_add_setup(out submodule, repo, url, path, useGitLink); + Ensure.ZeroResult(res); + return new SubmoduleHandle(submodule, true); + } + + public static unsafe void git_submodule_add_finalize(SubmoduleHandle submodule) + { + var res = NativeMethods.git_submodule_add_finalize(submodule); + Ensure.ZeroResult(res); + } + + public static unsafe string git_submodule_name(SubmoduleHandle submodule) + { + return NativeMethods.git_submodule_name(submodule); + } + /// /// Returns a handle to the corresponding submodule, /// or an invalid handle if a submodule is not found. diff --git a/LibGit2Sharp/SubmoduleCollection.cs b/LibGit2Sharp/SubmoduleCollection.cs index fc508107a..939726ad6 100644 --- a/LibGit2Sharp/SubmoduleCollection.cs +++ b/LibGit2Sharp/SubmoduleCollection.cs @@ -47,6 +47,21 @@ public virtual Submodule this[string name] } } + /// + /// Add a new submodule from the given url to the given path. The + /// repository should have already been cloned into the destination + /// path. After this call the submodule and updated .gitmodules + /// file will be added to the index. + /// + /// Url to use for the submodule + /// Path of the submodule + /// Non-zero to use git link + public virtual void Add(string url, string path, int useGitLink) + { + SubmoduleHandle handle = Proxy.git_submodule_add_setup(repo.Handle, url, path, useGitLink); + Proxy.git_submodule_add_finalize(handle); + } + /// /// Initialize specified submodule. /// From 9c43f85350154f8908a31e79b055a4753d2d43d4 Mon Sep 17 00:00:00 2001 From: Jason Goemaat Date: Thu, 9 Nov 2017 17:56:33 -0600 Subject: [PATCH 2/2] Fix memory leak --- LibGit2Sharp/Core/NativeMethods.cs | 10 +++------- LibGit2Sharp/SubmoduleCollection.cs | 1 + 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index 36616336a..96bf8310b 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -1606,14 +1606,10 @@ internal static extern void git_strarray_free( /// the submodule repo and perform the clone step as needed. Lastly, call /// 'git_submodule_add_finalize()' to wrap up adding the new submodule and /// .gitmodules to the index to be ready to commit. + /// + /// In our case, go ahead and do the clone first. The setup can be done + /// with the directory contents already in place. /// - //[DllImport(libgit2)] - //internal static extern unsafe int git_submodule_add_setup( - // out git_submodule* reference, - // git_repository* repo, - // [CustomMarshaler(typeof(StrictUtf8Marshaler), typeof(string))] byte* url, - // [CustomMarshaler(typeof(StrictUtf8Marshaler), typeof(string))] byte* path, - // int use_gitlink); [DllImport(libgit2)] private static extern unsafe int git_submodule_add_setup( out git_submodule* reference, diff --git a/LibGit2Sharp/SubmoduleCollection.cs b/LibGit2Sharp/SubmoduleCollection.cs index 939726ad6..47a292e55 100644 --- a/LibGit2Sharp/SubmoduleCollection.cs +++ b/LibGit2Sharp/SubmoduleCollection.cs @@ -60,6 +60,7 @@ public virtual void Add(string url, string path, int useGitLink) { SubmoduleHandle handle = Proxy.git_submodule_add_setup(repo.Handle, url, path, useGitLink); Proxy.git_submodule_add_finalize(handle); + handle.Free(); } ///