Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Introduce RemoteCollection.Rename. #741

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 1 commit into from
Jun 6, 2014
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
90 changes: 90 additions & 0 deletions LibGit2Sharp.Tests/RemoteFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,5 +214,95 @@ public void CanDeleteNonExistingRemote()
repo.Network.Remotes.Remove("i_dont_exist");
}
}

[Fact]
public void CanRenameExistingRemote()
{
var path = CloneStandardTestRepo();
using (var repo = new Repository(path))
{
Assert.NotNull(repo.Network.Remotes["origin"]);
Assert.Null(repo.Network.Remotes["renamed"]);
Assert.NotEmpty(repo.Refs.FromGlob("refs/remotes/origin/*"));
Copy link
Member

Choose a reason for hiding this comment

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

Maybe add also

Assert.Empty(repo.Refs.FromGlob("refs/remotes/renamed/*"));

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure.

Assert.Empty(repo.Refs.FromGlob("refs/remotes/renamed/*"));

repo.Network.Remotes.Rename("origin", "renamed", problem => Assert.True(false));
Assert.Null(repo.Network.Remotes["origin"]);
Assert.Empty(repo.Refs.FromGlob("refs/remotes/origin/*"));

Assert.NotNull(repo.Network.Remotes["renamed"]);
Assert.NotEmpty(repo.Refs.FromGlob("refs/remotes/renamed/*"));
}
}

[Fact]
public void CanRenameNonExistingRemote()
{
using (var repo = new Repository(StandardTestRepoPath))
{
Assert.Null(repo.Network.Remotes["i_dont_exist"]);

repo.Network.Remotes.Rename("i_dont_exist", "i_dont_either", problem => Assert.True(false));
Assert.Null(repo.Network.Remotes["i_dont_either"]);
}
}

[Fact]
public void ReportsRemotesWithNonDefaultRefSpecs()
{
var path = CloneStandardTestRepo();
using (var repo = new Repository(path))
{
Assert.NotNull(repo.Network.Remotes["origin"]);

repo.Network.Remotes.Update(
repo.Network.Remotes["origin"],
r => r.FetchRefSpecs = new[] { "+refs/heads/*:refs/remotes/upstream/*" });

repo.Network.Remotes.Rename("origin", "nondefault", problem => Assert.Equal("+refs/heads/*:refs/remotes/upstream/*", problem));

Assert.NotEmpty(repo.Refs.FromGlob("refs/remotes/nondefault/*"));
Assert.Empty(repo.Refs.FromGlob("refs/remotes/upstream/*"));

Assert.Null(repo.Network.Remotes["origin"]);
Assert.NotNull(repo.Network.Remotes["nondefault"]);
}
}

[Fact]
public void DoesNotReportRemotesWithAlreadyExistingRefSpec()
{
var path = CloneStandardTestRepo();
using (var repo = new Repository(path))
{
Assert.NotNull(repo.Network.Remotes["origin"]);

repo.Refs.Add("refs/remotes/renamed/master", "32eab9cb1f450b5fe7ab663462b77d7f4b703344");

repo.Network.Remotes.Rename("origin", "renamed", problem => Assert.True(false));

Assert.NotEmpty(repo.Refs.FromGlob("refs/remotes/renamed/*"));
Assert.Empty(repo.Refs.FromGlob("refs/remotes/origin/*"));

Assert.Null(repo.Network.Remotes["origin"]);
Assert.NotNull(repo.Network.Remotes["renamed"]);
}
}

[Fact]
public void CanNotRenameWhenRemoteWithSameNameExists()
{
const string name = "upstream";
const string url = "https://github.com/libgit2/libgit2sharp.git";

var path = CloneStandardTestRepo();
using (var repo = new Repository(path))
{
Assert.NotNull(repo.Network.Remotes["origin"]);
repo.Network.Remotes.Add(name, url);

Assert.Throws<NameConflictException>(() => repo.Network.Remotes.Rename("origin", "upstream"));
}
}
}
}
11 changes: 11 additions & 0 deletions LibGit2Sharp/Core/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,17 @@ internal static extern int git_branch_remote_name(
RepositorySafeHandle repo,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string canonical_branch_name);

[DllImport(libgit2)]
internal static extern int git_remote_rename(
RemoteSafeHandle remote,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string new_name,
git_remote_rename_problem_cb callback,
IntPtr payload);

internal delegate int git_remote_rename_problem_cb(
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] string problematic_refspec,
IntPtr payload);

