Skip to content

Commit 3b308f4

Browse files
committed
Take a name instead of an instance to update a remote
All the Remote instance does in RemoteUpdater is carry the name, as we need it in order to perform the update. Reflect this in the class fields as well as in the preferred way of calling it. While we're dealing with the updater, make it perform its actions inside a configuration transaction so the configuration does not change while we're processing updates.
1 parent 63771c5 commit 3b308f4

File tree

9 files changed

+178
-73
lines changed

9 files changed

+178
-73
lines changed

LibGit2Sharp.Tests/BranchFixture.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ public void QueryUnresolvableRemoteForRemoteBranch()
457457
Remote remote = repo.Network.Remotes["origin"];
458458
Assert.NotNull(remote);
459459

460-
repo.Network.Remotes.Update(remote, r => r.FetchRefSpecs = fetchRefSpecs);
460+
repo.Network.Remotes.Update("origin", r => r.FetchRefSpecs = fetchRefSpecs);
461461

462462
Branch branch = repo.Branches["refs/remotes/origin/master"];
463463

@@ -750,7 +750,7 @@ public void SetTrackedBranchForUnreasolvableRemoteThrows()
750750
// cannot be resolved.
751751
Remote remote = repo.Network.Remotes["origin"];
752752
Assert.NotNull(remote);
753-
repo.Network.Remotes.Update(remote, r => r.FetchRefSpecs = fetchRefSpecs);
753+
repo.Network.Remotes.Update("origin", r => r.FetchRefSpecs = fetchRefSpecs);
754754

755755
// Now attempt to update the tracked branch
756756
Branch branch = repo.CreateBranch(testBranchName);

