Skip to content

Win http fetch #213

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

Closed
wants to merge 2 commits into from
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
13 changes: 13 additions & 0 deletions LibGit2Sharp.Tests/BranchFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,19 @@ public void CanListAllBranchesIncludingRemoteRefs()
}
}

[Fact]
public void CanResolveTrackedRemote()
{
using (var repo = new Repository(StandardTestRepoPath))
{
Branch master = repo.Branches["master"];
Assert.Equal(repo.Remotes["origin"], master.ResolveTrackedRemote());

Branch test = repo.Branches["test"];
Assert.Null(test);
Copy link
Member

Choose a reason for hiding this comment

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

What's the use of this assert?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is not correct - I will update this test to cover what I meant for it to test.

}
}

[Fact]
public void CanLookupABranchByItsCanonicalName()
{
Expand Down
2 changes: 2 additions & 0 deletions LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@
<Compile Include="RepositoryFixture.cs" />
<Compile Include="TagFixture.cs" />
<Compile Include="TestHelpers\DirectoryHelper.cs" />
<Compile Include="TestHelpers\TestRemoteExpectedInfo.cs" />
<Compile Include="TestHelpers\IPostTestDirectoryRemover.cs" />
<Compile Include="TestHelpers\RemoteUpdateTipsCallbackHelper.cs" />
<Compile Include="TestHelpers\SelfCleaningDirectory.cs" />
<Compile Include="TestHelpers\SignatureExtensions.cs" />
<Compile Include="TestHelpers\SkipException.cs" />
Expand Down
34 changes: 34 additions & 0 deletions LibGit2Sharp.Tests/RepositoryFixture.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using LibGit2Sharp.Tests.TestHelpers;
Expand Down Expand Up @@ -120,6 +121,39 @@ private static void AssertIsHidden(string repoPath)
Assert.Equal(FileAttributes.Hidden, (attribs & FileAttributes.Hidden));
}


[Theory]
[InlineData("http://github.com/nulltoken/TestGitRepository")]
[InlineData("https://github.com/nulltoken/TestGitRepository")]
//[InlineData("[email protected]:nulltoken/TestGitRepository")]
public void CanFetchFromEmptyRepository(string url)
{
var scd = BuildSelfCleaningDirectory();
using (var repo = Repository.Init(scd.RootedDirectoryPath))
{
string remoteName = "testRepository";

TestRemoteExpectedInfo expectedResults = new TestRemoteExpectedInfo(remoteName);
RemoteUpdateTipsCallbackHelper helper = new RemoteUpdateTipsCallbackHelper(expectedResults.ExpectedReferenceCallbacks);

Remote remote = repo.Remotes.Add(remoteName, url);

FetchProgress progress = new FetchProgress();
progress.RemoteCallbacks.UpdateTipsChanged += new System.EventHandler<UpdateTipsChangedEventArgs>(helper.RemoteUpdateTipsHandler);
repo.Fetch(remote, progress);

Assert.Equal(expectedResults.ExpectedBranchTips.Count, repo.Branches.Count());
foreach (KeyValuePair<string, string> kvp in expectedResults.ExpectedBranchTips)
{
Branch branch = repo.Branches[kvp.Key];
Assert.NotNull(branch);
Assert.Equal(kvp.Value, branch.Tip.Sha);
}

helper.CheckUpdatedReferences();
}
}

