Skip to content

Commit 5295c7e

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 5e683b9 commit 5295c7e

File tree

9 files changed

+180
-71
lines changed

9 files changed

+180
-71
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: 58 additions & 43 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,34 @@ 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();
79+
List<RefSpec> oldRefSpecs;
80+
using (var remote = repo.Network.Remotes["origin"])
81+
{
82+
oldRefSpecs = remote.RefSpecs.ToList();
83+
}
8084

81-
var newRemote = repo.Network.Remotes.Update(remote,
85+
repo.Network.Remotes.Update("origin",
8286
r => r.FetchRefSpecs = newFetchRefSpecs, r => r.PushRefSpecs = newPushRefSpecs);
8387

84-
Assert.Equal(oldRefSpecs, remote.RefSpecs.ToList());
85-
86-
var actualNewFetchRefSpecs = newRemote.RefSpecs
87-
.Where(s => s.Direction == RefSpecDirection.Fetch)
88-
.Select(r => r.Specification)
89-
.ToArray();
90-
Assert.Equal(newFetchRefSpecs, actualNewFetchRefSpecs);
88+
using (var remote = repo.Network.Remotes["origin"])
89+
{
90+
Assert.Equal(oldRefSpecs, remote.RefSpecs.ToList());
91+
}
9192

92-
var actualNewPushRefSpecs = newRemote.RefSpecs
93-
.Where(s => s.Direction == RefSpecDirection.Push)
94-
.Select(r => r.Specification)
95-
.ToArray();
96-
Assert.Equal(newPushRefSpecs, actualNewPushRefSpecs);
93+
using (var newRemote = repo.Network.Remotes["origin"])
94+
{
95+
var actualNewFetchRefSpecs = newRemote.RefSpecs
96+
.Where(s => s.Direction == RefSpecDirection.Fetch)
97+
.Select(r => r.Specification)
98+
.ToArray();
99+
Assert.Equal(newFetchRefSpecs, actualNewFetchRefSpecs);
100+
101+
var actualNewPushRefSpecs = newRemote.RefSpecs
102+
.Where(s => s.Direction == RefSpecDirection.Push)
103+
.Select(r => r.Specification)
104+
.ToArray();
105+
Assert.Equal(newPushRefSpecs, actualNewPushRefSpecs);
106+
}
97107
}
98108
}
99109

@@ -105,15 +115,12 @@ public void RemoteUpdaterSavesRefSpecsPermanently()
105115

106116
using (var repo = new Repository(path))
107117
{
108-
using (var remote = repo.Network.Remotes["origin"])
109-
{
110-
repo.Network.Remotes.Update(remote, r => r.FetchRefSpecs = fetchRefSpecs);
111-
}
118+
repo.Network.Remotes.Update("origin", r => r.FetchRefSpecs = fetchRefSpecs);
112119
}
113120

114121
using (var repo = new Repository(path))
122+
using (var remote = repo.Network.Remotes["origin"])
115123
{
116-
var remote = repo.Network.Remotes["origin"];
117124
var actualRefSpecs = remote.RefSpecs
118125
.Where(r => r.Direction == RefSpecDirection.Fetch)
119126
.Select(r => r.Specification)
@@ -129,22 +136,25 @@ public void CanAddAndRemoveRefSpecs()
129136
var path = SandboxStandardTestRepo();
130137

131138
using (var repo = new Repository(path))
132-
using (var remote = repo.Network.Remotes["origin"])
133139
{
134-
using (var updatedRemote = repo.Network.Remotes.Update(remote,
140+
repo.Network.Remotes.Update("origin",
135141
r => r.FetchRefSpecs.Add(newRefSpec),
136-
r => r.PushRefSpecs.Add(newRefSpec)))
142+
r => r.PushRefSpecs.Add(newRefSpec));
143+
144+
using (var remote = repo.Network.Remotes["origin"])
145+
{
146+
Assert.Contains(newRefSpec, remote.FetchRefSpecs.Select(r => r.Specification));
147+
Assert.Contains(newRefSpec, remote.PushRefSpecs.Select(r => r.Specification));
148+
}
149+
150+
repo.Network.Remotes.Update("origin",
151+
r => r.FetchRefSpecs.Remove(newRefSpec),
152+
r => r.PushRefSpecs.Remove(newRefSpec));
153+
154+
using (var remote = repo.Network.Remotes["origin"])
137155
{
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-
}
156+
Assert.DoesNotContain(newRefSpec, remote.FetchRefSpecs.Select(r => r.Specification));
157+
Assert.DoesNotContain(newRefSpec, remote.PushRefSpecs.Select(r => r.Specification));
148158
}
149159
}
150160
}
@@ -155,18 +165,20 @@ public void CanClearRefSpecs()
155165
var path = SandboxStandardTestRepo();
156166
using (var repo = new Repository(path))
157167
{
158-
var remote = repo.Network.Remotes["origin"];
159168

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

163-
remote = repo.Network.Remotes.Update(remote,
172+
repo.Network.Remotes.Update("remote",
164173
r => r.FetchRefSpecs.Clear(),
165174
r => r.PushRefSpecs.Clear());
166175

167-
Assert.Empty(remote.FetchRefSpecs);
168-
Assert.Empty(remote.PushRefSpecs);
169-
Assert.Empty(remote.RefSpecs);
176+
using (var remote = repo.Network.Remotes["origin"])
177+
{
178+
Assert.Empty(remote.FetchRefSpecs);
179+
Assert.Empty(remote.PushRefSpecs);
180+
Assert.Empty(remote.RefSpecs);
181+
}
170182
}
171183
}
172184

@@ -183,11 +195,14 @@ public void SettingInvalidRefSpecsThrows(string refSpec)
183195
var path = SandboxStandardTestRepo();
184196
using (var repo = new Repository(path))
185197
{
186-
var remote = repo.Network.Remotes["origin"];
187-
var oldRefSpecs = remote.RefSpecs.Select(r => r.Specification).ToList();
198+
IEnumerable<string> oldRefSpecs;
199+
using (var remote = repo.Network.Remotes["origin"])
200+
{
201+
oldRefSpecs = remote.RefSpecs.Select(r => r.Specification).ToList();
202+
}
188203

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

192207
var newRemote = repo.Network.Remotes["origin"];
193208
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

@@ -314,8 +321,7 @@ public void ReportsRemotesWithNonDefaultRefSpecs()
314321
{
315322
Assert.NotNull(repo.Network.Remotes["origin"]);
316323

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

321327
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
@@ -331,6 +331,9 @@ internal static extern unsafe int git_config_delete_entry(
331331
git_config* cfg,
332332
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name);
333333

334+
[DllImport(libgit2)]
335+
internal static extern unsafe int git_config_lock(out IntPtr txn, git_config* config);
336+
334337
[DllImport(libgit2)]
335338
internal static extern unsafe int git_config_delete_multivar(
336339
git_config* cfg,
@@ -1814,6 +1817,12 @@ internal static extern unsafe int git_treebuilder_insert(
18141817

18151818
[DllImport(libgit2)]
18161819
internal static extern unsafe int git_cherrypick(git_repository* repo, git_object* commit, GitCherryPickOptions options);
1820+
1821+
[DllImport(libgit2)]
1822+
internal static extern int git_transaction_commit(IntPtr txn);
1823+
1824+
[DllImport(libgit2)]
1825+
internal static extern void git_transaction_free(IntPtr txn);
18171826
}
18181827
}
18191828
// 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)