Skip to content

Make Pull and Fetch commands #1288

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 28, 2016
Merged
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
32 changes: 16 additions & 16 deletions LibGit2Sharp.Tests/FetchFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public void CanFetchIntoAnEmptyRepository(string url)

using (var repo = new Repository(path))
{
Remote remote = repo.Network.Remotes.Add(remoteName, url);
repo.Network.Remotes.Add(remoteName, url);
Copy link
Member

Choose a reason for hiding this comment

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

Wouldn't this cause an issue, since Remote is now IDisposable?

I know this is a test method, but we should show the _right thing_™

Copy link
Member Author

Choose a reason for hiding this comment

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

It's in the same situation as before. We dispose of the remotes on repository disposal.


// Set up structures for the expected results
// and verifying the RemoteUpdateTips callback.
Expand All @@ -44,7 +44,7 @@ public void CanFetchIntoAnEmptyRepository(string url)
}

// Perform the actual fetch
repo.Network.Fetch(remote, new FetchOptions { OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler });
Commands.Fetch(repo, remoteName, new string[0], new FetchOptions { OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler }, null);

// Verify the expected
expectedFetchState.CheckUpdatedReferences(repo);
Expand All @@ -61,13 +61,13 @@ public void CanFetchIntoAnEmptyRepositoryWithCredentials()

using (var repo = new Repository(path))
{
Remote remote = repo.Network.Remotes.Add(remoteName, Constants.PrivateRepoUrl);
repo.Network.Remotes.Add(remoteName, Constants.PrivateRepoUrl);

// Perform the actual fetch
repo.Network.Fetch(remote, new FetchOptions
Commands.Fetch(repo, remoteName, new string[0], new FetchOptions
{
CredentialsProvider = Constants.PrivateRepoCredentials
});
}, null);
}
}

Expand All @@ -81,7 +81,7 @@ public void CanFetchAllTagsIntoAnEmptyRepository(string url)

using (var repo = new Repository(path))
{
Remote remote = repo.Network.Remotes.Add(remoteName, url);
repo.Network.Remotes.Add(remoteName, url);

// Set up structures for the expected results
// and verifying the RemoteUpdateTips callback.
Expand All @@ -101,10 +101,10 @@ public void CanFetchAllTagsIntoAnEmptyRepository(string url)
}

// Perform the actual fetch
repo.Network.Fetch(remote, new FetchOptions {
Commands.Fetch(repo, remoteName, new string[0], new FetchOptions {
TagFetchMode = TagFetchMode.All,
OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler
});
}, null);

// Verify the expected
expectedFetchState.CheckUpdatedReferences(repo);
Expand All @@ -124,7 +124,7 @@ public void CanFetchCustomRefSpecsIntoAnEmptyRepository(string url, string local

using (var repo = new Repository(path))
{
Remote remote = repo.Network.Remotes.Add(remoteName, url);
repo.Network.Remotes.Add(remoteName, url);

string refSpec = string.Format("refs/heads/{2}:refs/remotes/{0}/{1}", remoteName, localBranchName, remoteBranchName);

Expand All @@ -147,10 +147,10 @@ public void CanFetchCustomRefSpecsIntoAnEmptyRepository(string url, string local
}

// Perform the actual fetch
repo.Network.Fetch(remote, new string[] { refSpec }, new FetchOptions {
Commands.Fetch(repo, remoteName, new string[] { refSpec }, new FetchOptions {
TagFetchMode = TagFetchMode.None,
OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler
});
}, null);

// Verify the expected
expectedFetchState.CheckUpdatedReferences(repo);
Expand Down Expand Up @@ -181,7 +181,7 @@ public void FetchRespectsConfiguredAutoTagSetting(TagFetchMode tagFetchMode, int
r => r.TagFetchMode = tagFetchMode);

// Perform the actual fetch.
repo.Network.Fetch(remote);
Commands.Fetch(repo, remoteName, new string[0], null, null);

// Verify the number of fetched tags.
Assert.Equal(expectedTagCount, repo.Tags.Count());
Expand All @@ -199,7 +199,7 @@ public void CanFetchAllTagsAfterAnInitialClone()

using (var repo = new Repository(clonedRepoPath))
{
repo.Fetch("origin", new FetchOptions { TagFetchMode = TagFetchMode.All });
Commands.Fetch(repo, "origin", new string[0], new FetchOptions { TagFetchMode = TagFetchMode.All }, null);
}
}

Expand All @@ -225,17 +225,17 @@ public void FetchHonorsTheFetchPruneConfigurationEntry()

// No pruning when the configuration entry isn't defined
Assert.Null(clonedRepo.Config.Get<bool>("fetch.prune"));
clonedRepo.Fetch("origin");
Commands.Fetch(clonedRepo, "origin", new string[0], null, null);
Assert.Equal(5, clonedRepo.Branches.Count(b => b.IsRemote));

// No pruning when the configuration entry is set to false
clonedRepo.Config.Set<bool>("fetch.prune", false);
clonedRepo.Fetch("origin");
Commands.Fetch(clonedRepo, "origin", new string[0], null, null);
Assert.Equal(5, clonedRepo.Branches.Count(b => b.IsRemote));

// Auto pruning when the configuration entry is set to true
clonedRepo.Config.Set<bool>("fetch.prune", true);
clonedRepo.Fetch("origin");
Commands.Fetch(clonedRepo, "origin", new string[0], null, null);
Assert.Equal(4, clonedRepo.Branches.Count(b => b.IsRemote));
}
}
Expand Down
18 changes: 7 additions & 11 deletions LibGit2Sharp.Tests/NetworkFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ public void CanPull(FastForwardStrategy fastForwardStrategy)
}
};