[Fact(Skip = "This is fixed on libgit2's development tip")]
public void CanReinitARepository()
{
Expand Down
5 changes: 4 additions & 1 deletion LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ public void Dispose()
{
foreach (string directory in directories)
{
DirectoryHelper.DeleteDirectory(directory);
if (Directory.Exists(directory))
Copy link
Member

Choose a reason for hiding this comment

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

Could you please give me some more context about this?
When would this check be required?

Copy link
Member Author

Choose a reason for hiding this comment

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

I had hit an issue running the CanCloneARepository(...) tests where the test would create the SelfCleaningDirectory object and register the temporary directory path for deletion, but the test would fail in Repository.Clone(...) before the temporary directory actually got created. On cleanup, the test would throw an exception indicating that it could not find the path of the temporary directory (as it does not exist). The end result is that the test reports that it failed to find the path of the temporary directory (which hides the original exception).

Copy link
Member

Choose a reason for hiding this comment

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

Thanks a lot for your answer. Indeed, that makes sense.

However, I'm afraid this check could hide that something may have went wrong earlier (a wrongly written test for instance). Could you think of a way to let the user know about this not expected state?

Copy link
Member Author

Choose a reason for hiding this comment

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

For now (especially as the clone is no longer part of the test suite) I will remove this change so we can concentrate on the core Fetch changes.

{
DirectoryHelper.DeleteDirectory(directory);
}
}
}

Expand Down
60 changes: 60 additions & 0 deletions LibGit2Sharp.Tests/TestHelpers/RemoteUpdateTipsCallbackHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System.Collections.Generic;
using LibGit2Sharp.Core.Compat;
using Xunit;

namespace LibGit2Sharp.Tests.TestHelpers
{
/// <summary>
/// Class to verify the update_tips callback of the git_remote_callbacks structure.
/// </summary>
public class RemoteUpdateTipsCallbackHelper
{
private Dictionary<string, Tuple<ObjectId, ObjectId>> ExpectedReferenceUpdates;
private Dictionary<string, Tuple<ObjectId, ObjectId>> ObservedReferenceUpdates;

/// <summary>
/// Constructor.
/// </summary>
/// <param name="expectedCallbacks">Dictionary of expected reference name => tuple of (old ObjectId, new ObjectID) that should be updated.</param>
public RemoteUpdateTipsCallbackHelper(Dictionary<string, Tuple<ObjectId, ObjectId>> expectedCallbacks)
{
ExpectedReferenceUpdates = new Dictionary<string, Tuple<ObjectId, ObjectId>>(expectedCallbacks);
ObservedReferenceUpdates = new Dictionary<string, Tuple<ObjectId, ObjectId>>();
}

/// <summary>
/// Handler to hook up to UpdateTips callback.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void RemoteUpdateTipsHandler(object sender, UpdateTipsChangedEventArgs e)
{
// assert that we have not seen this reference before
Assert.DoesNotContain(e.ReferenceName, ObservedReferenceUpdates.Keys);
ObservedReferenceUpdates.Add(e.ReferenceName, new Tuple<ObjectId,ObjectId>(e.OldId, e.NewId));


// verify that this reference is in the list of expected references
Tuple<ObjectId, ObjectId> reference;
bool referenceFound = ExpectedReferenceUpdates.TryGetValue(e.ReferenceName, out reference);
Assert.True(referenceFound, string.Format("Could not find reference {0} in list of expected reference updates.", e.ReferenceName));

// verify that the old / new Object IDs
if(referenceFound)
{
Assert.Equal(reference.Item1, e.OldId);
Assert.Equal(reference.Item2, e.NewId);
}
}

/// <summary>
/// Check that all expected references have been updated.
/// </summary>
public void CheckUpdatedReferences()
{
// we have already verified that all observed reference updates are expected,
// verify that we have seen all expected reference updates
Assert.Equal(ExpectedReferenceUpdates.Count, ObservedReferenceUpdates.Count);
}
}
}
38 changes: 38 additions & 0 deletions LibGit2Sharp.Tests/TestHelpers/TestRemoteExpectedInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LibGit2Sharp.Core.Compat;

namespace LibGit2Sharp.Tests.TestHelpers
{
/// <summary>
/// This is the expected information based on the test repository at:
/// github.com/nulltoken/TestGitRepository
/// </summary>
public class TestRemoteExpectedInfo
{
public Dictionary<string, string> ExpectedBranchTips = new Dictionary<string, string>();

public Dictionary<string, string> ExpectedTags = new Dictionary<string, string>();

public Dictionary<string, Tuple<ObjectId, ObjectId>> ExpectedReferenceCallbacks = new Dictionary<string, Tuple<ObjectId, ObjectId>>();

public TestRemoteExpectedInfo(string remoteName)
{
ExpectedBranchTips.Add(remoteName + "/" + "master", "49322bb17d3acc9146f98c97d078513228bbf3c0");
ExpectedBranchTips.Add(remoteName + "/" + "first-merge", "0966a434eb1a025db6b71485ab63a3bfbea520b6");
ExpectedBranchTips.Add(remoteName + "/" + "no-parent", "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1");

ExpectedTags.Add("annotated_tag", "c070ad8c08840c8116da865b2d65593a6bb9cd2a");
ExpectedTags.Add("blob", "55a1a760df4b86a02094a904dfa511deb5655905");
ExpectedTags.Add("commit_tree", "8f50ba15d49353813cc6e20298002c0d17b0a9ee");
ExpectedTags.Add("nearly-dangling", "6e0c7bdb9b4ed93212491ee778ca1c65047cab4e");

string referenceUpdateBase = "refs/remotes/" + remoteName + "/";
ExpectedReferenceCallbacks.Add(referenceUpdateBase + "master", new Tuple<ObjectId, ObjectId>(new ObjectId("0000000000000000000000000000000000000000"), new ObjectId("49322bb17d3acc9146f98c97d078513228bbf3c0")));
ExpectedReferenceCallbacks.Add(referenceUpdateBase + "first-merge", new Tuple<ObjectId, ObjectId>(new ObjectId("0000000000000000000000000000000000000000"), new ObjectId("0966a434eb1a025db6b71485ab63a3bfbea520b6")));
ExpectedReferenceCallbacks.Add(referenceUpdateBase + "no-parent", new Tuple<ObjectId, ObjectId>(new ObjectId("0000000000000000000000000000000000000000"), new ObjectId("42e4e7c5e507e113ebbb7801b16b52cf867b7ce1")));
}
}
}
29 changes: 28 additions & 1 deletion LibGit2Sharp/Branch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,31 @@ public virtual ICommitLog Commits
get { return repo.Commits.QueryBy(new Filter { Since = this }); }
}

