Skip to content

Commit 97e4d08

Browse files
committed
Add the UpstreamBranchCanonicalName property to Branch
This property contains the reference of the tracked branch, relatively to the upstream repository (so "refs/heads/master", and not "refs/remotes/origin/master").
1 parent 2ad2c39 commit 97e4d08

File tree

4 files changed

+143
-39
lines changed

4 files changed

+143
-39
lines changed

LibGit2Sharp.Tests/BranchFixture.cs

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -259,12 +259,13 @@ public void CanResolveRemote()
259259
}
260260

261261
[Fact]
262-
public void RemoteForNonTrackingBranchIsNull()
262+
public void RemoteAndUpstreamBranchCanonicalNameForNonTrackingBranchIsNull()
263263
{
264264
using (var repo = new Repository(StandardTestRepoPath))
265265
{
266266
Branch test = repo.Branches["i-do-numbers"];
267267
Assert.Null(test.Remote);
268+
Assert.Null(test.UpstreamBranchCanonicalName);
268269
}
269270
}
270271

@@ -279,6 +280,16 @@ public void QueryRemoteForLocalTrackingBranch()
279280
}
280281
}
281282

283+
[Fact]
284+
public void QueryUpstreamBranchCanonicalNameForLocalTrackingBranch()
285+
{
286+
using (var repo = new Repository(StandardTestRepoPath))
287+
{
288+
Branch trackLocal = repo.Branches["track-local"];
289+
Assert.Equal("refs/heads/master", trackLocal.UpstreamBranchCanonicalName);
290+
}
291+
}
292+
282293
[Fact]
283294
public void CanLookupABranchByItsCanonicalName()
284295
{
@@ -454,20 +465,20 @@ public void CanWalkCommitsFromAnotherBranch()
454465
}
455466

456467
[Fact]
457-
public void CanSetUpstreamBranch()
468+
public void CanSetTrackedBranch()
458469
{
459470
const string testBranchName = "branchToSetUpstreamInfoFor";
460-
const string upstreamBranchName = "refs/remotes/origin/master";
471+
const string trackedBranchName = "refs/remotes/origin/master";
461472

462473
string path = CloneStandardTestRepo();
463474
using (var repo = new Repository(path))
464475
{
465476
Branch branch = repo.CreateBranch(testBranchName);
466477
Assert.False(branch.IsTracking);
467478

468-
Branch upstreamBranch = repo.Branches[upstreamBranchName];
479+
Branch trackedBranch = repo.Branches[trackedBranchName];
469480
repo.Branches.Update(branch,
470-
b => b.Upstream = upstreamBranch.CanonicalName);
481+
b => b.TrackedBranch = trackedBranch.CanonicalName);
471482

472483
// Verify the immutability of the branch.
473484
Assert.False(branch.IsTracking);
@@ -479,58 +490,59 @@ public void CanSetUpstreamBranch()
479490
Assert.NotNull(upstreamRemote);
480491

481492
Assert.True(branch.IsTracking);
482-
Assert.Equal(upstreamBranch, branch.TrackedBranch);
493+
Assert.Equal(trackedBranch, branch.TrackedBranch);
483494
Assert.Equal(upstreamRemote, branch.Remote);
484495
}
485496
}
486497