LibGit2Sharp.Tests/FetchFixture.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ public void FetchRespectsConfiguredAutoTagSetting(TagFetchMode tagFetchMode, int
177177
Assert.NotNull(remote);
178178

179179
// Update the configured autotag setting.
180-
repo.Network.Remotes.Update(remote,
180+
repo.Network.Remotes.Update(remoteName,
181181
r => r.TagFetchMode = tagFetchMode);
182182

183183
// Perform the actual fetch.

LibGit2Sharp.Tests/RefSpecFixture.cs

Lines changed: 56 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Linq;
1+
using System.Collections.Generic;
2+
using System.Linq;
23
using LibGit2Sharp.Tests.TestHelpers;
34
using Xunit;
45
using Xunit.Extensions;
@@ -75,25 +76,30 @@ public void CanReplaceRefSpecs(string[] newFetchRefSpecs, string[] newPushRefSpe
7576
var path = SandboxStandardTestRepo();
7677
using (var repo = new Repository(path))
7778
{
78-
var remote = repo.Network.Remotes["origin"];
79-
var oldRefSpecs = remote.RefSpecs.ToList();
80-
81-
var newRemote = repo.Network.Remotes.Update(remote,
82-
r => r.FetchRefSpecs = newFetchRefSpecs, r => r.PushRefSpecs = newPushRefSpecs);
83-
84-
Assert.Equal(oldRefSpecs, remote.RefSpecs.ToList());
79+
List<RefSpec> oldRefSpecs;
80+
using (var remote = repo.Network.Remotes["origin"])
81+
{
82+
oldRefSpecs = remote.RefSpecs.ToList();
8583

86-
var actualNewFetchRefSpecs = newRemote.RefSpecs
87-
.Where(s => s.Direction == RefSpecDirection.Fetch)
88-
.Select(r => r.Specification)
89-
.ToArray();
90-
Assert.Equal(newFetchRefSpecs, actualNewFetchRefSpecs);
84+
repo.Network.Remotes.Update("origin",
85+
r => r.FetchRefSpecs = newFetchRefSpecs, r => r.PushRefSpecs = newPushRefSpecs);
86+
Assert.Equal(oldRefSpecs, remote.RefSpecs.ToList());
87+
}
9188

92-
var actualNewPushRefSpecs = newRemote.RefSpecs
93-
.Where(s => s.Direction == RefSpecDirection.Push)
94-
.Select(r => r.Specification)
95-
.ToArray();
96-
Assert.Equal(newPushRefSpecs, actualNewPushRefSpecs);
89+
using (var newRemote = repo.Network.Remotes["origin"])
90+
{
91+
var actualNewFetchRefSpecs = newRemote.RefSpecs
92+
.Where(s => s.Direction == RefSpecDirection.Fetch)
93+
.Select(r => r.Specification)
94+
.ToArray();
95+
Assert.Equal(newFetchRefSpecs, actualNewFetchRefSpecs);
96+
97+
var actualNewPushRefSpecs = newRemote.RefSpecs
98+
.Where(s => s.Direction == RefSpecDirection.Push)
99+
.Select(r => r.Specification)
100+
.ToArray();
101+
Assert.Equal(newPushRefSpecs, actualNewPushRefSpecs);
102+
}
97103
}
98104
}
99105

@@ -105,15 +111,12 @@ public void RemoteUpdaterSavesRefSpecsPermanently()
105111

106112
using (var repo = new Repository(path))
107113
{
108-
using (var remote = repo.Network.Remotes["origin"])
109-
{
110-
repo.Network.Remotes.Update(remote, r => r.FetchRefSpecs = fetchRefSpecs);
111-
}
114+
repo.Network.Remotes.Update("origin", r => r.FetchRefSpecs = fetchRefSpecs);
112115
}
113116

114117
using (var repo = new Repository(path))
118+
using (var remote = repo.Network.Remotes["origin"])
115119
{
116-
var remote = repo.Network.Remotes["origin"];
117120
var actualRefSpecs = remote.RefSpecs
118121
.Where(r => r.Direction == RefSpecDirection.Fetch)
119122
.Select(r => r.Specification)
@@ -129,22 +132,25 @@ public void CanAddAndRemoveRefSpecs()
129132
var path = SandboxStandardTestRepo();
130133

131134
using (var repo = new Repository(path))
132-
using (var remote = repo.Network.Remotes["origin"])
133135
{
134-
using (var updatedRemote = repo.Network.Remotes.Update(remote,
136+
repo.Network.Remotes.Update("origin",
135137
r => r.FetchRefSpecs.Add(newRefSpec),
136-
r => r.PushRefSpecs.Add(newRefSpec)))
138+
r => r.PushRefSpecs.Add(newRefSpec));
139+
140+
using (var remote = repo.Network.Remotes["origin"])
141+
{
142+
Assert.Contains(newRefSpec, remote.FetchRefSpecs.Select(r => r.Specification));
143+
Assert.Contains(newRefSpec, remote.PushRefSpecs.Select(r => r.Specification));
144+
}
145+
146+
repo.Network.Remotes.Update("origin",
147+
r => r.FetchRefSpecs.Remove(newRefSpec),
148+
r => r.PushRefSpecs.Remove(newRefSpec));
149+
150+
using (var remote = repo.Network.Remotes["origin"])
137151
{
138-
Assert.Contains(newRefSpec, updatedRemote.FetchRefSpecs.Select(r => r.Specification));
139-
Assert.Contains(newRefSpec, updatedRemote.PushRefSpecs.Select(r => r.Specification));
140-
141-
using (var updatedRemote2 = repo.Network.Remotes.Update(updatedRemote,
142-
r => r.FetchRefSpecs.Remove(newRefSpec),
143-
r => r.PushRefSpecs.Remove(newRefSpec)))
144-
{
145-
Assert.DoesNotContain(newRefSpec, updatedRemote2.FetchRefSpecs.Select(r => r.Specification));
146-
Assert.DoesNotContain(newRefSpec, updatedRemote2.PushRefSpecs.Select(r => r.Specification));
147-
}
152+
Assert.DoesNotContain(newRefSpec, remote.FetchRefSpecs.Select(r => r.Specification));
153+
Assert.DoesNotContain(newRefSpec, remote.PushRefSpecs.Select(r => r.Specification));
148154
}
149155
}
150156
}
@@ -155,18 +161,20 @@ public void CanClearRefSpecs()
155161
var path = SandboxStandardTestRepo();
156162
using (var repo = new Repository(path))
157163
{
158-
var remote = repo.Network.Remotes["origin"];
159164

160165
// Push refspec does not exist in cloned repository
161-
remote = repo.Network.Remotes.Update(remote, r => r.PushRefSpecs.Add("+refs/test:refs/test"));
166+
repo.Network.Remotes.Update("origin", r => r.PushRefSpecs.Add("+refs/test:refs/test"));
162167

163-
remote = repo.Network.Remotes.Update(remote,
168+
repo.Network.Remotes.Update("origin",
164169
r => r.FetchRefSpecs.Clear(),
165170
r => r.PushRefSpecs.Clear());
166171

167-
Assert.Empty(remote.FetchRefSpecs);
168-
Assert.Empty(remote.PushRefSpecs);
169-
Assert.Empty(remote.RefSpecs);
172+
using (var remote = repo.Network.Remotes["origin"])
173+
{
174+
Assert.Empty(remote.FetchRefSpecs);
175+
Assert.Empty(remote.PushRefSpecs);
176+
Assert.Empty(remote.RefSpecs);
177+
}
170178
}
171179
}
172180

@@ -183,11 +191,14 @@ public void SettingInvalidRefSpecsThrows(string refSpec)
183191
var path = SandboxStandardTestRepo();
184192
using (var repo = new Repository(path))
185193
{
186-
var remote = repo.Network.Remotes["origin"];
187-
var oldRefSpecs = remote.RefSpecs.Select(r => r.Specification).ToList();
194+
IEnumerable<string> oldRefSpecs;
195+
using (var remote = repo.Network.Remotes["origin"])
196+
{
197+
oldRefSpecs = remote.RefSpecs.Select(r => r.Specification).ToList();
198+
}
188199

189200
Assert.Throws<InvalidSpecificationException>(() =>
190-
repo.Network.Remotes.Update(remote, r => r.FetchRefSpecs.Add(refSpec)));
201+
repo.Network.Remotes.Update("origin", r => r.FetchRefSpecs.Add(refSpec)));
191202

192203
var newRemote = repo.Network.Remotes["origin"];
193204
Assert.Equal(oldRefSpecs, newRemote.RefSpecs.Select(r => r.Specification).ToList());

LibGit2Sharp.Tests/RemoteFixture.cs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,13 @@ public void CanSetTagFetchMode(TagFetchMode tagFetchMode)
6666
Remote remote = repo.Network.Remotes[name];
6767
Assert.NotNull(remote);
6868

69-
Remote updatedremote = repo.Network.Remotes.Update(remote,
69+
repo.Network.Remotes.Update(name,
7070
r => r.TagFetchMode = tagFetchMode);
7171

72-
Assert.Equal(tagFetchMode, updatedremote.TagFetchMode);
72+
using (var updatedremote = repo.Network.Remotes[name])
73+
{
74+
Assert.Equal(tagFetchMode, updatedremote.TagFetchMode);
75+
}
7376
}
7477
}
7578

@@ -87,12 +90,14 @@ public void CanSetRemoteUrl()
8790
Remote remote = repo.Network.Remotes[name];
8891
Assert.NotNull(remote);
8992

90-
Remote updatedremote = repo.Network.Remotes.Update(remote,
91-
r => r.Url = newUrl);
93+
repo.Network.Remotes.Update(name, r => r.Url = newUrl);
9294

93-
Assert.Equal(newUrl, updatedremote.Url);
94-
// with no push url set, PushUrl defaults to the fetch url
95-
Assert.Equal(newUrl, updatedremote.PushUrl);
95+
using (var updatedremote = repo.Network.Remotes[name])
96+
{
97+
Assert.Equal(newUrl, updatedremote.Url);
98+
// with no push url set, PushUrl defaults to the fetch url
99+
Assert.Equal(newUrl, updatedremote.PushUrl);
100+
}
96101
}
97102
}
98103

