Skip to content

Commit da03beb

Browse files
committed
Teach RewriteHistory to rewrite symbolic refs
Closes #501
1 parent 894ed19 commit da03beb

File tree

2 files changed

+34
-15
lines changed

2 files changed

+34
-15
lines changed

LibGit2Sharp.Tests/FilterBranchFixture.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ public void CanProvideNewNamesForTags()
506506
Assert.NotEqual(testTarget, repo.Tags["test_new_b25fa35"].Target);
507507
}
508508

509-
[Fact(Skip = "Rewriting of symbolic references is not supported yet")]
509+
[Fact]
510510
public void CanRewriteSymbolicRefsPointingToTags()
511511
{
512512
const string tagRefName = "refs/tags/test";

LibGit2Sharp/Core/HistoryRewriter.cs

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ internal class HistoryRewriter
1111

1212
private readonly HashSet<Commit> targetedCommits;
1313
private readonly Dictionary<GitObject, GitObject> objectMap = new Dictionary<GitObject, GitObject>();
14+
private readonly Dictionary<Reference, Reference> refMap = new Dictionary<Reference, Reference>();
1415
private readonly Queue<Action> rollbackActions = new Queue<Action>();
1516

1617
private readonly string backupRefsNamespace;
@@ -76,25 +77,42 @@ public void Execute()
7677
}
7778
}
7879

79-
private void RewriteReference(Reference reference)
80+
private Reference RewriteReference(Reference reference)
8081
{
82+
// Has this target already been rewritten?
83+
if (refMap.ContainsKey(reference))
84+
{
85+
return refMap[reference];
86+
}
87+
8188
var sref = reference as SymbolicReference;
8289
if (sref != null)
8390
{
84-
// TODO: Handle a cornercase where a symbolic reference
85-
// points to a Tag which name has been rewritten
86-
return;
91+
return RewriteReference(
92+
sref, old => old.Target, RewriteReference,
93+
(refs, old, target, logMessage) => refs.UpdateTarget(old, target, logMessage));
8794
}
8895

8996
var dref = reference as DirectReference;
9097
if (dref != null)
9198
{
92-
RewriteReference(dref);
99+
return RewriteReference(
100+
dref, old => old.Target, RewriteTarget,
101+
(refs, old, target, logMessage) => refs.UpdateTarget(old, target.Id, logMessage));
93102
}
103+
104+
return reference;
94105
}
95106

96-
private void RewriteReference(DirectReference oldRef)
107+
private Reference RewriteReference<TRef, TTarget>(
108+
TRef oldRef, Func<TRef, TTarget> selectTarget,
109+
Func<TTarget, TTarget> rewriteTarget,
110+
Func<ReferenceCollection, TRef, TTarget, string, Reference> updateTarget)
111+
where TRef : Reference
112+
where TTarget : class
97113
{
114+
var oldRefTarget = selectTarget(oldRef);
115+
98116
string newRefName = oldRef.CanonicalName;
99117
if (oldRef.IsTag() && options.TagNameRewriter != null)
100118
{
@@ -103,12 +121,12 @@ private void RewriteReference(DirectReference oldRef)
103121
false, oldRef.TargetIdentifier);
104122
}
105123

106-
var newTarget = RewriteTarget(oldRef.Target);
124+
var newTarget = rewriteTarget(oldRefTarget);
107125

108-
if (oldRef.Target == newTarget && oldRef.CanonicalName == newRefName)
126+
if (oldRefTarget.Equals(newTarget) && oldRef.CanonicalName == newRefName)
109127
{
110128
// The reference isn't rewritten
111-
return;
129+
return oldRef;
112130
}
113131

114132
string backupName = backupRefsNamespace + oldRef.CanonicalName.Substring("refs/".Length);
@@ -126,19 +144,20 @@ private void RewriteReference(DirectReference oldRef)
126144
{
127145
repo.Refs.Remove(oldRef);
128146
rollbackActions.Enqueue(() => repo.Refs.Add(oldRef.CanonicalName, oldRef, true, "filter-branch: abort"));
129-
return;
147+
return refMap[oldRef] = null;
130148
}
131149

132-
Reference newRef = repo.Refs.UpdateTarget(oldRef, newTarget.Id, "filter-branch: rewrite");
133-
rollbackActions.Enqueue(() => repo.Refs.UpdateTarget(oldRef, oldRef.Target.Id, "filter-branch: abort"));
150+
Reference newRef = updateTarget(repo.Refs, oldRef, newTarget, "filter-branch: rewrite");
151+
rollbackActions.Enqueue(() => updateTarget(repo.Refs, oldRef, oldRefTarget, "filter-branch: abort"));
134152

135153
if (newRef.CanonicalName == newRefName)
136154
{
137-
return;
155+
return refMap[oldRef] = newRef;
138156
}
139157

140-
repo.Refs.Move(newRef, newRefName);
158+
var movedRef = repo.Refs.Move(newRef, newRefName);
141159
rollbackActions.Enqueue(() => repo.Refs.Move(newRef, oldRef.CanonicalName));
160+
return refMap[oldRef] = movedRef;
142161
}
143162

144163
private void RewriteCommit(Commit commit)

0 commit comments

Comments
 (0)