[DllImport(libgit2)]
internal static extern int git_branch_upstream_name(
GitBuf buf,
Expand Down
28 changes: 28 additions & 0 deletions LibGit2Sharp/Core/Proxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Runtime.InteropServices;
using System.Threading;
using LibGit2Sharp.Core.Handles;
using LibGit2Sharp.Handlers;

// ReSharper disable InconsistentNaming
namespace LibGit2Sharp.Core
Expand Down Expand Up @@ -1958,6 +1959,33 @@ public static string git_remote_name(RemoteSafeHandle remote)
return NativeMethods.git_remote_name(remote);
}

public static void git_remote_rename(RepositorySafeHandle repo, string name, string new_name, RemoteRenameFailureHandler callback)
{
using (ThreadAffinity())
{
using (RemoteSafeHandle remote = git_remote_load(repo, name, false))
{
if (remote == null)
{
return;
}

if (callback == null)
{
callback = (problem) => {};
}

int res = NativeMethods.git_remote_rename(
remote,
new_name,
(problem, payload) => { callback(problem); return 0; },
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 could send the callback as payload, but this works too and it's shorter and more concise.

Copy link
Member

Choose a reason for hiding this comment

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

Couldn't we rather pass IntPtr.Zero (rather than an empty callback) when the user doesn't want to be notified?

Grmpf... The callback doesn't seem to be as optional as it should be (cf https://github.com/libgit2/libgit2/blob/development/src/remote.c#L1450)

/cc @carlosmn

Copy link
Member Author

Choose a reason for hiding this comment

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

Yesh,

Copy link
Member

Choose a reason for hiding this comment

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

I think we should go with returning a list of strings instead of using a callback, but making it optional would be a good first step.

Copy link
Member Author

Choose a reason for hiding this comment

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

How would you handle normal rename issues then?

Copy link
Member

Choose a reason for hiding this comment

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

What would these "normal" issues be? AFAICT we only call this for non-default refspecs.

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, I see, you meant a ref param with a list of strings.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, what in C# would be an out param where we tell the caller what went wrong, since this has nothing to do with events and thus doesn't need a callback.

Copy link
Member

Choose a reason for hiding this comment

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

Actually, I'd rather avoid relying on ref/out params on public API as none of the user facing methods behave this way.

Copy link
Member

Choose a reason for hiding this comment

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

Right, I'm talking about the C interface. Hence the "would" :p You still get to keep your fancy return values.

IntPtr.Zero);

Ensure.ZeroResult(res);
}
}
}

public static void git_remote_save(RemoteSafeHandle remote)
{
using (ThreadAffinity())
Expand Down
10 changes: 10 additions & 0 deletions LibGit2Sharp/Handlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@
/// <param name="unmatchedPath">The unmatched path.</param>
public delegate void UnmatchedPathHandler(string unmatchedPath);

/// <summary>
/// Delegate definition for remote rename failure callback.
/// <para>
/// This callback will be called to notify the caller of fetch refspecs
/// that haven't been automatically updated and need potential manual tweaking.
/// </para>
/// </summary>
/// <param name="problematicRefspec">The refspec which didn't match the default.</param>
public delegate void RemoteRenameFailureHandler(string problematicRefspec);

/// <summary>
/// The stages of pack building.
/// </summary>
Expand Down
17 changes: 17 additions & 0 deletions LibGit2Sharp/RemoteCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Linq;
using LibGit2Sharp.Core;
using LibGit2Sharp.Core.Handles;
using LibGit2Sharp.Handlers;

namespace LibGit2Sharp
{
Expand Down Expand Up @@ -138,6 +139,22 @@ public virtual void Remove(string name)
Proxy.git_remote_delete(repository.Handle, name);
}

/// <summary>
/// Renames an existing <see cref="Remote"/>.
/// </summary>
/// <param name="name">The current remote name.</param>
/// <param name="newName">The new name the existing remote should bear.</param>
/// <param name"renameCallback">The callback to be used when problems with renaming occur. (e.g. non-default fetch refspecs)</para>
/// <returns>A new <see cref="Remote"/>.</returns>
public virtual Remote Rename(string name, string newName, RemoteRenameFailureHandler callback = null)
{
Ensure.ArgumentNotNull(name, "name");
Ensure.ArgumentNotNull(newName, "newName");

Proxy.git_remote_rename(repository.Handle, name, newName, callback);
return this[newName];
}

private string DebuggerDisplay
{
get
Expand Down