MergeResult mergeResult = repo.Network.Pull(Constants.Signature, pullOptions);
MergeResult mergeResult = Commands.Pull(repo, Constants.Signature, pullOptions);

if(fastForwardStrategy == FastForwardStrategy.Default || fastForwardStrategy == FastForwardStrategy.FastForwardOnly)
{
Expand Down Expand Up @@ -197,7 +197,7 @@ public void CanPullIntoEmptyRepo()
b => b.UpstreamBranch = "refs/heads/master");

// Pull!
MergeResult mergeResult = repo.Network.Pull(Constants.Signature, new PullOptions());
MergeResult mergeResult = Commands.Pull(repo, Constants.Signature, new PullOptions());

Assert.Equal(mergeResult.Status, MergeStatus.FastForward);
Assert.Equal(mergeResult.Commit, repo.Branches["refs/remotes/origin/master"].Tip);
Expand All @@ -224,7 +224,7 @@ public void PullWithoutMergeBranchThrows()

try
{
repo.Network.Pull(Constants.Signature, new PullOptions());
Commands.Pull(repo, Constants.Signature, new PullOptions());
}
catch(MergeFetchHeadNotFoundException ex)
{
Expand Down Expand Up @@ -252,10 +252,7 @@ public void CanMergeFetchedRefs()
Assert.False(repo.RetrieveStatus().Any());
Assert.Equal(repo.Lookup<Commit>("refs/remotes/origin/master~1"), repo.Head.Tip);

using (var remote = repo.Network.Remotes[repo.Head.RemoteName])
{
repo.Network.Fetch(remote);
}
Commands.Fetch(repo, repo.Head.RemoteName, new string[0], null, null);

MergeOptions mergeOptions = new MergeOptions()
{
Expand All @@ -282,8 +279,7 @@ public void CanPruneRefs()
using (var repo = new Repository(clonedRepoPath))
{
repo.Network.Remotes.Add("pruner", clonedRepoPath2);
var remote = repo.Network.Remotes["pruner"];
repo.Network.Fetch(remote);
Commands.Fetch(repo, "pruner", new string[0], null, null);
Assert.NotNull(repo.Refs["refs/remotes/pruner/master"]);

// Remove the branch from the source repository
Expand All @@ -293,11 +289,11 @@ public void CanPruneRefs()
}

// and by default we don't prune it
repo.Network.Fetch(remote);
Commands.Fetch(repo, "pruner", new string[0], null, null);
Assert.NotNull(repo.Refs["refs/remotes/pruner/master"]);

// but we do when asked by the user
repo.Network.Fetch(remote, new FetchOptions { Prune = true} );
Commands.Fetch(repo, "pruner", new string[0], new FetchOptions { Prune = true}, null);
Assert.Null(repo.Refs["refs/remotes/pruner/master"]);
}
}
Expand Down
6 changes: 3 additions & 3 deletions LibGit2Sharp.Tests/RepositoryFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ public void CanFetchFromRemoteByName()