/// <summary>
/// Gets the <see cref="Remote"/> tracking this branch
/// </summary>
/// <returns>The <see cref="Remote"/> tracking this branch if one exists, otherwise returns null.</returns>
public virtual Remote ResolveTrackedRemote()
{
Copy link
Member

Choose a reason for hiding this comment

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

I'd rather be in favor of exposing a property Remote.

Could you please cover with test how this would behave when dealing with a local tracking branch (there's one in the StandardTestRepository named "track-local" IIRC)?

Copy link
Member Author

Choose a reason for hiding this comment

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

I will expose this as a new property.

I was not aware of local tracking tracking branches ... I covered this behavior in a test, In this case, there is no "Remote" for the local machine that I could see, so this would return "null"... Maybe we should add a property indicating that this is a "local tracking" branch?

if (IsTracking)
{
string trackedRemoteName = ResolveTrackedRemoteName();
if (string.IsNullOrEmpty(trackedRemoteName))
{
return null;
}

return repo.Remotes[trackedRemoteName];
}
else
{
return null;
}
}

private Branch ResolveTrackedBranch()
{
var trackedRemote = repo.Config.Get<string>("branch", Name, "remote", null);
string trackedRemote = ResolveTrackedRemoteName();
if (trackedRemote == null)
{
return null;
Expand All @@ -154,6 +176,11 @@ private Branch ResolveTrackedBranch()
return repo.Branches[remoteRefName];
}

private string ResolveTrackedRemoteName()
{
return repo.Config.Get<string>("branch", Name, "remote", null) as string;
}

private static string ResolveTrackedReference(string trackedRemote, string trackedRefName)
{
if (trackedRemote == ".")
Expand Down
10 changes: 10 additions & 0 deletions LibGit2Sharp/Core/GitDirection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;

namespace LibGit2Sharp.Core
{
internal enum GitDirection
{
Fetch = 0,
Push = 1
}
}
10 changes: 5 additions & 5 deletions LibGit2Sharp/Core/GitIndexerStats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
namespace LibGit2Sharp.Core
{
[StructLayout(LayoutKind.Sequential)]
public class GitIndexerStats
internal class GitIndexerStats
Copy link
Member

Choose a reason for hiding this comment

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

❤️ !

{
public int Total;
public int Processed;
public int Received;
public uint Total;
public uint Processed;
public uint Received;
}
}
}
23 changes: 23 additions & 0 deletions LibGit2Sharp/Core/GitRemoteCallbacks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace LibGit2Sharp.Core
{
/// <summary>
/// Structure for git_remote_callbacks
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct GitRemoteCallbacks
{
internal NativeMethods.remote_progress_callback progress;

internal NativeMethods.remote_completion_callback completion;

internal NativeMethods.remote_update_tips_callback update_tips;

internal IntPtr data;
}
}
25 changes: 25 additions & 0 deletions LibGit2Sharp/Core/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,16 @@ public static extern int git_reference_set_target(
[DllImport(libgit2)]
public static extern GitReferenceType git_reference_type(ReferenceSafeHandle reference);

internal delegate void remote_progress_callback(IntPtr str, int len, IntPtr data);

internal delegate int remote_completion_callback(int type, IntPtr data);

internal delegate int remote_update_tips_callback(
IntPtr refName,
ref GitOid oldId,
ref GitOid newId,
IntPtr data);

[DllImport(libgit2)]
public static extern void git_remote_free(IntPtr remote);

Expand Down Expand Up @@ -582,9 +592,24 @@ public static extern int git_remote_new(
[return : MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]
public static extern string git_remote_url(RemoteSafeHandle remote);

[DllImport(libgit2)]
public static extern int git_remote_connect(RemoteSafeHandle remote, GitDirection direction);

[DllImport(libgit2)]
public static extern void git_remote_disconnect(RemoteSafeHandle remote);

[DllImport(libgit2)]
public static extern int git_remote_download(RemoteSafeHandle remote, ref long bytes, GitIndexerStats stats);

[DllImport(libgit2)]
public static extern int git_remote_save(RemoteSafeHandle remote);

[DllImport(libgit2)]
public static extern void git_remote_set_callbacks(RemoteSafeHandle remote, ref GitRemoteCallbacks callbacks);

[DllImport(libgit2)]
public static extern int git_remote_update_tips(RemoteSafeHandle remote);

[DllImport(libgit2)]
public static extern int git_repository_config(
out ConfigurationSafeHandle cfg,
Expand Down
50 changes: 50 additions & 0 deletions LibGit2Sharp/FetchProgress.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using LibGit2Sharp.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LibGit2Sharp
{
/// <summary>
/// Contains data regarding fetch progress.
/// </summary>
public class FetchProgress : IndexerStats
Copy link
Member

Choose a reason for hiding this comment

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

Nitpick... From a semantic standpoint, I don't think FetchProgress should derive from IndexerStats.
To my knowledge, indexing is only the last step of the fetching process.

Copy link
Member Author

Choose a reason for hiding this comment

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

I will split this out so FetchProgress no longer derives from IndexerStats.

{
/// <summary>
/// Fetch progress constructor.
/// </summary>
public FetchProgress()
{
RemoteCallbacks = new RemoteCallbacks();
}

/// <summary>
/// Bytes received.
/// </summary>
public long Bytes
{
get
{
return bytes;
}
}

/// <summary>
/// The events fired in response to callbacks from libgit2.
/// </summary>
public RemoteCallbacks RemoteCallbacks { get; set; }

internal override void Reset()
{
base.Reset();
bytes = 0;
}

#region Fields

internal long bytes;

#endregion
}
}
Loading