private Reference RewriteReference <TRef, TTarget>(
            TRef oldRef, Func <TRef, TTarget> selectTarget,
            Func <TTarget, TTarget> rewriteTarget,
            ReferenceUpdater <TRef, TTarget> updateTarget)
            where TRef : Reference
            where TTarget : class
        {
            var oldRefTarget = selectTarget(oldRef);
            var signature    = repo.Config.BuildSignature(DateTimeOffset.Now);

            string newRefName = oldRef.CanonicalName;

            if (oldRef.IsTag() && options.TagNameRewriter != null)
            {
                newRefName = Reference.TagPrefix +
                             options.TagNameRewriter(oldRef.CanonicalName.Substring(Reference.TagPrefix.Length),
                                                     false, oldRef.TargetIdentifier);
            }

            var newTarget = rewriteTarget(oldRefTarget);

            if (oldRefTarget.Equals(newTarget) && oldRef.CanonicalName == newRefName)
            {
                // The reference isn't rewritten
                return(oldRef);
            }

            string backupName = backupRefsNamespace + oldRef.CanonicalName.Substring("refs/".Length);

            if (repo.Refs.Resolve <Reference>(backupName) != null)
            {
                throw new InvalidOperationException(
                          String.Format("Can't back up reference '{0}' - '{1}' already exists", oldRef.CanonicalName, backupName));
            }

            repo.Refs.Add(backupName, oldRef.TargetIdentifier, signature, "filter-branch: backup");
            rollbackActions.Enqueue(() => repo.Refs.Remove(backupName));

            if (newTarget == null)
            {
                repo.Refs.Remove(oldRef);
                rollbackActions.Enqueue(() => repo.Refs.Add(oldRef.CanonicalName, oldRef, signature, "filter-branch: abort", true));
                return(refMap[oldRef] = null);
            }

            Reference newRef = updateTarget(repo.Refs, oldRef, newTarget, signature, "filter-branch: rewrite");

            rollbackActions.Enqueue(() => updateTarget(repo.Refs, oldRef, oldRefTarget, signature, "filter-branch: abort"));

            if (newRef.CanonicalName == newRefName)
            {
                return(refMap[oldRef] = newRef);
            }

            var movedRef = repo.Refs.Move(newRef, newRefName);

            rollbackActions.Enqueue(() => repo.Refs.Move(newRef, oldRef.CanonicalName));
            return(refMap[oldRef] = movedRef);
        }