Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions LibGit2Sharp.Tests/SubmoduleFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,5 +150,23 @@ public void CanStageChangeInSubmoduleViaIndexStageWithOtherPaths(string submodul
Assert.Equal(SubmoduleStatus.IndexModified, statusAfter & SubmoduleStatus.IndexModified);
}
}

[Theory]
[InlineData("https://github.com/libgit2/TestGitRepository", false)]
[InlineData("https://github.com/libgit2/TestGitRepository", true)]
[InlineData("git://github.com/libgit2/TestGitRepository", true)]
// [InlineData("git://github.com/libgit2/libgit2sharp.git", true)]
public void CanAddASubmodule(string url, bool useGitLink)
{
var path = CloneStandardTestRepo();
using (var repo = new Repository(path))
{
var submodule = repo.Submodules.Add("test_submodule", url, null, useGitLink);
Assert.NotNull(submodule);

Assert.True(Directory.Exists(repo.Info.WorkingDirectory + Path.DirectorySeparatorChar + "test_submodule"));
Assert.True(File.Exists(repo.Info.WorkingDirectory + Path.DirectorySeparatorChar + @"test_submodule" + Path.DirectorySeparatorChar + "master.txt"));
}
}
}
}
12 changes: 12 additions & 0 deletions LibGit2Sharp/Core/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,18 @@ internal static extern StatusEntrySafeHandle git_status_byindex(
internal static extern void git_status_list_free(
IntPtr statusList);

[DllImport(libgit2)]
internal static extern int git_submodule_add_setup(
out SubmoduleSafeHandle reference,
RepositorySafeHandle repo,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath path,
bool use_gitlink);

[DllImport(libgit2)]
internal static extern int git_submodule_add_finalize(
SubmoduleSafeHandle submodule);

[DllImport(libgit2)]
internal static extern int git_submodule_lookup(
out SubmoduleSafeHandle reference,
Expand Down
20 changes: 20 additions & 0 deletions LibGit2Sharp/Core/Proxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2474,6 +2474,26 @@ public static ICollection<TResult> git_submodule_foreach<TResult>(RepositorySafe
return git_foreach(resultSelector, c => NativeMethods.git_submodule_foreach(repo, (x, y, p) => c(x, y, p), IntPtr.Zero));
}

public static SubmoduleSafeHandle git_submodule_add_setup(RepositorySafeHandle repo, string url, FilePath path, bool useGitLink)
{
using (ThreadAffinity())
{
SubmoduleSafeHandle sub;
var res = NativeMethods.git_submodule_add_setup(out sub, repo, url, path, useGitLink);
Ensure.ZeroResult(res);
return sub;
}
}

public static void git_submodule_add_finalize(SubmoduleSafeHandle submodule)
{
using (ThreadAffinity())
{
var res = NativeMethods.git_submodule_add_finalize(submodule);
Ensure.ZeroResult(res);
}
}

public static void git_submodule_add_to_index(SubmoduleSafeHandle submodule, bool write_index)
{
using (ThreadAffinity())
Expand Down
38 changes: 38 additions & 0 deletions LibGit2Sharp/SubmoduleCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using LibGit2Sharp.Core;
using LibGit2Sharp.Core.Handles;
Expand Down Expand Up @@ -32,6 +33,43 @@ internal SubmoduleCollection(Repository repo)
this.repo = repo;
}

/// <summary>
/// Adds a new repository, checkout the selected branch and add it to superproject index
/// </summary>
/// <remarks>New style: sub-repo goes in &lt;repo-dir&gt;/modules/&lt;name&gt;/ with a
/// gitlink in the sub-repo workdir directory to that repository
///
/// Old style: sub-repo goes directly into repo/&lt;name&gt;/.git/
/// </remarks>
/// <param name="relativePath">The path of the submodule inside of the super repository, if none, name is taken.</param>
/// <param name="url">The url of the remote repository</param>
/// <param name="committish">A revparse spec for the submodule.</param>
/// <param name="useGitLink">Use new style git subrepos or oldstyle.</param>
/// <returns></returns>
public virtual Submodule Add(string relativePath, string url, string committish = null, bool useGitLink = true)
{
Ensure.ArgumentNotNullOrEmptyString(relativePath, "name");
Ensure.ArgumentNotNullOrEmptyString(url, "url");

string subPath = Path.Combine(repo.Info.WorkingDirectory, relativePath);
Repository.Clone(url, subPath);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, changes made as per your suggestion.

I'm not sure it's the right thing to do tho, since the output is different to the way the git command itself works. For instance if I do a git submodule add repo I would end up with a .git file in my subrepo directory, the contents of which point back to my main .git directory at a subdirectory of that which then houses all the usual files and directories you'd expect to find. Doing the checkout and then running the submodule_add over the top of that houses all the indexing files and directories in .git directory of the subrepo.

It's only a small difference I'll grant you, those files need to live somewhere, but it's the kind of thing that could break in the future. I've done a bit of digging, and I think I know how to do the fetch of the remote head, so I'll plod on with that and do another commit later. You can then choose to take whichever implementation takes your fancy.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgive the placing of that comment, it should have been quite a bit lower. (github takes a bit of getting used to).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one other thing. Do I need to do another pull request for the updated change????

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one other thing. Do I need to do another pull request for the updated change????

No, you can just push your changes to the source branch of your pull request (e.g. your submodule_add branch).

The way I would expect this to work (from reading the libgit2 documentation, I have not tried this myself), is that you would:

  1. Call git_submodule_add_setup
  2. Clone or (fetch + checkout) the submodule repository
  3. Call git_submodule_add_finalize

I believe the useGitLink parameter controls whether the submodule workdir is contained in the .git/submodules directory, or directly in the main repository workdir. I am not sure which directory the fetch should be perfumed in if useGitLink is true.

Maybe @carlosmn has more specific guidance.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Submodules are more @arrbee 's thing, but I'll try go give some guidance on this.

We need to support the old method of storing the git-dir under the worktree for older git versions, but most tools should use the gitlink versions when creating new ones.

The old version is fairly straightforward to clone. For the new version, we would probably need to use git_clone_into(). I'm not sure if this is currently exposed in libgit2sharp.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I try and clone into the directory after I call git_submodule_add_setup then it barfs because the directory already exists. deleting it doesn't seem sensible. I will check, but the setting of use_git_link determines the behaviour of git_submodule_add_setup. (either new style or old style checkout)

git_submodule_add_setup does all the initial setup of the directory and git structure, but doesn't fetch or checkout. I think the git_submodule_add_finalize takes the revision of the submodule and does whatever it needs to do with that in the main git repo to set it as the version.

I'll work on getting the fetch and checkout working, and commit (probably tomorrow now). Can I just confirm that the remote head can be retrieved by examining the contents of the .git/refs/remotes/origin/HEAD in the submodule .git directory? Is there some programatic way I can get that in libgit2sharp??


using (SubmoduleSafeHandle handle = Proxy.git_submodule_add_setup(repo.Handle, url, relativePath, useGitLink))
{
if (committish != null)
{
using (var subRepo = new Repository(subPath))
{
subRepo.Checkout(committish);
}
}

Proxy.git_submodule_add_finalize(handle);
}

return this[relativePath];
}

/// <summary>
/// Gets the <see cref="LibGit2Sharp.Submodule"/> with the specified name.
/// </summary>
Expand Down