487498
[Fact]
488-
public void CanSetUpstreamMergeBranch()
499+
public void CanSetUpstreamBranch()
489500
{
490501
const string testBranchName = "branchToSetUpstreamInfoFor";
491-
const string mergeBranchName = "refs/heads/master";
492-
const string upstreamBranchName = "refs/remotes/origin/master";
493-
const string upstreamRemoteName = "origin";
502+
const string upstreamBranchName = "refs/heads/master";
503+
const string trackedBranchName = "refs/remotes/origin/master";
504+
const string remoteName = "origin";
494505

495506
string path = CloneStandardTestRepo();
496507
using (var repo = new Repository(path))
497508
{
498509
Branch branch = repo.CreateBranch(testBranchName);
499510
Assert.False(branch.IsTracking);
500511

501-
Branch upstreamBranch = repo.Branches[upstreamBranchName];
512+
Branch trackedBranch = repo.Branches[trackedBranchName];
502513
Branch updatedBranch = repo.Branches.Update(branch,
503-
b => b.UpstreamRemote = upstreamRemoteName,
504-
b => b.UpstreamMergeBranch = mergeBranchName);
514+
b => b.Remote = remoteName,
515+
b => b.UpstreamBranch = upstreamBranchName);
505516

506517
// Verify the immutability of the branch.
507518
Assert.False(branch.IsTracking);
508519

509-
Remote upstreamRemote = repo.Network.Remotes[upstreamRemoteName];
520+
Remote upstreamRemote = repo.Network.Remotes[remoteName];
510521
Assert.NotNull(upstreamRemote);
511522

512523
Assert.True(updatedBranch.IsTracking);
513-
Assert.Equal(upstreamBranch, updatedBranch.TrackedBranch);
524+
Assert.Equal(trackedBranch, updatedBranch.TrackedBranch);
525+
Assert.Equal(upstreamBranchName, updatedBranch.UpstreamBranchCanonicalName);
514526
Assert.Equal(upstreamRemote, updatedBranch.Remote);
515527
}
516528
}
517529

518530
[Fact]
519-
public void CanSetLocalUpstreamBranch()
531+
public void CanSetLocalTrackedBranch()
520532
{
521533
const string testBranchName = "branchToSetUpstreamInfoFor";
522-
const string upstreamBranchName = "refs/heads/master";
534+
const string localTrackedBranchName = "refs/heads/master";
523535

524536
string path = CloneStandardTestRepo();
525537
using (var repo = new Repository(path))
526538
{
527539
Branch branch = repo.CreateBranch(testBranchName);
528540
Assert.False(branch.IsTracking);
529541

530-
Branch upstreamBranch = repo.Branches[upstreamBranchName];
542+
Branch trackedBranch = repo.Branches[localTrackedBranchName];
531543

532544
repo.Branches.Update(branch,
533-
b => b.Upstream = upstreamBranch.CanonicalName);
545+
b => b.TrackedBranch = trackedBranch.CanonicalName);
534546

535547
// Get the updated branch information.
536548
branch = repo.Branches[testBranchName];
@@ -548,15 +560,16 @@ public void CanSetLocalUpstreamBranch()
548560

549561
// Verify the IsTracking and TrackedBranch properties.
550562
Assert.True(branch.IsTracking);
551-
Assert.Equal(upstreamBranch, branch.TrackedBranch);
563+
Assert.Equal(trackedBranch, branch.TrackedBranch);
564+
Assert.Equal("refs/heads/master", branch.UpstreamBranchCanonicalName);
552565
}
553566
}
554567

555568
[Fact]
556-
public void CanUnsetUpstreamBranch()
569+
public void CanUnsetTrackedBranch()
557570
{
558571
const string testBranchName = "branchToSetUpstreamInfoFor";
559-
const string upstreamBranchName = "refs/remotes/origin/master";
572+
const string trackedBranchName = "refs/remotes/origin/master";
560573

561574
string path = CloneStandardTestRepo();
562575
using (var repo = new Repository(path))
@@ -565,16 +578,18 @@ public void CanUnsetUpstreamBranch()
565578
Assert.False(branch.IsTracking);
566579

567580
branch = repo.Branches.Update(branch,
568-
b => b.Upstream = upstreamBranchName);
581+
b => b.TrackedBranch = trackedBranchName);
569582

570583
// Got the updated branch from the Update() method
571584
Assert.True(branch.IsTracking);
572585

573586
branch = repo.Branches.Update(branch,
574-
b => b.Upstream = null);
587+
b => b.TrackedBranch = null);
575588

576589
// Verify this is no longer a tracking branch
577590
Assert.False(branch.IsTracking);
591+
Assert.Null(branch.Remote);
592+
Assert.Null(branch.UpstreamBranchCanonicalName);
578593
}
579594
}
580595

LibGit2Sharp/Branch.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,26 @@ public virtual ICommitLog Commits
129129
get { return repo.Commits.QueryBy(new Filter { Since = this }); }
130130
}
131131