@@ -114,12 +119,14 @@ public void CanSetRemotePushUrl()
114119
Assert.Equal(url, remote.Url);
115120
Assert.Equal(url, remote.PushUrl);
116121

117-
Remote updatedremote = repo.Network.Remotes.Update(remote,
118-
r => r.PushUrl = pushurl);
122+
repo.Network.Remotes.Update(name, r => r.PushUrl = pushurl);
119123

120-
// url should not change, push url should be set to new value
121-
Assert.Equal(url, updatedremote.Url);
122-
Assert.Equal(pushurl, updatedremote.PushUrl);
124+
using (var updatedremote = repo.Network.Remotes[name])
125+
{
126+
// url should not change, push url should be set to new value
127+
Assert.Equal(url, updatedremote.Url);
128+
Assert.Equal(pushurl, updatedremote.PushUrl);
129+
}
123130
}
124131
}
125132

@@ -292,8 +299,7 @@ public void ReportsRemotesWithNonDefaultRefSpecs()
292299
{
293300
Assert.NotNull(repo.Network.Remotes["origin"]);
294301

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

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

LibGit2Sharp/Configuration.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,5 +759,29 @@ private ConfigurationHandle Snapshot()
759759
{
760760
return Proxy.git_config_snapshot(configHandle);
761761
}
762+
763+
/// <summary>
764+
/// Perform a series of actions within a transaction.
765+
///
766+
/// The configuration will be locked during this function and the changes will be committed at the end. These
767+
/// changes will not be visible in the configuration until the end of this method.
768+
///
769+
/// If the action throws an exception, the changes will be rolled back.
770+
/// </summary>
771+
/// <param name="action">The code to run under the transaction</param>
772+
public virtual unsafe void WithinTransaction(Action action)
773+
{
774+
IntPtr txn = IntPtr.Zero;
775+
try
776+
{
777+
txn = Proxy.git_config_lock(configHandle);
778+
action();
779+
Proxy.git_transaction_commit(txn);
780+
}
781+
finally
782+
{
783+
Proxy.git_transaction_free(txn);
784+
}
785+
}
762786
}
763787
}

