Skip to content

Commit 9f1bb67

Browse files
committed
Add SubmoduleCollection.Add and test
1 parent 2f8ad26 commit 9f1bb67

4 files changed

Lines changed: 133 additions & 0 deletions

File tree

LibGit2Sharp.Tests/SubmoduleFixture.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,69 @@ public void CanUpdateSubmoduleAfterCheckout()
314314
}
315315
}
316316

317+
[Fact]
318+
public void CanAddSubmodule()
319+
{
320+
var path = SandboxStandardTestRepo();
321+
var pathSubRepoOrigin = SandboxStandardTestRepo();
322+
323+
string submoduleSubPath = "submodule_target_wd";
324+
string expectedSubmodulePath = Path.GetFullPath(Path.Combine(path, submoduleSubPath));
325+
string expectedSubmoduleUrl = pathSubRepoOrigin.Replace('\\', '/');
326+
ObjectId expectedCommitId = (ObjectId)"32eab9cb1f450b5fe7ab663462b77d7f4b703344";
327+
328+
using (var repo = new Repository(path))
329+
{
330+
// setup config with dummy user so we can commit
331+
CreateConfigurationWithDummyUser(repo, Constants.Identity);
332+
333+
// check on adding config entry
334+
var configEntryBeforeAdd = repo.Config.Get<string>(string.Format("submodule.{0}.url", submoduleSubPath));
335+
Assert.Null(configEntryBeforeAdd);
336+
337+
// first step is cloning the repository to where it goes
338+
Repository.Clone(pathSubRepoOrigin, expectedSubmodulePath);
339+
340+
// add submodule
341+
repo.Submodules.Add(pathSubRepoOrigin, submoduleSubPath, 1);
342+
Submodule submodule = repo.Submodules[submoduleSubPath];
343+
Assert.NotNull(submodule);
344+
345+
// check that the expected commit is checked out, but not set in parent repo until committed
346+
Assert.Equal(expectedCommitId, repo.Submodules[submoduleSubPath].WorkDirCommitId);
347+
Assert.Null(repo.Submodules[submoduleSubPath].HeadCommitId);
348+
349+
// check status
350+
var submoduleStatus = submodule.RetrieveStatus();
351+
Assert.True((submoduleStatus & SubmoduleStatus.InIndex) == SubmoduleStatus.InIndex);
352+
Assert.True((submoduleStatus & SubmoduleStatus.InConfig) == SubmoduleStatus.InConfig);
353+
Assert.True((submoduleStatus & SubmoduleStatus.InWorkDir) == SubmoduleStatus.InWorkDir);
354+
Assert.True((submoduleStatus & SubmoduleStatus.IndexAdded) == SubmoduleStatus.IndexAdded);
355+
356+
// check that config entry was added with the correct url
357+
var configEntryAfterAdd = repo.Config.Get<string>(string.Format("submodule.{0}.url", submoduleSubPath));
358+
Assert.NotNull(configEntryAfterAdd);
359+
Assert.Equal(expectedSubmoduleUrl, configEntryAfterAdd.Value);
360+
361+
// check on directory being added
362+
Assert.True(Directory.Exists(expectedSubmodulePath));
363+
364+
// manually check commit by opening submodule as a repository
365+
using (var repo2 = new Repository(expectedSubmodulePath))
366+
{
367+
Assert.False(repo2.Info.IsHeadDetached);
368+
Assert.False(repo2.Info.IsHeadUnborn);
369+
Commit headCommit = repo2.Head.Tip;
370+
Assert.Equal(headCommit.Id, expectedCommitId);
371+
}
372+
373+
// commit parent repository, then verify it reports the correct CommitId for the submodule
374+
Signature signature = repo.Config.BuildSignature(DateTimeOffset.Now);
375+
repo.Commit("Added submodule " + submoduleSubPath, signature, signature);
376+
Assert.Equal(expectedCommitId, repo.Submodules[submoduleSubPath].HeadCommitId);
377+
}
378+
}
379+
317380
[Fact]
318381
public void CanReadSubmoduleProperties()
319382
{

LibGit2Sharp/Core/NativeMethods.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1601,6 +1601,35 @@ internal static extern unsafe void git_status_list_free(
16011601
internal static extern void git_strarray_free(
16021602
ref GitStrArray array);
16031603

1604+
/// <summary>
1605+
/// To fully emulate "git submodule add", call this function, then open
1606+
/// the submodule repo and perform the clone step as needed. Lastly, call
1607+
/// 'git_submodule_add_finalize()' to wrap up adding the new submodule and
1608+
/// .gitmodules to the index to be ready to commit.
1609+
/// </summary>
1610+
//[DllImport(libgit2)]
1611+
//internal static extern unsafe int git_submodule_add_setup(
1612+
// out git_submodule* reference,
1613+
// git_repository* repo,
1614+
// [CustomMarshaler(typeof(StrictUtf8Marshaler), typeof(string))] byte* url,
1615+
// [CustomMarshaler(typeof(StrictUtf8Marshaler), typeof(string))] byte* path,
1616+
// int use_gitlink);
1617+
[DllImport(libgit2)]
1618+
private static extern unsafe int git_submodule_add_setup(
1619+
out git_submodule* reference,
1620+
git_repository* repo,
1621+
[CustomMarshaler(typeof(StrictUtf8Marshaler), typeof(string))] byte* url,
1622+
[CustomMarshaler(typeof(StrictUtf8Marshaler), typeof(string))] byte* path,
1623+
int use_gitlink);
1624+
1625+
[DllImport(libgit2)]
1626+
internal static extern unsafe int git_submodule_add_finalize(
1627+
git_submodule* submodule);
1628+
1629+
[DllImport(libgit2)]
1630+
internal static extern unsafe string git_submodule_name(
1631+
git_submodule* submodule);
1632+
16041633
[DllImport(libgit2)]
16051634
private static extern unsafe int git_submodule_lookup(
16061635
out git_submodule* reference,

LibGit2Sharp/Core/Proxy.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2954,6 +2954,32 @@ public static unsafe int git_status_list_entrycount(StatusListHandle list)
29542954

29552955
#region git_submodule_
29562956

2957+
/// <summary>
2958+
/// This does "git submodule add" up to the fetch and checkout of the
2959+
/// submodule contents. It preps a new submodule, creates an entry in
2960+
/// .gitmodules and creates an empty initialized repository either at the
2961+
/// given path in the working directory or in .git/modules with a gitlink
2962+
/// from the working directory to the new repo.
2963+
/// </summary>s
2964+
public static unsafe SubmoduleHandle git_submodule_add_setup(RepositoryHandle repo, string url, string path, int useGitLink)
2965+
{
2966+
git_submodule* submodule;
2967+
var res = NativeMethods.git_submodule_add_setup(out submodule, repo, url, path, useGitLink);
2968+
Ensure.ZeroResult(res);
2969+
return new SubmoduleHandle(submodule, true);
2970+
}
2971+
2972+
public static unsafe void git_submodule_add_finalize(SubmoduleHandle submodule)
2973+
{
2974+
var res = NativeMethods.git_submodule_add_finalize(submodule);
2975+
Ensure.ZeroResult(res);
2976+
}
2977+
2978+
public static unsafe string git_submodule_name(SubmoduleHandle submodule)
2979+
{
2980+
return NativeMethods.git_submodule_name(submodule);
2981+
}
2982+
29572983
/// <summary>
29582984
/// Returns a handle to the corresponding submodule,
29592985
/// or an invalid handle if a submodule is not found.

LibGit2Sharp/SubmoduleCollection.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,21 @@ public virtual Submodule this[string name]
4747
}
4848
}
4949

50+
/// <summary>
51+
/// Add a new submodule from the given url to the given path. The
52+
/// repository should have already been cloned into the destination
53+
/// path. After this call the submodule and updated .gitmodules
54+
/// file will be added to the index.
55+
/// </summary>
56+
/// <param name="url">Url to use for the submodule</param>
57+
/// <param name="path">Path of the submodule</param>
58+
/// <param name="useGitLink">Non-zero to use git link</param>
59+
public virtual void Add(string url, string path, int useGitLink)
60+
{
61+
SubmoduleHandle handle = Proxy.git_submodule_add_setup(repo.Handle, url, path, useGitLink);
62+
Proxy.git_submodule_add_finalize(handle);
63+
}
64+
5065
/// <summary>
5166
/// Initialize specified submodule.
5267
/// <para>

0 commit comments

Comments
 (0)