132+
/// <summary>
133+
/// Gets the configured canonical name of the upstream branch.
134+
/// <para>
135+
/// This is the upstream reference to which this branch will be pushed.
136+
/// It corresponds to the "branch.branch_name.merge" property of the config file.
137+
/// </para>
138+
/// </summary>
139+
public virtual string UpstreamBranchCanonicalName
140+
{
141+
get
142+
{
143+
if (IsRemote)
144+
{
145+
return Remote.FetchSpecTransformToSource(CanonicalName);
146+
}
147+
148+
return UpstreamBranchCanonicalNameFromLocalBranch();
149+
}
150+
}
151+
132152
/// <summary>
133153
/// Gets the configured <see cref="Remote"/> to fetch from and push to.
134154
/// </summary>
@@ -156,6 +176,18 @@ public virtual Remote Remote
156176
}
157177
}
158178

179+
private string UpstreamBranchCanonicalNameFromLocalBranch()
180+
{
181+
ConfigurationEntry<string> mergeRefEntry = repo.Config.Get<string>("branch", Name, "merge");
182+
183+
if (mergeRefEntry == null)
184+
{
185+
return null;
186+
}
187+
188+
return mergeRefEntry.Value;
189+
}
190+
159191
private string RemoteNameFromLocalBranch()
160192
{
161193
ConfigurationEntry<string> remoteEntry = repo.Config.Get<string>("branch", Name, "remote");

LibGit2Sharp/BranchUpdater.cs

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,26 @@ internal BranchUpdater(Repository repo, Branch branch)
4141
/// master branch.
4242
/// </para>
4343
/// </summary>
44+
[Obsolete("This property will be removed in the next release. Please use BranchUpdate.TrackedBranch instead.")]
4445
public virtual string Upstream
46+
{
47+
set { TrackedBranch = value; }
48+
}
49+
50+
/// <summary>
51+
/// Sets the upstream information for the branch.
52+
/// <para>
53+
/// Passing null or string.Empty will unset the upstream.
54+
/// </para>
55+
/// <para>
56+
/// The upstream branch name is with respect to the current repository.
57+
/// So, passing "refs/remotes/origin/master" will set the current branch
58+
/// to track "refs/heads/master" on the origin. Passing in
59+
/// "refs/heads/master" will result in the branch tracking the local
60+
/// master branch.
61+
/// </para>
62+
/// </summary>
63+
public virtual string TrackedBranch
4564
{
4665
set
4766
{
@@ -58,28 +77,56 @@ public virtual string Upstream
5877
/// <summary>
5978
/// Set the upstream merge branch directly for this branch.
6079
/// <para>
61-
/// To track the "master" branch on the "origin" remote, set the
62-
/// UpstreamRemote property to "origin" and the UpstreamMergeBranch
63-
/// property to "refs/heads/master".
64-
/// </para>
80+
/// To track the "master" branch on the "origin" remote, set the
81+
/// <see cref="Remote"/> property to "origin" and the <see cref="UpstreamBranch"/>
82+
/// property to "refs/heads/master".
83+
/// </para>
6584
/// </summary>
85+
[Obsolete("This property will be removed in the next release. Please use BranchUpdate.UpstreamBranch instead.")]
6686
public virtual string UpstreamMergeBranch
87+
{
88+
set { UpstreamBranch = value; }
89+
}
90+
91+
/// <summary>
92+
/// Set the upstream branch for this branch.
93+
/// <para>
94+
/// To track the "master" branch on the "origin" remote, set the
95+
/// <see cref="Remote"/> property to "origin" and the <see cref="UpstreamBranch"/>
96+
/// property to "refs/heads/master".
97+
/// </para>
98+
/// </summary>
99+
public virtual string UpstreamBranch
67100
{
68101
set
69102
{
70-
SetUpstreamMergeBranch(value);
103+
SetUpstreamBranch(value);
71104
}
72105
}
73106

74107
/// <summary>
75-
/// Set the upstream remote for this branch.
108+
/// Set the upstream merge branch directly for this branch.
76109
/// <para>
77110
/// To track the "master" branch on the "origin" remote, set the
78-
/// UpstreamRemote property to "origin" and the UpstreamMergeBranch
111+
/// RemoteName property to "origin" and the UpstreamMergeBranch
79112
/// property to "refs/heads/master".
80113
/// </para>
81114
/// </summary>
115+
[Obsolete("This property will be removed in the next release. Please use BranchUpdate.UpstreamMergeBranchCanonicalName instead.")]
82116
public virtual string UpstreamRemote
117+
{
118+
set { Remote = value; }
119+
}
120+
121+
/// <summary>
122+
/// Set the upstream remote for this branch.
123+
/// <para>
124+
/// To track the "master" branch on the "origin" remote, set the
125+
/// <see cref="Remote"/> property to "origin" and the <see cref="UpstreamBranch"/>
126+
/// property to "refs/heads/master".
127+
/// </para>
128+
/// </summary>
129+
public virtual string Remote
83130
{
84131
set
85132
{
@@ -90,7 +137,7 @@ public virtual string UpstreamRemote
90137
private void UnsetUpstream()
91138
{
92139
SetUpstreamRemote(string.Empty);
93-
SetUpstreamMergeBranch(string.Empty);
140+
SetUpstreamBranch(string.Empty);
94141
}
95142

96143
/// <summary>
@@ -117,14 +164,14 @@ private void SetUpstream(string upstreamBranchName)
117164
GetUpstreamInformation(upstreamBranchName, out remoteName, out branchName);
118165

119166
SetUpstreamRemote(remoteName);
120-
SetUpstreamMergeBranch(branchName);
167+
SetUpstreamBranch(branchName);
121168
}
122169

123170
/// <summary>
124171
/// Set the upstream merge branch for the local branch.
125172
/// </summary>
126173
/// <param name="mergeBranchName">The merge branch in the upstream remote's namespace.</param>
127-
private void SetUpstreamMergeBranch(string mergeBranchName)
174+
private void SetUpstreamBranch(string mergeBranchName)
128175
{
129176
string configKey = string.Format("branch.{0}.merge", branch.Name);
130177

@@ -188,11 +235,7 @@ private void GetUpstreamInformation(string canonicalName, out string remoteName,
188235
remoteName = Proxy.git_branch_remote_name(repo.Handle, canonicalName);
189236

190237
Remote remote = repo.Network.Remotes.RemoteForName(remoteName);
191-
using (RemoteSafeHandle remoteHandle = Proxy.git_remote_load(repo.Handle, remote.Name, true))
192-
{
193-
GitRefSpecHandle refSpecPtr = Proxy.git_remote_fetchspec(remoteHandle);
194-
mergeBranchName = Proxy.git_refspec_rtransform(refSpecPtr, canonicalName);
195-
}
238+
mergeBranchName = remote.FetchSpecTransformToSource(canonicalName);
196239
}
197240
else
198241
{

LibGit2Sharp/Remote.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,20 @@ public virtual void Fetch(
7373
repository.Network.Fetch(this, tagFetchMode, onProgress, onCompletion, onUpdateTips, onTransferProgress, credentials);
7474
}
7575

76+
/// <summary>
77+
/// Transform a reference to its source reference using the <see cref = "Remote" />'s default fetchspec.
78+
/// </summary>
79+
/// <param name="reference">The reference to transform.</param>
80+
/// <returns>The transformed reference.</returns>
81+
internal string FetchSpecTransformToSource(string reference)
82+
{
83+
using (RemoteSafeHandle remoteHandle = Proxy.git_remote_load(repository.Handle, Name, true))
84+
{
85+
GitRefSpecHandle fetchSpecPtr = Proxy.git_remote_fetchspec(remoteHandle);
86+
return Proxy.git_refspec_rtransform(fetchSpecPtr, reference);
87+
}
88+
}
89+
7690
/// <summary>
7791
/// Determines whether the specified <see cref = "Object" /> is equal to the current <see cref = "Remote" />.
7892
/// </summary>

0 commit comments

Comments
 (0)