using (var repo = new Repository(repoPath))
{
Remote remote = repo.Network.Remotes.Add(remoteName, url);
repo.Network.Remotes.Add(remoteName, url);

// We will first fetch without specifying any Tag options.
// After we verify this fetch, we will perform a second fetch
Expand All @@ -208,13 +208,13 @@ public void CanFetchFromRemoteByName()
}

// Perform the actual fetch
repo.Fetch(remote.Name, new FetchOptions { OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler });
Commands.Fetch(repo, remoteName, new string[0], new FetchOptions { OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler }, null);

// Verify the expected state
expectedFetchState.CheckUpdatedReferences(repo);

// Now fetch the rest of the tags
repo.Fetch(remote.Name, new FetchOptions { TagFetchMode = TagFetchMode.All });
Commands.Fetch(repo, remoteName, new string[0], new FetchOptions { TagFetchMode = TagFetchMode.All }, null);

// Verify that the "nearly-dangling" tag is now in the repo.
Tag nearlyDanglingTag = repo.Tags["nearly-dangling"];
Expand Down
13 changes: 8 additions & 5 deletions LibGit2Sharp.Tests/SmartSubtransportFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void CustomSmartSubtransportTest(string scheme, string url)

using (var repo = new Repository(repoPath))
{
Remote remote = repo.Network.Remotes.Add(remoteName, url);
repo.Network.Remotes.Add(remoteName, url);

// Set up structures for the expected results
// and verifying the RemoteUpdateTips callback.
Expand All @@ -63,7 +63,9 @@ public void CustomSmartSubtransportTest(string scheme, string url)
}

// Perform the actual fetch
repo.Network.Fetch(remote, new FetchOptions { OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler, TagFetchMode = TagFetchMode.Auto });
Commands.Fetch(repo, remoteName, new string[0],
new FetchOptions { OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler, TagFetchMode = TagFetchMode.Auto },
null);