LibGit2Sharp/Core/NativeMethods.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,9 @@ internal static extern unsafe int git_config_delete_entry(
342342
git_config* cfg,
343343
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name);
344344

345+
[DllImport(libgit2)]
346+
internal static extern unsafe int git_config_lock(out IntPtr txn, git_config* config);
347+
345348
[DllImport(libgit2)]
346349
internal static extern unsafe int git_config_delete_multivar(
347350
git_config* cfg,
@@ -1825,6 +1828,12 @@ internal static extern unsafe int git_treebuilder_insert(
18251828

18261829
[DllImport(libgit2)]
18271830
internal static extern unsafe int git_cherrypick(git_repository* repo, git_object* commit, GitCherryPickOptions options);
1831+
1832+
[DllImport(libgit2)]
1833+
internal static extern int git_transaction_commit(IntPtr txn);
1834+
1835+
[DllImport(libgit2)]
1836+
internal static extern void git_transaction_free(IntPtr txn);
18281837
}
18291838
}
18301839
// ReSharper restore InconsistentNaming

LibGit2Sharp/Core/Proxy.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,15 @@ public static unsafe ConfigurationHandle git_config_snapshot(ConfigurationHandle
635635
return new ConfigurationHandle(handle, true);
636636
}
637637

638+
public static unsafe IntPtr git_config_lock(git_config* config)
639+
{
640+
IntPtr txn;
641+
int res = NativeMethods.git_config_lock(out txn, config);
642+
Ensure.ZeroResult(res);
643+
644+
return txn;
645+
}
646+
638647
#endregion
639648

640649
#region git_cred_
@@ -3197,6 +3206,20 @@ public static unsafe ObjectId git_treebuilder_write(TreeBuilderHandle bld)
31973206

31983207
#endregion
31993208

3209+
#region git_transaction_
3210+
3211+
public static void git_transaction_commit(IntPtr txn)
3212+
{
3213+
NativeMethods.git_transaction_commit(txn);
3214+
}
3215+
3216+
public static void git_transaction_free(IntPtr txn)
3217+
{
3218+
NativeMethods.git_transaction_free(txn);
3219+
}
3220+
3221+
#endregion
3222+
32003223
#region git_libgit2_
32013224

32023225
/// <summary>

LibGit2Sharp/RemoteCollection.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ internal Remote RemoteForName(string name, bool shouldThrowIfNotFound = true)
5353
/// <param name="remote">The remote to update.</param>
5454
/// <param name="actions">Delegate to perform updates on the remote.</param>
5555
/// <returns>The updated remote.</returns>
56+
[Obsolete("This method is deprecated. Use the overload with a remote name")]
5657
public virtual Remote Update(Remote remote, params Action<RemoteUpdater>[] actions)
5758
{
5859
var updater = new RemoteUpdater(repository, remote);
@@ -65,6 +66,25 @@ public virtual Remote Update(Remote remote, params Action<RemoteUpdater>[] actio
6566
return this[remote.Name];
6667
}
6768

69+
/// <summary>
70+
/// Update properties of a remote.
71+
///
72+
/// These updates will be performed as a bulk update at the end of the method.
73+
/// </summary>
74+
/// <param name="remote">The name of the remote to update.</param>
75+
/// <param name="actions">Delegate to perform updates on the remote.</param>
76+
public virtual void Update(string remote, params Action<RemoteUpdater>[] actions)
77+
{
78+
var updater = new RemoteUpdater(repository, remote);
79+
80+
repository.Config.WithinTransaction(() => {
81+
foreach (Action<RemoteUpdater> action in actions)
82+
{
83+
action(updater);
84+
}
85+
});
86+
}
87+
6888
/// <summary>
6989
/// Returns an enumerator that iterates through the collection.
7090
/// </summary>

0 commit comments

Comments
 (0)