// Verify the expected
expectedFetchState.CheckUpdatedReferences(repo);
Expand Down Expand Up @@ -99,7 +101,7 @@ public void CanUseCredentials(string scheme, string url, string user, string pas

using (var repo = new Repository(scd.DirectoryPath))
{
Remote remote = repo.Network.Remotes.Add(remoteName, url);
repo.Network.Remotes.Add(remoteName, url);

// Set up structures for the expected results
// and verifying the RemoteUpdateTips callback.
Expand All @@ -113,9 +115,10 @@ public void CanUseCredentials(string scheme, string url, string user, string pas
}

// Perform the actual fetch
repo.Network.Fetch(remote, new FetchOptions { OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler, TagFetchMode = TagFetchMode.Auto,
Commands.Fetch(repo, remoteName, new string[0], new FetchOptions {
OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler, TagFetchMode = TagFetchMode.Auto,
CredentialsProvider = (_user, _valid, _hostname) => new UsernamePasswordCredentials() { Username = "libgit3", Password = "libgit3" },
});
}, null);

// Verify the expected
expectedFetchState.CheckUpdatedReferences(repo);
Expand Down
80 changes: 80 additions & 0 deletions LibGit2Sharp/Commands/Fetch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System.Collections.Generic;
using LibGit2Sharp;
using LibGit2Sharp.Core;
using LibGit2Sharp.Core.Handles;

namespace LibGit2Sharp
{
/// <summary>
/// Class to serve as namespacing for the command-emulating methods
/// </summary>
public static partial class Commands
{
private static RemoteHandle RemoteFromNameOrUrl(RepositoryHandle repoHandle, string remote)
{
RemoteHandle handle = null;
handle = Proxy.git_remote_lookup(repoHandle, remote, false);

// If that wasn't the name of a remote, let's use it as a url
if (handle == null)
{
handle = Proxy.git_remote_create_anonymous(repoHandle, remote);
}

return handle;
}

/// <summary>
/// Perform a fetch
/// </summary>
/// <param name="repository">The repository in which to fetch.</param>
/// <param name="remote">The remote to fetch from. Either as a remote name or a URL</param>
/// <param name="options">Fetch options.</param>
/// <param name="logMessage">Log message for any ref updates.</param>
/// <param name="refspecs">List of refspecs to apply as active.</param>
public static void Fetch(Repository repository, string remote, IEnumerable<string> refspecs, FetchOptions options, string logMessage)
{
Ensure.ArgumentNotNull(remote, "remote");

options = options ?? new FetchOptions();
using (var remoteHandle = RemoteFromNameOrUrl(repository.Handle, remote))
{

var callbacks = new RemoteCallbacks(options);
GitRemoteCallbacks gitCallbacks = callbacks.GenerateCallbacks();

// It is OK to pass the reference to the GitCallbacks directly here because libgit2 makes a copy of
// the data in the git_remote_callbacks structure. If, in the future, libgit2 changes its implementation
// to store a reference to the git_remote_callbacks structure this would introduce a subtle bug
// where the managed layer could move the git_remote_callbacks to a different location in memory,
// but libgit2 would still reference the old address.
//
// Also, if GitRemoteCallbacks were a class instead of a struct, we would need to guard against
// GC occuring in between setting the remote callbacks and actual usage in one of the functions afterwords.
var fetchOptions = new GitFetchOptions
{
RemoteCallbacks = gitCallbacks,
download_tags = Proxy.git_remote_autotag(remoteHandle),
};

if (options.TagFetchMode.HasValue)
{
fetchOptions.download_tags = options.TagFetchMode.Value;
}

if (options.Prune.HasValue)
{
fetchOptions.Prune = options.Prune.Value ? FetchPruneStrategy.Prune : FetchPruneStrategy.NoPrune;
}
else
{
fetchOptions.Prune = FetchPruneStrategy.FromConfigurationOrDefault;
}

Proxy.git_remote_fetch(remoteHandle, refspecs, fetchOptions, logMessage);
}

}
}
}

43 changes: 43 additions & 0 deletions LibGit2Sharp/Commands/Pull.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using LibGit2Sharp;
using LibGit2Sharp.Core;

namespace LibGit2Sharp
{
/// <summary>
/// Fetch changes from the configured upstream remote and branch into the branch pointed at by HEAD.
/// </summary>
public static partial class Commands
{
/// <summary>
/// Fetch changes from the configured upstream remote and branch into the branch pointed at by HEAD.
/// </summary>
/// <param name="repository">The repository.</param>
/// <param name="merger">The signature to use for the merge.</param>
/// <param name="options">The options for fetch and merging.</param>
public static MergeResult Pull(Repository repository, Signature merger, PullOptions options)
{
Ensure.ArgumentNotNull(repository, "repository");
Ensure.ArgumentNotNull(merger, "merger");
Ensure.ArgumentNotNull(options, "options");


options = options ?? new PullOptions();
Branch currentBranch = repository.Head;

if (!currentBranch.IsTracking)
{
throw new LibGit2SharpException("There is no tracking information for the current branch.");
}

if (currentBranch.RemoteName == null)
{
throw new LibGit2SharpException("No upstream remote for the current branch.");
}

Commands.Fetch(repository, currentBranch.RemoteName, new string[0], options.FetchOptions, null);
return repository.MergeFetchedRefs(merger, options.MergeOptions);
}
}
}

5 changes: 5 additions & 0 deletions LibGit2Sharp/LibGit2Sharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,8 @@
<Compile Include="Core\Handles\Libgit2Object.cs" />
<Compile Include="Core\GitCredential.cs" />
<Compile Include="Core\GitCredentialUserpass.cs" />
<Compile Include="Commands\Pull.cs" />
<Compile Include="Commands\Fetch.cs" />
</ItemGroup>
<ItemGroup>
<CodeAnalysisDictionary Include="CustomDictionary.xml" />
Expand Down Expand Up @@ -386,4 +388,7 @@
<Target Name="AfterBuild">
</Target>
-->
<ItemGroup>
<Folder Include="Commands\" />
</ItemGroup>
</Project>
Loading